Cómo usar una clase de plugin dentro de una plantilla en WordPress
Estoy desarrollando un plugin para enviar invitaciones a amigos que muestra un formulario al hacer clic en un enlace. He encapsulado todas las funciones en una clase siguiendo el código del plugin Report Broken Video de @toscho. El código relevante es el siguiente:
/*
Plugin Name: Enviar Invitación
Plugin URI: http://w3boutique.net
Description: Envía por correo el enlace de la página actual a un amigo
Author: Nandakumar Chandrasekhar
Version: 0.1
Author URI: http://w3boutique.net/about-nanda.html
License: GPL2
*/
// include() o require() de archivos necesarios aquí
// Configuraciones y detalles van aquí
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 requiere la versión '
. SEND_INVITATION_MIN_WORDPRESS_VERSION
. 'o superior <a href="http://codex.wordpress.org/Upgrading_WordPress">Por favor
actualice!</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() {
// Inicia el buffer de salida, evita que el contenido se muestre directamente
// en el script, en su lugar se almacena en un buffer que puede ser vaciado
// para permitir que el archivo incluido se almacene en una variable
// Ver 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 = 'Invitación a SwanLotus';
$message = 'Navanitachora te invita a ver este enlace';
wp_mail($to, $subject, $message);
$result = 'El correo fue enviado exitosamente';
}
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);
}
}
/* Fin del archivo */
?>
No sé cómo usar esta clase en mi plantilla para mostrar el formulario. Sé que necesito instanciar la clase pero no estoy seguro de dónde colocar el código y cómo acceder al objeto para usarlo en mi plantilla. Tengo conocimientos de POO pero no los he usado en este contexto antes y necesito una guía paso a paso.
Muchas gracias.

La mejor forma de usar tu clase sin conocer el objeto es mediante una acción. Registras la acción antes de que se carguen los archivos del tema para la presentación, WordPress se encargará del resto.
Código de ejemplo:
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: Plugin Action Demo
*/
add_action( 'init', array ( 'Plugin_Action_Demo', 'init' ) );
class Plugin_Action_Demo
{
/**
* Crea una nueva instancia.
*
* @wp-hook init
* @see __construct()
* @return void
*/
public static function init()
{
new self;
}
/**
* Registra la acción. Puede hacer más cosas mágicas.
*/
public function __construct()
{
add_action( 'plugin_action_demo', array ( $this, 'print_foo' ), 10, 1 );
}
/**
* Imprime 'foo' múltiples $times.
*
* Uso:
* <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 );
}
}
Ahora puedes llamar do_action( 'plugin_action_demo', 50 );
en algún lugar de tu tema o en otro plugin, y no tienes que preocuparte más por el funcionamiento interno de la clase.
Si desactivas el plugin, aún estarás seguro: WordPress simplemente ignora acciones desconocidas y el do_action()
no causará daño. Además, otros plugins pueden eliminar o reemplazar la acción, así que has construido una pequeña API con un simple add_action()
.
También podrías construir un singleton:
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: Plugin Singleton Demo
*/
class Plugin_Singleton_Demo
{
protected static $instance = NULL;
/**
* Crea una nueva instancia si no existe una.
*
* @wp-hook init
* @return object
*/
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
/**
* No accesible desde el exterior.
*/
protected function __construct() {}
/**
* Imprime 'foo' múltiples $times.
*
* @param int $times
* @return void
*/
public function print_foo( $times = 1 )
{
echo str_repeat( ' foo ', (int) $times );
}
}
Ahora print_foo()
es accesible mediante:
Plugin_Singleton_Demo::get_instance()->print_foo();
No recomiendo el patrón Singleton. Tiene algunos inconvenientes serios.

Por curiosidad, ¿cuál es el propósito de plugin_action_demo
en add_action()
? ¿Cómo sabe do_action( 'print_foo', 50 );
que la acción es plugin_action_demo
o ese nombre es irrelevante?

Gracias, eso funciona, solo tengo que agregar alguna validación y debería estar listo. Gracias @toscho me has enseñado mucho. :-)

@tosco Encontré tu respuesta buscando artículos sobre singletons para plugins de WordPress. ¿Te das cuenta de que estás usando un Singleton en tu respuesta recomendada, solo que no lo estás imponiendo? Imagina que un diseñador de temas llama new Plugin_Action_Demo
?? Cualquiera que use do_action('plugin_action_demo')
activaría dos (2) llamadas a Plugin_Action_Demo->print_foo()
, que no es lo que quieres. El furor por los singletons ignora que hay casos de uso apropiados. Como información, tanto @ericmann como yo estamos blogueando ahora para defender el uso de singletons para el espacio de nombres de plugins de WordPress, él en eamann.com y yo en hardcorewp.com.

@MikeSchinkel El enfoque que recomiendo actualmente es este: https://gist.github.com/3804204 – puedes acceder a él como un Singleton, pero no tienes que hacerlo.

@tocho De acuerdo, veo que no tienes que acceder a tu ejemplo como un singleton, pero ¿cuál es el propósito? ¿En qué casos de uso querrías hacerlo?

@MikeSchinkel Pruebas unitarias y clases hijas. Y no, no cambio la visibilidad en una clase hija a menos que me vea obligado. :)

@toscho ¿Realmente haces pruebas unitarias de las clases que usas como base para tus plugins? Si es así, ¿cómo manejas los hooks que no se pueden probar sin una carga de página? ¿No hace eso que las pruebas unitarias de esas clases sean inútiles? Y estoy subclasificando mis singletons sin problema, de hecho es clave en mi arquitectura; ¿cómo es un problema? Por cierto, estoy a punto de publicar un post defendiendo los singletons y escribiré un seguimiento abordando las pruebas unitarias. Pero si tienes razones legítimas en contra, me gustaría saberlo antes de publicar. Gracias de antemano.

@MikeSchinkel Yo uso http://core.trac.wordpress.org/browser/tests – la carga de la página no es un problema. Y no me gusta la idea de Eric de probar una subclase que no es el código de producción real y difiere en comportamiento. Debería escribir un blog sobre esto, los comentarios no son el lugar adecuado. :)

@toscho - Espero con interés tu publicación en el blog. Por cierto, no entendí a qué te referías con "No cambio la visibilidad en una clase hija a menos que me vea obligado."

@toscho Además, cuando dijiste "la carga de la página no es un problema" creo que malinterpretaste; estaba diciendo que la carga de la página es prácticamente la única forma de probar una clase utilizada para conectar manejadores de hooks con métodos, es decir, que las pruebas unitarias clásicas no tienen sentido.
