In questo articolo vedremo come implementare un framework di base per operare sui temi di WordPress.
Struttura del framework
Il nostro framework avrà delle classi base che verranno estese dalle classi che le useranno. Le classi base hanno il compito di automatizzare il più possibile dei task che in WordPress richiedono sempre le stesse routine.
AJAX
In WordPress AJAX viene implementato usando questa procedura:
- Viene registrata una action tramite i prefissi
wp_ajax
(per utenti loggati) ewp_ajax_nopriv
(per tutti i visitatori). - Viene associata alla action una funzione o un metodo di una classe; nel secondo caso la parola chiave
$this
fa riferimento alla classe che definisce il metodo
WordPress imposta il Content Type della risposta su text/html
, quindi occorre cambiare questa impostazione prima di restituire l'output al client.
class My_Ajax_Wrapper {
protected $_actions = array();
protected $_types;
public function __construct( $contentTypes ) {
$this->_types = $contentTypes; // es. application/json, text/xml ecc.
}
public function addAction( $name, $visibility, $method ) {
$this->_actions[] = array(
'name' => $name, // Nome action
'visibility' => $visibility, // all, public, private
'method' => $method // Metodo della classe discendente
);
}
// $reference è l'istanza della classe discendente
public function registerActions( $reference ) {
$actions = $this->_actions;
if( count( $actions ) == 0 ) {
return;
}
foreach( $actions as $action ) {
$visibility = $action['visibility'];
if( $visibility == 'all' ) {
add_action( 'wp_ajax_' . $action['name'], array( $reference, $action['method'] ) );
add_action( 'wp_ajax_nopriv_' . $action['name'], array( $reference, $action['method'] ) );
} else if( $visibility == 'public' ) {
add_action( 'wp_ajax_nopriv_' . $action['name'], array( $reference, $action['method'] ) );
} else {
add_action( 'wp_ajax_' . $action['name'], array( $reference, $action['method'] ) );
}
}
}
public function setContentType( $type = 'application/json' ) {
$allowed = $this->_types;
if( !in_array( $type, $allowed ) ) {
return;
}
header( 'Content-Type: ' . $type );
}
}
Esempio d'uso:
require_once( 'My_Ajax_Wrapper.php' );
class My_Ajax {
protected $_ajax;
public function __construct() {
$this->_ajax = new My_Ajax_Wrapper( array( 'application/json', 'text/xml' ) );
$this->actions();
$this->_ajax->registerActions( $this );
}
public function actions() {
$this->_ajax->addAction( 'test', 'all', 'foo' );
$this->_ajax->addAction( 'bar', 'all', 'bar' );
}
public function foo() {
$this->_ajax->setContentType( 'text/xml' );
echo '<foo>ok</foo>';
exit();
}
public function bar() {
$this->_ajax->setContentType( 'application/json' );
echo json_encode( array( 'ok' => 'bar' ) );
exit();
}
}
$myA = new My_Ajax();
L'action test
restituisce il suo output in XML, mentre la seconda action lo restituisce in JSON. $this
nel metodo registerActions()
punta alla classe corrente, ossia My_Ajax
.
Custom fields
I custom fields in WordPress non sono altro che campi aggiunti ai post e alle pagine. Ciascun campo è un elemento di un normale form HTML che viene visualizzato da WordPress all'interno di un box HTML detto metabox (il nome deriva dal fatto che questi campi sono campi meta).
Quando si crea un nuovo campo bisogna associarlo a un metabox con dei parametri:
- ID (stringa identificativa)
- Titolo
- Funzione o metodo di una classe che genera l'HTML del campo
- Screen, ossia se il campo va associato a un post, a una pagina o a un custom post type
- Contesto, ossia la posizione nell'editor di WordPress
- Priorità
Poi bisogna salvare il valore del campo aggiungendo la action save_post
: infatti i custom field non vengono salvati in automatico.
Definiamo la seguente classe base:
class My_CF {
public $parameters;
public $fields;
public function __construct( $params = array(), $fiels = array() ) {
$this->parameters = $params; // parametri metabox
$this->fields = $fiels; // campi
add_action( 'add_meta_boxes', array( &$this, 'register' ) ); // crea la metabox
add_action( 'save_post', array( &$this, 'save' ) ); // salva i campi
// aggiungiamo uno script per i campi immagine
wp_register_script( 'cf', plugins_url() . '/my-plugin/framework/cf/cf.js', array( 'jquery' ), '1.0', true );
wp_enqueue_script( 'cf' );
}
// crea la metabox
public function register() {
$config = $this->parameters;
add_meta_box( $config['id'], $config['title'], array( &$this, 'output' ), $config['screen'], $config['context'], $config['priority'] );
}
// crea il codice HTML dei campi
public function output( $post ) {
$form_fields = $this->fields;
$post_id = $post->ID;
$html = '';
$output = array();
foreach( $form_fields as $form_field ) {
$output[] = $this->_display( $form_field, $post_id );
}
$html = implode( "\n", $output );
echo $html;
}
// Salva il campo
public function save( $post_id ) {
$form_fields = $this->fields;
$config = $this->parameters;
if ( $config['screen'] != $_POST['post_type'] ) {
return $post_id;
}
if ( !current_user_can( 'edit_post', $post_id ) ) {
return $post_id;
}
foreach( $form_fields as $form_field ) {
$name = $form_field['name'];
update_post_meta( $post_id, $name, sanitize_text_field( $_POST[$name] ) );
}
}
// Crea una stringa HTML a seconda del tipo di campo
private function _display( $field, $id ) {
$field_html = '';
if( !is_array( $field ) ) {
return '';
}
switch( $field['type'] ) {
case 'text':
$field_html = '<p><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label><input type="' . $field['type'] . '" style="display: block; width: 100%;" name="' . $field['name'] . '" placeholder="' . $field['label'] . '" value="' . get_post_meta( $id, $field['name'], true ) . '" /></p>';
break;
case 'checkbox':
case 'radio':
$checked = ( get_post_meta( $id, $field['name'], true ) == $field['value'] ) ? ' checked="checked"' : '';
$field_html = '<p><input type="' . $field['type'] . '" name="' . $field['name'] . '" value="' . $field['value'] . '"' . $checked . ' /><label style="font-weight: bold; margin-left: 0.5em" for="' . $field['name'] . '">' . $field['label'] . '</label></p>';
break;
case 'textarea':
$field_html = '<p><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label><textarea cols="20" rows="10" style="display: block; width: 100%;" name="' . $field['name'] . '" placeholder="' . $field['label'] . '">';
$field_html .= esc_html( get_post_meta( $id, $field['name'], true ) );
$field_html .= '</textarea></p>';
break;
case 'select':
$field_html = '<p><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label>';
$field_html .= '<select name="' . $field['name'] . '" style="display: block; width: 100%;">';
$field_html .= '<option value=""></option>';
foreach( $field['options'] as $opt ) {
$selected = ( get_post_meta( $id, $field['name'], true ) == $opt['value'] ) ? ' selected="selected"' : '';
$field_html .= '<option value="' . $opt['value'] . '"' . $selected . '>' . $opt['label'] . '</option>';
}
$field_html .= '</select></p>';
break;
case 'image':
$field_html = '<div><label style="display: block; font-weight: bold; margin-bottom: 0.6em" for="' . $field['name'] . '">' . $field['label'] . '</label>';
$image = ( is_numeric( get_post_meta( $id, $field['name'], true ) ) ) ? wp_get_attachment_image_src( get_post_meta( $id, $field['name'], true ), 'thumbnail' ) : '';
$field_html .= '<div id="cf-image-' . $field['name'] . '" style="background-color: #ccc; background-size: cover; background-repeat: no-repeat; width: 150px; height: 150px;">';
if( $image !== '' ) {
$field_html .= '<img src="' . $image[0] . '" alt="" width="150" height="150" />';
} else {
$field_html .= '<img />';
}
$field_html .= '</div>';
$field_html .= '<input type="hidden" name="' . $field['name'] . '" value="' . get_post_meta( $id, $field['name'], true ) . '" />';
$field_html .= '<p><button type="button" class="cf-image button-primary">' . __( 'Seleziona', 'my-plugin' ) . '</button></p>';
$field_html .= '</div>';
break;
default:
break;
}
return $field_html;
}
}
Il codice jQuery viene usato per selezionare le immagini dalla Media Library:
(function( $ ) {
var CF = function( element ) {
this.$el = $( element );
if( this.$el.length ) {
this.init();
}
};
CF.prototype = {
init: function() {
this.selectImage();
},
selectImage: function() {
this.$el.each(function() {
var $btn = $( this ),
$input = $btn.parent().prev(),
$img = $input.prev().find( "img" );
$btn.click(function() {
if( wp.media.frames.gk_frame ) {
wp.media.frames.gk_frame.open();
} else {
wp.media.frames.gk_frame = wp.media({
title: "Seleziona immagine",
multiple: false,
library: {
type: "image"
},
button: {
text: "Usa immagine"
}
});
var selectMedia = function() {
var selection = wp.media.frames.gk_frame.state().get( "selection" );
if( !selection ) {
return;
}
selection.each(function( attachment ) {
var attrs = attachment.attributes;
var imageID = attrs.id; // ID immagine
var url = attrs.url; // URL immagine
$input.val( imageID );
$img.parent().css( "background-image", "url(" + url + ")" );
});
};
wp.media.frames.gk_frame.on( "close", selectMedia );
wp.media.frames.gk_frame.open();
}
});
});
}
};
$(function() {
var _cf = new CF( ".cf-image" );
});
})( jQuery );
Esempio d'uso:
require_once( 'My_CF.php' );
class My_Fields {
public $parameters = array();
public $fields = array();
protected $_cf;
public function __construct( $params, $flds ) {
$this->parameters = $params;
$this->fields = $flds;
if ( is_admin() ) {
// quando viene caricato l'editor
add_action( 'load-post.php', array( $this, 'loadFields' ) );
add_action( 'load-post-new.php', array( $this, 'loadFields' ) );
}
}
public function loadFields() {
$this->_cf = new My_CF( $this->parameters, $this->fields );
}
}
Quindi instanziamo la classe:
$my_params = array(
'id' => 'test',
'title' => 'Test',
'screen' => 'post',
'context' => 'side',
'priority' => 'default'
);
$my_fields = array(
array(
'name' => 'foo',
'type' => 'text',
'label' => 'Foo'
),
array(
'name' => 'bar',
'type' => 'checkbox',
'label' => 'Bar',
'value' => '1'
)
);
$cf = new My_Fields( $my_params, $my_fields );