Utilizzare una classe plugin all'interno di un template
Sto scrivendo un plugin per inviare un invito a un amico che apre un form quando viene cliccato un link. Ho incapsulato tutte le funzioni in una classe seguendo il codice del plugin Report Broken Video di @toscho. Il codice rilevante è il seguente:
/*
Plugin Name: Send Invitation
Plugin URI: http://w3boutique.net
Description: Invia via email il link della pagina corrente a un amico
Author: Nandakumar Chandrasekhar
Version: 0.1
Author URI: http://w3boutique.net/about-nanda.html
License: GPL2
*/
// include() o require() di eventuali file necessari qui
// Impostazioni e dettagli di configurazione vanno qui
define('SEND_INVITATION_MIN_WORDPRESS_VERSION', '3.1.1');
define('SEND_INVITATION_PLUGIN_URL', plugins_url('', __FILE__));
add_action( 'init', array( 'SendInvitation', 'nc_sendinvitation_init' ) );
class SendInvitation {
protected $nonce_name = 'nc_sendinvitation';
protected $post_url = '';
public static function nc_sendinvitation_init() {
new self;
}
public function __construct() {
add_action( 'init', array(&$this, 'nc_sendinvitation_head' ));
add_action( 'init', array(&$this, 'nc_sendinvitation_check_wordpress_version' ));
add_action( 'init', array(&$this, 'nc_sendinvitation_form_action' ));
//$this->post_url = $this->nc_sendinvitation_get_post_url();
}
public function nc_sendinvitation_head() {
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'send_invitation_js',
plugins_url( 'js/send-invitation.js', __FILE__ ),
array( 'jquery' ) );
wp_enqueue_style( 'send_invitation_css',
plugins_url( 'css/send-invitation.css', __FILE__ ) );
}
public function nc_sendinvitation_check_wordpress_version() {
global $wp_version;
$exit_msg = 'Send Invitation richiede la versione '
. SEND_INVITATION_MIN_WORDPRESS_VERSION
. 'o successiva <a href="http://codex.wordpress.org/Upgrading_WordPress">Si prega di
aggiornare!</a>';
if ( version_compare( $wp_version, SEND_INVITATION_MIN_WORDPRESS_VERSION, '<') )
{
exit( $exit_msg );
}
}
public function nc_sendinvitation_form_action() {
$action = '';
if ( $_SERVER['REQUEST_METHOD'] != 'POST' )
{
$action = $this->nc_sendinvitation_get_form();
}
else if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
$action = $this->nc_sendinvitation_handle_submit();
}
return $action;
}
public function nc_sendinvitation_get_form() {
// Avvia l'output buffer, previene l'output diretto del contenuto nello
// script, invece viene memorizzato in un buffer che può poi essere svuotato
// per permettere al file include di essere memorizzato in una variabile
// Vedi http://www.codingforums.com/showthread.php?t=124537
ob_start();
include('send-invitation-form.php');
$send_invitation_link = ob_get_clean();
return $send_invitation_link;
}
public function nc_sendinvitation_handle_submit() {
if ( isset( $_POST['form_type'] ) && ( $_POST['form_type'] == 'nc_sendinvitation' ) ) {
$to = 'navanitachora@gamil.com';
$subject = 'Invito a SwanLotus';
$message = 'Navanitachora ti invita a visualizzare questo link';
wp_mail($to, $subject, $message);
$result = 'Email inviata con successo';
}
else {
$result = $this->nc_sendinvitation_get_form();
}
return $result;
}
public function nc_sendinvitation_get_post_url() {
global $post;
$blog_id = get_option('page_for_posts');
$post_id = '';
if (is_home($blog_id)) {
$post_id = $blog_id;
} else {
$post_id = $post->ID;
}
return get_permalink($post_id);
}
}
/* Fine del File */
?>
Non so come utilizzare questa classe nel mio template per visualizzare il form. So che devo istanziare la classe ma non sono sicuro dove inserire il codice e come accedere all'oggetto per utilizzarlo nel mio template. Ho conoscenze di OOP ma non l'ho mai usato in questo contesto e avrei bisogno di una guida passo passo.
Grazie mille.

Il modo migliore per utilizzare la tua classe senza conoscere l'oggetto è un'azione. Registri l'azione prima che i file del tema per la presentazione vengano caricati, WordPress si occuperà del resto.
Codice di esempio:
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: Plugin Action Demo
*/
add_action( 'init', array ( 'Plugin_Action_Demo', 'init' ) );
class Plugin_Action_Demo
{
/**
* Crea una nuova istanza.
*
* @wp-hook init
* @see __construct()
* @return void
*/
public static function init()
{
new self;
}
/**
* Registra l'azione. Può fare anche altre cose magiche.
*/
public function __construct()
{
add_action( 'plugin_action_demo', array ( $this, 'print_foo' ), 10, 1 );
}
/**
* Stampa 'foo' multiple $times.
*
* Utilizzo:
* <code>do_action( 'plugin_action_demo', 50 );</code>
*
* @wp-hook plugin_action_demo
* @param int $times
* @return void
*/
public function print_foo( $times = 1 )
{
print str_repeat( ' foo ', (int) $times );
}
}
Ora puoi chiamare do_action( 'plugin_action_demo', 50 );
da qualche parte nel tuo tema o in un altro plugin, e non dovrai più preoccuparti del funzionamento interno della classe.
Se disattivi il plugin sei comunque al sicuro: WordPress semplicemente ignora le azioni sconosciute e il do_action()
non farà alcun danno. Inoltre, altri plugin possono rimuovere o sostituire l'azione, quindi hai creato una bella mini API con un semplice add_action()
.
Potresti anche creare un singleton:
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: Plugin Singleton Demo
*/
class Plugin_Singleton_Demo
{
protected static $instance = NULL;
/**
* Crea una nuova istanza se non ce n'è già una.
*
* @wp-hook init
* @return object
*/
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
/**
* Non accessibile dall'esterno.
*/
protected function __construct() {}
/**
* Stampa 'foo' multiple $times.
*
* @param int $times
* @return void
*/
public function print_foo( $times = 1 )
{
echo str_repeat( ' foo ', (int) $times );
}
}
Ora print_foo()
è accessibile tramite:
Plugin_Singleton_Demo::get_instance()->print_foo();
Non raccomando il pattern Singleton. Ha alcuni gravi svantaggi.

Per curiosità, qual è lo scopo di plugin_action_demo
in add_action()
? Come fa do_action( 'print_foo', 50 );
a sapere che l'azione è plugin_action_demo
o quel nome è irrilevante?

Grazie, funziona, devo solo aggiungere un po' di validazione e dovrei aver finito. Grazie @toscho mi hai insegnato molto. :-)

@tosco Ho trovato la tua risposta cercando articoli sui singleton per i plugin di WordPress. Ti rendi conto che stai usando un Singleton nella tua risposta consigliata, semplicemente non lo stai imponendo? Immagina se un themer chiamasse new Plugin_Action_Demo
?? Chiunque usasse do_action('plugin_action_demo')
attiverebbe due (2) chiamate a Plugin_Action_Demo->print_foo()
, non quello che vuoi. La furia contro i singleton ignora che ci siano casi d'uso appropriati. Come informazione, sia @ericmann che io stiamo scrivendo articoli per sostenere l'uso dei singleton per il namespacing dei plugin WordPress, lui su eamann.com e io su hardcorewp.com.

@MikeSchinkel L'approccio che attualmente consiglio è questo: https://gist.github.com/3804204 – puoi accedervi come un Singleton, ma non sei obbligato.

@tocho Va bene, vedo che non devi accedere al tuo esempio come un singleton, ma qual è il vantaggio? In quali casi d'uso vorresti farlo?

@MikeSchinkel Test unitari e classi figlie. E no, non cambio la visibilità in una classe figlia a meno che non sia obbligato. :)

@toscho Testi effettivamente le classi usate come base per i tuoi plugin? Se sì, come gestisci gli hook che non possono essere testati senza un caricamento di pagina? Non rende inutile il test unitario di quelle classi? E io sto facendo il subclassing dei miei singleton senza problemi, anzi è fondamentale per la mia architettura; perché dovrebbe essere un problema? A proposito, sto per pubblicare un post che promuove i singleton e scriverò un seguito riguardante il testing unitario. Ma se hai ragioni valide contrarie, vorrei saperlo prima di pubblicare. Grazie in anticipo.

@MikeSchinkel Io uso http://core.trac.wordpress.org/browser/tests – il tempo di caricamento della pagina non è un problema. E non mi piace l'idea di Eric di testare una sottoclasse che non è il codice di produzione effettivo e differisce nel comportamento. Dovrei scrivere un post su questo, i commenti non sono fatti per questo. :)

@toscho - Non vedo l'ora di leggere il tuo post sul blog. A proposito, non ho capito cosa intendevi con "Non cambio la visibilità in una classe figlia a meno che non sia costretto."

@toscho Inoltre, quando hai detto "il caricamento della pagina non è un problema" penso tu abbia frainteso; intendevo dire che il caricamento della pagina è praticamente l'unico modo per testare una classe utilizzata per collegare i gestori di hook con i metodi, cioè che il classico unit testing non ha senso.
