Це альтернативний підхід до @kaiser відповіді , що я виявив досить добре (+1 від мене), але вимагає додаткової роботи, щоб використовувати основні функції WP, і вона сама по собі низька, інтегрована з ієрархією шаблонів.
Підхід, яким я хочу поділитися, ґрунтується на одному класі (це знята версія з чогось над чим я працюю), який піклується про рендеринг даних для шаблонів.
Він має деякі (IMO) цікаві особливості:
- шаблони - це стандартні файли шаблонів WordPress (single.php, page.php), вони отримують трохи більше енергії
- існуючі шаблони просто працюють, тому ви можете без особливих зусиль інтегрувати шаблон із існуючих тем
- на відміну від @kaiser- підходу, у шаблонах ви отримуєте доступ до змінних за допомогою
$this
ключового слова: це дає можливість уникнути сповіщень у виробництві у випадку невизначених змінних
Engine
клас
namespace GM\Template;
class Engine
{
private $data;
private $template;
private $debug = false;
/**
* Bootstrap rendering process. Should be called on 'template_redirect'.
*/
public static function init()
{
add_filter('template_include', new static(), 99, 1);
}
/**
* Constructor. Sets debug properties.
*/
public function __construct()
{
$this->debug =
(! defined('WP_DEBUG') || WP_DEBUG)
&& (! defined('WP_DEBUG_DISPLAY') || WP_DEBUG_DISPLAY);
}
/**
* Render a template.
* Data is set via filters (for main template) or passed to method for partials.
* @param string $template template file path
* @param array $data template data
* @param bool $partial is the template a partial?
* @return mixed|void
*/
public function __invoke($template, array $data = array(), $partial = false)
{
if ($partial || $template) {
$this->data = $partial
? $data
: $this->provide(substr(basename($template), 0, -4));
require $template;
$partial or exit;
}
return $template;
}
/**
* Render a partial.
* Partial-specific data can be passed to method.
* @param string $template template file path
* @param array $data template data
* @param bool $isolated when true partial has no access on parent template context
*/
public function partial($partial, array $data = array(), $isolated = false)
{
do_action("get_template_part_{$partial}", $partial, null);
$file = locate_template("{$partial}.php");
if ($file) {
$class = __CLASS__;
$template = new $class();
$template_data = $isolated ? $data : array_merge($this->data, $data);
$template($file, $template_data, true);
} elseif ($this->debug) {
throw new \RuntimeException("{$partial} is not a valid partial.");
}
}
/**
* Used in templates to access data.
* @param string $name
* @return string
*/
public function __get($name)
{
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
if ($this->debug) {
throw new \RuntimeException("{$name} is undefined.");
}
return '';
}
/**
* Provide data to templates using two filters hooks:
* one generic and another query type specific.
* @param string $type Template file name (without extension, e.g. "single")
* @return array
*/
private function provide($type)
{
$generic = apply_filters('gm_template_data', array(), $type);
$specific = apply_filters("gm_template_data_{$type}", array());
return array_merge(
is_array($generic) ? $generic : array(),
is_array($specific) ? $specific : array()
);
}
}
(Доступно як Gist тут.)
Як користуватись
Єдине, що потрібно - це викликати Engine::init()
метод, ймовірно, на 'template_redirect'
гачку. Це можна зробити в темі functions.php
або з плагіна.
require_once '/path/to/the/file/Engine.php';
add_action('template_redirect', array('GM\Template\Engine', 'init'), 99);
Це все.
Ваші наявні шаблони будуть працювати як очікувані. Але тепер у вас є можливість отримати доступ до даних спеціальних шаблонів.
Дані користувацьких шаблонів
Для передачі спеціальних даних у шаблони є два фільтри:
'gm_template_data'
'gm_template_data_{$type}'
Перший запускається для всіх шаблонів, другий - специфічний для шаблону, насправді динамічна частина {$type}
- це ім'я файлу шаблону без розширення файлу.
Наприклад, фільтр 'gm_template_data_single'
може використовуватися для передачі даних доsingle.php
шаблону.
Зворотні виклики, приєднані до цих гаків, повинні повертати масив , де ключі є іменами змінних.
Наприклад, ви можете передавати метадані у такий спосіб, як так:
add_filter('gm_template_data', function($data) {
if (is_singular()) {
$id = get_queried_object_id();
$data['extra_title'] = get_post_meta($id, "_theme_extra_title", true);
}
return $data;
};
І тоді всередині шаблону ви можете просто використовувати:
<?= $this->extra_title ?>
Режим налагодження
Коли і константи, WP_DEBUG
і WP_DEBUG_DISPLAY
істинні, клас працює в режимі налагодження. Це означає, що якщо змінна не визначена, викидається виняток.
Коли клас не знаходиться в режимі налагодження (можливо, у виробництві), доступ до невизначеної змінної виведе порожню рядок.
Моделі даних
Приємний та підтримуваний спосіб організації даних - це використання модельних класів.
Вони можуть бути дуже простими класами, які повертають дані за допомогою тих же фільтрів, що описані вище. Немає конкретного інтерфейсу, який слід керувати, їх можна організувати відповідно до ваших уподобань.
Нижче, є лише приклад, але ви вільні робити по-своєму.
class SeoModel
{
public function __invoke(array $data, $type = '')
{
switch ($type) {
case 'front-page':
case 'home':
$data['seo_title'] = 'Welcome to my site';
break;
default:
$data['seo_title'] = wp_title(' - ', false, 'right');
break;
}
return $data;
}
}
add_filter('gm_template_data', new SeoModel(), 10, 2);
__invoke()
Метод (який працює , коли клас використовується як зворотний виклик) повертає рядок , які будуть використовуватися для <title>
тега шаблону.
Завдяки тому, що другий аргумент, який передається, 'gm_template_data'
- це ім'я шаблону, метод повертає власний заголовок для домашньої сторінки.
Маючи код вище, тоді можна використовувати щось подібне
<title><?= $this->seo_title ?></title>
у <head>
розділі сторінки.
Партії
WordPress має такі функції , як get_header()
і get_template_part()
що можна використовувати для завантаження партіалов в основний шаблон.
Ці функції, як і всі інші функції WordPress, можуть використовуватися в шаблонах при використанні Engine
класу.
Єдина проблема полягає в тому, що всередині частинок, завантажених за допомогою основних функцій WordPress, неможливо використовувати розширену функцію отримання даних користувальницького шаблону $this
.
З цієї причини в Engine
класі є метод, partial()
який дозволяє завантажити часткове (повністю сумісний з дочірньою темою спосіб) та все ще мати можливість використовувати в приватних даних користувальницькі шаблонні дані.
Використання досить просте.
Якщо припустимо, що у папці з partials/content.php
темою (або дочірньою темою) є файл , його можна включити, використовуючи:
<?php $this->partial('partials/content') ?>
Всередині цього часткового можливо отримати доступ до всіх даних батьківських тем однаковим чином.
На відміну від функцій WordPress, Engine::partial()
метод дозволяє передавати конкретні дані партизалам, просто передаючи масив даних як другий аргумент.
<?php $this->partial('partials/content', array('greeting' => 'Welcome!')) ?>
За замовчуванням партії мають доступ до даних, доступних у батьківській темі, та до переданих експлікацій даних.
Якщо якась змінна, явно передана частковій, має те саме ім'я батьківської тематичної змінної, то змінна, явно передана, перемагає.
Однак також можливо включити часткове в ізольованому режимі, тобто частковий не має доступу до даних батьківської теми. Для цього просто передайте true
третій аргумент partial()
:
<?php $this->partial('partials/content', array('greeting' => 'Welcome!'), true) ?>
Висновок
Навіть якщо він досить простий, Engine
клас досить повний, але, безумовно, його можна вдосконалити. Наприклад, немає можливості перевірити, чи визначена змінна чи ні.
Завдяки 100% сумісності з функціями WordPress та ієрархією шаблонів, ви можете інтегрувати його з існуючим та стороннім кодом без проблем.
Однак зауважте, що це лише частково перевірено, тому можливо, є проблеми, яких я ще не виявив.
П'ять балів у розділі "Що ми здобули?" у відповіді @kaiser :
- Легко обмінюватися шаблонами без зміни структури даних
- Майте легко читати шаблони
- Уникайте глобальних масштабів
- Може блок-тест
- Можна обміняти модель / дані, не завдаючи шкоди іншим компонентам
всі дійсні і для мого класу.