Использование класса плагина внутри шаблона WordPress

7 мар. 2012 г., 09:28:07
Просмотры: 16.9K
Голосов: 10

Я пишу плагин для отправки приглашения другу, который открывает форму при клике на ссылку. Все функции я инкапсулировал в класс, следуя примеру плагина Report Broken Video от @toscho. Соответствующий код приведен ниже:

/*
Plugin Name: Send Invitation
Plugin URI: http://w3boutique.net
Description: Отправляет ссылку на текущую страницу другу по email
Author: Nandakumar Chandrasekhar
Version: 0.1
Author URI: http://w3boutique.net/about-nanda.html
License: GPL2
*/

// include() или require() необходимых файлов

// Настройки и конфигурация
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 требуется версия '
    . SEND_INVITATION_MIN_WORDPRESS_VERSION
    . 'или новее <a href="http://codex.wordpress.org/Upgrading_WordPress">Пожалуйста,
обновите WordPress!</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() {
       // Буферизация вывода для сохранения содержимого include в переменную
       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 = 'Приглашение на SwanLotus';
            $message = 'Navanitachora приглашает вас посмотреть эту ссылку';
            wp_mail($to, $subject, $message);
            $result = 'Письмо успешно отправлено';
    }
    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);
}
}
/* Конец файла */
?>

Я не понимаю, как использовать этот класс в своем шаблоне, чтобы отобразить форму. Я знаю, что нужно создать экземпляр класса, но не уверен, куда поместить этот код и как получить доступ к объекту для использования в шаблоне. У меня есть знания ООП, но я не использовал его в таком контексте и мне нужно пошаговое руководство.

Заранее спасибо.

0
Все ответы на вопрос 1
14

Лучший способ использовать ваш класс без необходимости знать объект — это действие (action). Вы регистрируете действие до загрузки файлов темы для отображения, а WordPress позаботится обо всем остальном.

Пример кода:

<?php # -*- coding: utf-8 -*-
/**
 * Plugin Name: Plugin Action Demo
 */
add_action( 'init', array ( 'Plugin_Action_Demo', 'init' ) );

class Plugin_Action_Demo
{
    /**
     * Создает новый экземпляр.
     *
     * @wp-hook init
     * @see    __construct()
     * @return void
     */
    public static function init()
    {
        new self;
    }

    /**
     * Регистрирует действие. Может выполнять дополнительные магические вещи.
     */
    public function __construct()
    {
        add_action( 'plugin_action_demo', array ( $this, 'print_foo' ), 10, 1 );
    }

    /**
     * Выводит 'foo' указанное количество $times.
     *
     * Использование:
     *    <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 );
    }
}

Теперь вы можете вызвать do_action( 'plugin_action_demo', 50 ); где-нибудь в вашей теме или в другом плагине, и вам больше не нужно заботиться о внутренней работе класса.

Если вы деактивируете плагин, вы все равно в безопасности: WordPress просто игнорирует неизвестные действия, и вызов do_action() не причинит вреда. Кроме того, другие плагины могут удалить или заменить действие, поэтому вы создали мини-API всего с одним add_action().

Вы также можете реализовать синглтон:

<?php # -*- coding: utf-8 -*-
/**
 * Plugin Name: Plugin Singleton Demo
 */
class Plugin_Singleton_Demo
{
    protected static $instance = NULL;

    /**
     * Создает новый экземпляр, если его еще нет.
     *
     * @wp-hook init
     * @return object
     */
    public static function get_instance()
    {

        NULL === self::$instance and self::$instance = new self;
        return self::$instance;
    }

    /**
     * Недоступен извне.
     */
    protected function __construct() {}

    /**
     * Выводит 'foo' указанное количество $times.
     *
     * @param int $times
     * @return void
     */
    public function print_foo( $times = 1 )
    {
        echo str_repeat( ' foo ', (int) $times );
    }
}

Теперь метод print_foo() доступен через:

Plugin_Singleton_Demo::get_instance()->print_foo();

Я не рекомендую использовать паттерн Singleton. У него есть несколько серьезных недостатков.

7 мар. 2012 г. 09:52:51
Комментарии

Из чистого любопытства, какова цель plugin_action_demo в add_action()? Как do_action( 'print_foo', 50 ); узнаёт, что действие — это plugin_action_demo, или это имя не имеет значения?

Jared Jared
7 мар. 2012 г. 10:05:49

Ок, немного запутался. :) Спасибо, что прояснили.

Jared Jared
7 мар. 2012 г. 10:26:57

Для справки: declare() требует php 5.3+ :)

kaiser kaiser
7 мар. 2012 г. 11:23:38

Спасибо, это работает, осталось только добавить немного валидации, и я закончу. Спасибо @toscho, ты многому меня научил. :-)

nandac nandac
8 мар. 2012 г. 09:12:15

@tosco Нашел твой ответ, когда искал статьи о синглтонах для плагинов WordPress. Ты же понимаешь, что в твоем рекомендуемом ответе ты используешь синглтон, просто не форсируешь его? Представь, если темист вызовет new Plugin_Action_Demo?? Любой, кто использует do_action('plugin_action_demo'), вызовет два (2) вызова Plugin_Action_Demo->print_foo(), а это не то, что нужно. Шумиха вокруг синглтонов игнорирует тот факт, что есть подходящие случаи их использования. Кстати, и @ericmann, и я сейчас ведем блоги, где выступаем за использование синглтонов для пространств имен плагинов WordPress: он на eamann.com, а я на hardcorewp.com.

MikeSchinkel MikeSchinkel
9 янв. 2013 г. 16:34:53

@MikeSchinkel Подход, который я сейчас рекомендую, такой: https://gist.github.com/3804204 – к нему можно обращаться как к синглтону, но не обязательно.

fuxia fuxia
9 янв. 2013 г. 16:38:04

@tocho Хорошо, я вижу, что тебе не нужно обращаться к твоему примеру как к синглтону, но в чем смысл? В каких случаях это может быть полезно?

MikeSchinkel MikeSchinkel
9 янв. 2013 г. 17:34:31

@MikeSchinkel Юнит-тесты и дочерние классы. И нет, я не изменяю видимость в дочернем классе, если меня не заставят. :)

fuxia fuxia
9 янв. 2013 г. 17:44:37

@toscho Ты действительно пишешь юнит-тесты для классов, используемых в основе твоих плагинов? Если да, то как ты справляешься с хуками, которые нельзя протестировать без загрузки страницы? Разве это не делает юнит-тестирование этих классов бессмысленным? И я без проблем создаю подклассы для своих синглтонов, более того, это ключевой элемент моей архитектуры; почему это может быть проблемой? Кстати, я собираюсь опубликовать пост в защиту синглтонов и напишу продолжение про юнит-тестирование. Но если у тебя есть веские аргументы против, я хотел бы узнать об этом до публикации. Заранее спасибо.

MikeSchinkel MikeSchinkel
9 янв. 2013 г. 17:51:00

@MikeSchinkel Я использую http://core.trac.wordpress.org/browser/tests – скорость загрузки страницы не проблема. И мне не нравится идея Эрика тестировать подкласс, который не является реальным рабочим кодом и отличается в поведении. Надо бы написать об этом в блоге, комментарии не для этого. :)

fuxia fuxia
9 янв. 2013 г. 18:09:08

@toscho - Буду ждать твою запись в блоге. Кстати, я не понял, что ты имел в виду под "Я не меняю видимость в дочернем классе, если меня к этому не вынуждают."

MikeSchinkel MikeSchinkel
9 янв. 2013 г. 20:32:29

@MikeSchinkel Видимость конструктора, как рекомендовал Эрик.

fuxia fuxia
9 янв. 2013 г. 20:33:23

@toscho Ах, Эрик — это не я. :)

MikeSchinkel MikeSchinkel
9 янв. 2013 г. 20:34:03

@toscho Также, когда ты сказал "загрузка страницы не проблема", мне кажется, ты недопонял; я имел в виду, что загрузка страницы — это практически единственный способ протестировать класс, используемый для подключения обработчиков хуков к методам, т.е. что классическое модульное тестирование здесь не имеет смысла.

MikeSchinkel MikeSchinkel
9 янв. 2013 г. 20:36:26
Показать остальные 9 комментариев