Jest to alternatywne podejście do odpowiedzi @kaiser , które uważam za całkiem w porządku (+1 ode mnie), ale wymaga dodatkowej pracy do użycia z podstawowymi funkcjami WP i samo w sobie jest zintegrowane z hierarchią szablonów.
Podejście, które chcę udostępnić, opiera się na jednej klasie (jest to wersja uproszczona z czegoś, nad czym pracuję), który zajmuje się renderowaniem danych dla szablonów.
Ma kilka interesujących funkcji (IMO):
- Szablony to standardowe pliki szablonów WordPress (single.php, page.php), które zyskują nieco więcej mocy
- istniejące szablony po prostu działają, dzięki czemu można bez problemu zintegrować szablony z istniejących motywów
- w przeciwieństwie do podejścia @kaiser , w szablonach uzyskuje się dostęp do zmiennych za pomocą
$this
słowa kluczowego: daje to możliwość uniknięcia powiadomień podczas produkcji w przypadku niezdefiniowanych zmiennych
Engine
Class
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()
);
}
}
(Dostępne tutaj jako Gist .)
Jak używać
Jedyne, czego potrzeba, to wywołanie Engine::init()
metody, prawdopodobnie po 'template_redirect'
podniesieniu słuchawki. Można to zrobić w temacie functions.php
lub z wtyczki.
require_once '/path/to/the/file/Engine.php';
add_action('template_redirect', array('GM\Template\Engine', 'init'), 99);
To wszystko.
Twoje istniejące szablony będą działać zgodnie z oczekiwaniami. Ale teraz masz możliwość dostępu do niestandardowych danych szablonu.
Niestandardowe dane szablonu
Do przekazywania niestandardowych danych do szablonów służą dwa filtry:
'gm_template_data'
'gm_template_data_{$type}'
Pierwszy jest uruchamiany dla wszystkich szablonów, drugi jest specyficzny dla szablonu, w rzeczywistości część dymamiczna {$type}
jest baząame pliku szablonu bez rozszerzenia pliku.
Np. Filtr 'gm_template_data_single'
może służyć do przekazywania danych do single.php
szablonu.
Oddzwanianie dołączone do tych haków musi zwrócić tablicę , w której kluczami są nazwy zmiennych.
Na przykład możesz przekazywać metadane jako dane szablonu, więc:
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;
};
A następnie w szablonie możesz po prostu użyć:
<?= $this->extra_title ?>
Tryb debugowania
Gdy obie stałe WP_DEBUG
i WP_DEBUG_DISPLAY
są prawdziwe, klasa działa w trybie debugowania. Oznacza to, że jeśli zmienna nie jest zdefiniowana, generowany jest wyjątek.
Gdy klasa nie znajduje się w trybie debugowania (prawdopodobnie w środowisku produkcyjnym), dostęp do niezdefiniowanej zmiennej spowoduje wyświetlenie pustego ciągu.
Modele danych
Dobrym i łatwym w utrzymaniu sposobem organizacji danych jest użycie klas modeli.
Mogą to być bardzo proste klasy, które zwracają dane przy użyciu tych samych filtrów opisanych powyżej. Nie ma konkretnego interfejsu do naśladowania, mogą być zorganizowane zgodnie z twoimi preferencjami.
Belowe, jest tylko przykład, ale możesz to zrobić na swój własny sposób.
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()
Metoda (to działa, gdy klasa jest używana jak callback) zwraca ciąg być stosowane do <title>
tagu szablonu.
Dzięki temu, że drugim przekazywanym argumentem 'gm_template_data'
jest nazwa szablonu, metoda zwraca niestandardowy tytuł strony głównej.
Mając powyższy kod, można wtedy użyć czegoś takiego
<title><?= $this->seo_title ?></title>
w <head>
sekcji strony.
Częściowe
WordPress posiada funkcje podobne get_header()
lub get_template_part()
które mogą być wykorzystane do załadowania partials w głównym szablonie.
Te funkcje, podobnie jak wszystkie inne funkcje WordPress, mogą być używane w szablonach podczas korzystania z Engine
klasy.
Jedyny problem polega na tym, że w częściach załadowanych przy użyciu podstawowych funkcji WordPress nie można użyć zaawansowanej funkcji uzyskiwania niestandardowych danych szablonu $this
.
Z tego powodu Engine
klasa ma metodę, partial()
która pozwala na załadowanie częściowego (w sposób w pełni zgodny z motywem potomnym) i nadal jest w stanie używać częściowo niestandardowych danych szablonu.
Użycie jest dość proste.
Zakładając, że istnieje plik o nazwie partials/content.php
wewnątrz folderu motywu (lub motywu potomnego), można go dołączyć, używając:
<?php $this->partial('partials/content') ?>
Wewnątrz tej częściowej będzie można uzyskać dostęp do wszystkich danych motywu nadrzędnego w ten sam sposób.
W przeciwieństwie do funkcji WordPress, Engine::partial()
metoda pozwala przekazywać określone dane do częściowych, po prostu przekazując tablicę danych jako drugi argument.
<?php $this->partial('partials/content', array('greeting' => 'Welcome!')) ?>
Domyślnie częściowe mają dostęp do danych dostępnych w motywie nadrzędnym i do przekazywanych danych wyjaśniających.
Jeśli jakaś zmienna jawnie przekazana do częściowej ma taką samą nazwę nadrzędnej zmiennej motywu, wówczas zmienna jawnie przekazana wygrywa.
Jednak możliwe jest również włączenie częściowego w trybie izolowanym , tj. Częściowy nie ma dostępu do danych motywu nadrzędnego. Aby to zrobić, wystarczy przekazać true
trzeci argument do partial()
:
<?php $this->partial('partials/content', array('greeting' => 'Welcome!'), true) ?>
Wniosek
Nawet jeśli dość prosta, Engine
klasa jest dość kompletna, ale z pewnością można ją jeszcze ulepszyć. Np. Nie ma możliwości sprawdzenia, czy zmienna jest zdefiniowana, czy nie.
Dzięki 100% kompatybilności z funkcjami WordPress i hierarchią szablonów możesz bez problemu zintegrować go z istniejącym i zewnętrznym kodem.
Należy jednak pamiętać, że jest tylko częściowo testowany, więc możliwe są problemy, których jeszcze nie odkryłem.
Pięć punktów w części „Co zyskaliśmy?” w odpowiedzi @kaiser :
- Łatwo wymieniaj szablony bez zmiany struktury danych
- Czytelne tempaltes
- Unikaj globalnego zasięgu
- Może test jednostkowy
- Może wymieniać Model / dane bez szkody dla innych komponentów
są również ważne dla mojej klasy.