Questo è un approccio alternativo a @kaiser risposta di , che ho trovato abbastanza bene (+1 da parte mia) ma richiede un lavoro aggiuntivo da utilizzare con le funzioni WP di base ed è di per sé basso integrato con la gerarchia dei modelli.
L'approccio che voglio condividere si basa su una singola classe (è una versione ridotta da qualcosa su cui sto lavorando) che si occupa dei dati di rendering per i modelli.
Ha alcune caratteristiche interessanti (IMO):
- i modelli sono file standard di WordPress (single.php, page.php) e ottengono un po 'più di potenza
- i modelli esistenti funzionano, quindi puoi integrare modelli da temi esistenti senza alcuno sforzo
- a differenza di dell'approccio @kaiser , nei template accedi alle variabili usando la
$this
parola chiave: questo ti dà la possibilità di evitare avvisi in produzione in caso di variabili non definite
Il Engine
classe
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()
);
}
}
(Disponibile come Gist qui.)
Come usare
L'unica cosa necessaria è chiamare il Engine::init()
metodo, probabilmente 'template_redirect'
agganciato. Questo può essere fatto in tema functions.php
o da un plugin.
require_once '/path/to/the/file/Engine.php';
add_action('template_redirect', array('GM\Template\Engine', 'init'), 99);
È tutto.
I tuoi modelli esistenti funzioneranno come previsto. Ma ora hai la possibilità di accedere ai dati del modello personalizzato.
Dati modello personalizzati
Per passare i dati personalizzati ai modelli ci sono due filtri:
'gm_template_data'
'gm_template_data_{$type}'
Il primo viene attivato per tutti i modelli, il secondo è specifico del modello, infatti la parte dinamica {$type}
è il nome di base del file modello senza estensione.
Ad esempio, il filtro 'gm_template_data_single'
può essere utilizzato per passare dati alsingle.php
modello.
I callback associati a questi hook devono restituire un array , in cui le chiavi sono i nomi delle variabili.
Ad esempio, puoi trasmettere metadati come piace ai dati dei modelli:
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;
};
E poi, all'interno del modello puoi semplicemente usare:
<?= $this->extra_title ?>
Modalità di debug
Quando entrambe le costanti WP_DEBUG
e WP_DEBUG_DISPLAY
sono vere, la classe funziona in modalità debug. Significa che se non viene definita una variabile viene generata un'eccezione.
Quando la classe non è in modalità debug (probabilmente in produzione), l'accesso a una variabile non definita genererà una stringa vuota.
Modelli di dati
Un modo piacevole e manutenibile per organizzare i tuoi dati è usare le classi del modello.
Possono essere classi molto semplici, che restituiscono dati utilizzando gli stessi filtri sopra descritti. Non esiste un'interfaccia particolare da seguire, possono essere organizzati secondo le tue preferenze.
Di seguito, c'è solo un esempio, ma sei libero di fare a modo tuo.
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);
Il __invoke()
metodo (che viene eseguito quando una classe viene utilizzata come un callback) restituisce una stringa da utilizzare per il <title>
tag del modello.
Grazie al fatto che il secondo argomento passato 'gm_template_data'
è il nome del modello, il metodo restituisce un titolo personalizzato per la home page.
Avendo il codice sopra, è quindi possibile usare qualcosa di simile
<title><?= $this->seo_title ?></title>
nella <head>
sezione della pagina.
parziali
WordPress ha funzioni simili get_header()
o get_template_part()
che possono essere utilizzate per caricare i parziali nel modello principale.
Queste funzioni, proprio come tutte le altre funzioni di WordPress, possono essere utilizzate nei modelli quando si utilizza la Engine
classe.
L'unico problema è che all'interno dei parziali caricati utilizzando le funzioni principali di WordPress non è possibile utilizzare la funzionalità avanzata di ottenere dati modello personalizzati utilizzando $this
.
Per questo motivo, la Engine
classe ha un metodo partial()
che consente di caricare un parziale (in modo completamente compatibile con temi secondari) e di poter comunque utilizzare in parziali i dati del modello personalizzato.
L'utilizzo è piuttosto semplice.
Supponendo che ci sia un file chiamato partials/content.php
all'interno della cartella del tema (o tema figlio), può essere incluso usando:
<?php $this->partial('partials/content') ?>
All'interno di quel parziale sarà possibile accedere a tutti i dati del tema principale allo stesso modo.
A differenza delle funzioni di WordPress, il Engine::partial()
metodo consente di trasmettere dati specifici ai parziali, semplicemente passando una matrice di dati come secondo argomento.
<?php $this->partial('partials/content', array('greeting' => 'Welcome!')) ?>
Per impostazione predefinita, i parziali hanno accesso ai dati disponibili nel tema principale e alla spiegazione dei dati trasmessa.
Se una variabile passata in modo esplicito al parziale ha lo stesso nome di una variabile del tema principale, allora vince la variabile passata in modo esplicito.
Tuttavia, è anche possibile includere un parziale in modalità isolata , ovvero il parziale non ha accesso ai dati del tema principale. Per fare ciò, basta passare true
come terzo argomento a partial()
:
<?php $this->partial('partials/content', array('greeting' => 'Welcome!'), true) ?>
Conclusione
Anche se piuttosto semplice, la Engine
classe è piuttosto completa, ma sicuramente può essere ulteriormente migliorata. Ad esempio, non è possibile verificare se una variabile è definita o meno.
Grazie alla sua compatibilità al 100% con le funzionalità di WordPress e la gerarchia dei modelli, puoi integrarlo senza problemi con codice esistente e di terze parti.
Tuttavia, tieni presente che è stato testato solo parzialmente, quindi è possibile che non ci siano ancora problemi rilevati.
I cinque punti in "Che cosa abbiamo guadagnato?" nella risposta di @kaiser :
- Scambia facilmente i modelli senza modificare la struttura dei dati
- Abbia leggere tempaltes
- Evitare l'ambito globale
- Can Unit-Test
- Può scambiare il Modello / i dati senza danneggiare altri componenti
sono tutti validi anche per la mia classe.