Во вчерашнем посте я рассказал о конструкторах плагинов WordPress и о том, почему хуки не должны быть в конструкторе.
Хотя я упомянул несколько способов регистрации хуков, я не стал вдаваться в подробности каждой из этих стратегий. Насколько я понимаю, они заслуживают отдельной статьи, чтобы предоставить как можно больше подробностей о том, как что-то настроить.
Например, один из методов, которыми я поделился, гласил:
- В WordPress можно создать класс, который поддерживает реестр объектов и хуков.
Другими словами, речь идет о регистрации хуков WordPress с использованием объектно-ориентированного подхода для уменьшения связанности и повышения согласованности между компонентами в плагине.
Но что это вообще значит? Каковы преимущества, которые он дает, как он настраивается и как он используется?
Регистрация хуков WordPress
Если вы читаете это, вы, вероятно, знакомы с системой хуков WordPress, порядком их запуска и тем, как функция или класс могут зарегистрировать свои функции в WordPress, чтобы они могли выполнять любую работу, с которой им нужно справиться.
И мы часто видим, как классы делают это самостоятельно. В зависимости от проекта, я делаю это сам. Для тех, кто не в курсе, обычно это выглядит примерно так :
<?php
add_action( 'plugins_loaded', 'acme_start' );
/**
* Start the machine.
* https://www.youtube.com/watch?v=ysoMOefPyRs
*/
function acme_start() {
$plugin = new AcmeColumn();
}
Но все это можно разбить на более сплоченные классы, чтобы в конечном итоге дать им еще меньше ответственности (это хорошо) и уменьшить связь между классом или набором классов с WordPress.
Пример дизайна, который я разберу в этом посте.
Однако противоречащий здравому смыслу характер этого заключается в том, что для этого потребуется как минимум еще один класс. Но вот как это работает.
Настройка
Для целей этого примера мы будем использовать простой класс, который будет регистрировать некоторый тип действия в WordPress. Идея архитектуры работает примерно так:
- Есть основной класс с функцией, которую мы хотим подключить к WordPress.
- Существует класс, отвечающий за организацию подключения функции класса к WordPress.
Достаточно легко, верно? Но вот в чем загвоздка: класс, отвечающий за регистрацию функций данного класса в WordPress, — это та точка, которая требует дизайнерского решения.
Во-первых, давайте назовем класс HookRegistry, чтобы мы могли правильно на него ссылаться. Далее, давайте вызовем класс с функциями, которые мы хотим подключить к AcmeColumn, просто для представления класса, который добавляет новый столбец на панель управления Page в административной области WordPress.
С учетом этого дизайнерское решение сводится к следующему:
- Должен ли HookRegistery знать об AcmeColumn?
- Должна ли AcmeColumn знать о HookRegistry?
Я знаю, что есть другие способы организовать это, а также есть стратегии, как справиться с этим (например, инверсия управления ), и это темы, которые стоит изучить, но чтобы сделать эту первоначальную идею максимально простой, я представлю ее для будущий пост.
Использование класса
Учитывая приведенные выше параметры, мы передадим экземпляр AcmeColumn в HookRegistry, когда классы будут созданы во время начального процесса запуска плагина WordPress. Это может выглядеть примерно так :
<?php
add_action( 'plugins_loaded', 'acme_start' );
/**
* Start the machine.
* https://www.youtube.com/watch?v=ysoMOefPyRs
*/
function acme_start() {
$registry = new HookRegistry();
$acme_column = new AcmeColumn( $registry );
$acme_column->start();
}
Затем, когда придет время, чтобы AcmeColumn зарегистрировала свою функцию в WordPress, мы вызовем HookRegistry и дадим ему указание сделать это.
Во- первых, AcmeColumn :
<?php
class AcmeColumn {
private $registry;
public function __construct( $registry) {
$this->registry = $registry;
}
public function start() {
$registry->add_hook( 'filter', 'manage_edit-page_columns', $this, 'add_page_column' );
}
public function add_page_column( $page_columns) {
$page_columns['template'] = 'Acme Column';
return $page_columns;
}
}
Затем HookRegistry :
<?php
class HookRegistry {
public add_hook( $type, $name, $object, $method) {
$type = strtolower( $type );
if ('filter' !== $type || 'action' !== $type) {
return new WP_Error( '1', 'No proper hook type defined.' );
}
}
private function add_filter( $name, $object, $method) {
add_filter( $name, array( $object, $method) );
}
private function add_action( $name, $object, $method) {
add_action( $name, array( $object, $method) );
}
}
При желании мы также можем вести список различных классов и хуков, которые были зарегистрированы. Это может быть или не быть полезным в зависимости от вашей реализации, поэтому я делюсь исключительно как «вот то, что вы, возможно, захотите сделать».
И это могло бы выглядеть так (используя простой ассоциативный массив):
<?php
class HookRegistry {
private $registry;
public function __construct() {
$this->registery = array();
}
public add_hook( $id, $type, $name, $object, $method) {
$type = strtolower( $type );
if ('filter' !== $type || 'action' !== $type) {
return new WP_Error( '1', 'No proper hook type defined.' );
}
if ('filter' === $type) {
$this->add_filter( $name, $object, $method );
} else {
$this->add_action( $name, $object, $method );
}
$hook_info = array(
$type,
$name,
$object,
$method,
);
$this->registry[ $id ] = $hook_info;
}
private function add_filter( $name, $object, $method) {
add_filter( $name, array( $object, $method) );
}
private function add_action( $name, $object, $method) {
add_action( $name, array( $object, $method) );
}
}
Обратите внимание, что в приведенном выше классе он теперь принимает $id в качестве параметра. Существует несколько способов идентификации информации, поступающей в реестр, самый простой из которых — создать идентификатор самостоятельно.
Однако, если вы хотите использовать что-то вроде имени хука или имени класса, это тоже сработает. Просто обратите внимание, что, поскольку это ассоциативный массив, он может поддерживать только одно значение для каждого ключа, поэтому вы можете в конечном итоге испортить предыдущие данные, если не будете осторожны.
В любом случае, это то, что я считаю необязательным, но если это реализовано, важно убедиться, что у вас есть правильные функции для извлечения экземпляра объекта по ключу.
Один из многих
Как и во всем, что связано с этим типом работы, можно перестроить или переориентировать ее так, чтобы она работала по-другому или соответствовала вашим потребностям. Цель состоит не в том, чтобы показать окончательный шаблон того, как что-то делать, а в том, чтобы подойти к нему и адаптировать его (во многом как любой шаблон проектирования).
Кроме того, это предназначено для того, чтобы наши классы сохраняли обязанности, для которых они созданы, в то же время позволяя им регистрироваться в WordPress по мере необходимости. На этот раз, однако, классу не нужно делать это самому.
Вместо этого он передает ответственность классу, который несет исключительную ответственность за регистрацию указанных перехватчиков. Таким образом, хотя он вводит больше классов, он увеличивает сплоченность и уменьшает связанность.
Это дает преимущества в обслуживании, тестировании и общем дизайне.