Простое руководство по организации классов, ориентированных на WordPress
Одна из вещей, над которой я приложил гораздо более согласованные усилия, вероятно, больше, чем когда-либо раньше, — это управление разделением задач между классами, ответственными за взаимодействие с WordPress, и теми, кто отвечает за работу с проблемной областью.
Например, предположим, что вы работаете над плагином, и он будет взаимодействовать со сторонним API. Кроме того, этот плагин также будет предлагать меню, типы сообщений, таксономии и т. д. в области администрирования WordPress.
Здесь две зоны ответственности:
- область, отвечающая за общее решение проблемы,
- область, отвечающая за взаимодействие с WordPress.
Вы можете утверждать, что важно объединять тестовые области, взаимодействующие с WordPress, но я также знаю, что это проверенные и настоящие API, у которых есть собственный набор тестов.
Вместо этого мы должны сосредоточиться на модульном тестировании и отделить нашу бизнес-логику от WordPress.
Но не в этом суть этого поста. Вместо этого это больше о способе потенциальной компоновки проекта, когда часть его будет взаимодействовать с WordPress.
Я говорил о важности и преимуществах пространств имен в предыдущих постах, так что не буду слишком углубляться в это обсуждение здесь.
Вместо этого я хочу поговорить об организации файлов на уровне файловой системы и уровне пространства имен, чтобы они были четко разделены по областям своей специализации и чтобы мы могли убедиться, что мы, скажем, сосредоточили наши модульные тесты (и другие тестирования) в наиболее важных областях.
Абстрагирование мета-боксов
Мне нравится следить за тем, чтобы мои каталоги и файловая структура отражали структуру моих пространств имен. Конечно, это помогает с файловой организацией, но также и с концептуальной организацией.
То есть, если я собираюсь работать с метабоксами, то я знаю, что, скорее всего, смогу найти файлы метабоксов в каталоге, вложенном в родительский каталог WordPress, а затем в подкаталоге Admin, за которым следует каталог MetaBox.
В связи с этим, как мог бы выглядеть набор классов, предназначенных для работы с метаблоками, если бы мы написали для них код, пригодный для повторного использования? Учитывая то, что мы знаем о метабоксах, мы знаем, что нам, вероятно, понадобится следующее:
- абстрактный класс, определяющий тип записи, к которому будет привязан каждый метабокс,
- две функции для метабокса — одна для его регистрации, одна для отображения содержимого,
- каталог, содержащий вид или презентацию метабокса,
- файл, который будет служить указанным представлением.
Учитывая вышеперечисленные моменты, возможно, структура каталогов будет выглядеть так:
Далее у нас есть код, который отражает эту структуру. То есть в нашем каталоге WordPress у нас будет подкаталог Admin, поскольку мета-поле отображается в области администрирования WordPress, и у нас будет подкаталог View, который будет содержать файл, отвечающий за отображение информации.
Это оставляет нам необходимость создать несколько классов, как указано выше. Возможно, абстрактный базовый класс будет выглядеть так:
<?php
namespace AcmeWordPressAdminMetaBox;
abstract class AbstractMetaBox
{
protected $postType;
public function __construct()
{
$this->postType = 'acme_post_type';
}
abstract public function render();
abstract public function display();
}
Тогда конкретная реализация расширит класс, и это будет выглядеть так:
<?php
namespace AcmeWordPressAdminMetaBox;
class AcmeMetaBox extends AbstractMetaBox
{
/**
* {@inheritdoc}
*/
public function render()
{
add_meta_box(
'acme-product-image',
'Product Image',
[$this, 'display'],
$this->postType,
'side',
'default'
);
}
/**
* {@inheritdoc}
*/
public function display()
{
include_once plugin_dir_path(__FILE__).'Views/acme-product-image.php';
}
}
И, наконец, представление для класса будет содержать любую разметку и код шаблона для рендеринга информации :
<div class="product-image-metabox">
<p>
<img src="<?= esc_html(get_post_meta(get_the_ID(), 'product_image', true)); ?>" alt="<?= esc_attr(get_the_title()); ?>" />
<input type="text" value="<?= esc_html(get_post_meta(get_the_ID(), 'product_image', true)); ?>" />
</p>
</div>
Это дает нам именно то, что нам нужно, в хорошо организованном, многоразовом способе работы с метабоксами. Его также можно повторить для таких вещей, как меню, типы записей, таксономии и так далее.
Но я отвлекся.
Несколько слов о модульном тестировании (с PHPUnit)
Как я упоминал ранее в посте, я считаю, что классы модульного тестирования, которые решают проблемы, уникальные для нашей проблемной области, важны. Это означает, что вам нужно указать файлу конфигурации PHPUnit исключить файлы, ориентированные на WordPress.
Преимущество того, что я изложил выше, заключается в том, что это становится тривиально простым. Проще говоря, вы можете добавить это в свой файл phpunit.xml :
<testsuites>
<testsuite name="Plugin">
<directory>./tests</directory>
<exclude>./tests/phpunit</exclude>
<exclude>./src/WordPress</exclude>
</testsuite>
</testsuites>
Это дает вам возможность сосредоточиться на написании тестов специально для вашей проблемной области, при этом гарантируя, что вы пишете масштабируемый, удобный в сопровождении и повторно используемый код на основе WordPress.
В настоящее время я пишу электронную книгу (наряду с другим премиальным контентом). Если вам интересно, посмотрите, что вы получите.