Cum să utilizezi o clasă de plugin în interiorul unui template WordPress
Scriu un plugin pentru trimiterea unei invitații către un prieten care deschide un formular când se face click pe un link. Am încapsulat toate funcțiile într-o clasă urmând codul din pluginul Report Broken Video de la @toscho. Codul relevant este mai jos:
/*
Plugin Name: Trimite Invitație
Plugin URI: http://w3boutique.net
Descriere: Trimite prin email linkul paginii curente unui prieten
Autor: Nandakumar Chandrasekhar
Versiune: 0.1
Author URI: http://w3boutique.net/about-nanda.html
Licență: GPL2
*/
// include() sau require() orice fișiere necesare aici
// Setări și detalii de configurare merg aici
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 = 'Trimite Invitația necesită versiunea '
. SEND_INVITATION_MIN_WORDPRESS_VERSION
. 'sau mai nouă <a href="http://codex.wordpress.org/Upgrading_WordPress">Te rog
actualizează!</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() {
// Pornim bufferul de output, previne afișarea directă a conținutului
// în script, în schimb este stocat într-un buffer care poate fi golit
// pentru a permite includerea fișierului într-o variabilă
// Vezi 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 = 'Invitație la SwanLotus';
$message = 'Navanitachora te invită să vizitezi acest link';
wp_mail($to, $subject, $message);
$result = 'Emailul a fost trimis cu succes';
}
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);
}
}
/* Sfârșitul fișierului */
?>
Nu știu cum să folosesc această clasă în template-ul meu pentru a afișa formularul. Știu că trebuie să instantiez clasa dar nu sunt sigur unde să pun codul și cum să accesez obiectul pentru a-l folosi în template. Am cunoștințe de OOP dar nu le-am folosit în acest context până acum și am nevoie de câteva indicații pas cu pas.
Mulțumesc mult.

Cea mai bună metodă de a utiliza clasa ta fără a cunoaște obiectul este prin intermediul unei acțiuni. Înregistrezi acțiunea înainte ca fișierele temei pentru prezentare să fie încărcate, iar WordPress se va ocupa de restul.
Cod exemplu:
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: Demo Acțiune Plugin
*/
add_action( 'init', array ( 'Plugin_Action_Demo', 'init' ) );
class Plugin_Action_Demo
{
/**
* Creează o nouă instanță.
*
* @wp-hook init
* @see __construct()
* @return void
*/
public static function init()
{
new self;
}
/**
* Înregistrează acțiunea. Poate face și alte lucruri magice.
*/
public function __construct()
{
add_action( 'plugin_action_demo', array ( $this, 'print_foo' ), 10, 1 );
}
/**
* Afișează 'foo' de mai multe $times ori.
*
* Utilizare:
* <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 );
}
}
Acum poți apela do_action( 'plugin_action_demo', 50 );
undeva în tema ta sau într-un alt plugin, și nu mai trebuie să te preocupi de funcționalitățile interne ale clasei.
Dacă dezactivezi pluginul, totul este în siguranță: WordPress pur și simplu ignoră acțiunile necunoscute, iar do_action()
nu va crea probleme. În plus, alte pluginuri pot elimina sau înlocui acțiunea, astfel că ai creat o mini-API simplă cu un singur add_action()
.
Ai putea de asemenea să construiești un singleton:
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: Demo Singleton Plugin
*/
class Plugin_Singleton_Demo
{
protected static $instance = NULL;
/**
* Creează o nouă instanță dacă nu există una.
*
* @wp-hook init
* @return object
*/
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
/**
* Nu este accesibil din exterior.
*/
protected function __construct() {}
/**
* Afișează 'foo' de mai multe $times ori.
*
* @param int $times
* @return void
*/
public function print_foo( $times = 1 )
{
echo str_repeat( ' foo ', (int) $times );
}
}
Acum print_foo()
este accesibil prin:
Plugin_Singleton_Demo::get_instance()->print_foo();
Nu recomand modelul Singleton. Acesta are câteva dezavantaje serioase.

Din curiozitate, care este scopul lui plugin_action_demo
în add_action()
? Cum știe do_action( 'print_foo', 50 );
că acțiunea este plugin_action_demo
sau acel nume este irelevant?

Mulțumesc, funcționează, trebuie doar să adaug niște validări și ar trebui să fiu gata. Mulțumesc @toscho, m-ai învățat multe. :-)

@tosco Am găsit răspunsul tău în timp ce căutam articole despre singleton pentru plugin-uri WordPress. Îți dai seama că folosești un Singleton în răspunsul tău recomandat, doar că nu îl impui? Imaginează-ți că un temar apelează new Plugin_Action_Demo
?? Oricine folosește do_action('plugin_action_demo')
ar declanșa două (2) apeluri la Plugin_Action_Demo->print_foo()
, nu ceea ce vrei. Controversele în jurul singleton-ilor ignoră faptul că există cazuri de utilizare adecvate. Ca o informație, atât @ericmann cât și eu bloguim acum pentru a promova singleton-i pentru namespace-uri în plugin-urile WordPress, el la eamann.com și eu la hardcorewp.com.

@MikeSchinkel Abordarea pe care o recomand în prezent este aceasta: https://gist.github.com/3804204 – poți să o accesezi ca pe un Singleton, dar nu e obligatoriu.

@tocho Bine, văd că nu trebuie să accesezi exemplul tău ca un singleton, dar care e scopul? În ce cazuri de utilizare ai vrea să faci asta?

@MikeSchinkel Teste unitare și clase derivate. Și nu, nu modific vizibilitatea într-o clasă derivată decât dacă sunt obligat. :)

@toscho Testezi efectiv unitar clasele folosite ca bază pentru plugin-urile tale? Dacă da, cum te descurci cu hook-urile care nu pot fi testate fără încărcarea unei pagini? Nu face asta testarea unitară a acelor clase inutilă? Și eu folosesc moștenire pe singleton-urile mele fără probleme, de fapt e esențial în arhitectura mea; cum reprezintă o problemă? Apropo, sunt pe cale să public un post care susține singleton-urile și voi scrie o continuare despre testarea unitară. Dar dacă ai motive valide împotriva, aș vrea să le știu înainte de publicare. Mulțumesc anticipat.

@MikeSchinkel Eu folosesc http://core.trac.wordpress.org/browser/tests – timpul de încărcare al paginii nu este o problemă. Și nu-mi place ideea lui Eric de a testa o subclasă care nu este codul de producție real și care diferă în comportament. Ar trebui să scriu un articol despre asta, comentariile nu sunt făcute pentru așa ceva. :)

@toscho - Aștept cu interes articolul tău. Apropo, nu am înțeles ce ai vrut să spui prin "Nu schimb vizibilitatea într-o clasă copil decât dacă sunt nevoit."

@MikeSchinkel Vizibilitatea constructorului conform recomandării lui Eric.

@toscho De asemenea, când ai spus "încărcarea paginii nu este o problemă" cred că ai înțeles greșit; eu spuneam că încărcarea paginii este practic singura modalitate de a testa o clasă folosită pentru a conecta handler-ele de hook-uri cu metode, adică testarea unitară clasică nu are sens.
