Il s'agit d'une approche alternative à la réponse @kaiser , que j'ai trouvée assez bien (+1 de moi) mais nécessite un travail supplémentaire pour être utilisé avec les fonctions de base de WP et elle est en soi peu intégrée à la hiérarchie des modèles.
L'approche que je veux partager est basée sur une seule classe (c'est une version allégée de quelque chose sur laquelle je travaille) qui prend en charge le rendu des données pour les modèles.
Il a quelques fonctionnalités (IMO) intéressantes:
- les modèles sont des fichiers de modèles WordPress standard (single.php, page.php), ils obtiennent un peu plus de puissance
- les modèles existants fonctionnent, vous pouvez donc intégrer le modèle à partir de thèmes existants sans effort
- contrairement à l' approche @kaiser , dans les modèles, vous accédez aux variables à l'aide d'un
$this
mot-clé: cela vous donne la possibilité d'éviter les notifications en production en cas de variables non définies
La 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()
);
}
}
(Disponible sous forme de Gist ici.)
Comment utiliser
La seule chose nécessaire est d'appeler la Engine::init()
méthode, probablement en 'template_redirect'
décrochant. Cela peut être fait par thème functions.php
ou à partir d'un plugin.
require_once '/path/to/the/file/Engine.php';
add_action('template_redirect', array('GM\Template\Engine', 'init'), 99);
C'est tout.
Vos modèles existants fonctionneront comme prévu. Mais maintenant, vous avez la possibilité d'accéder aux données du modèle personnalisé.
Données de modèle personnalisé
Pour transmettre des données personnalisées aux modèles, il existe deux filtres:
'gm_template_data'
'gm_template_data_{$type}'
Le premier est déclenché pour tous les modèles, le second est spécifique au modèle, en fait, la partie dymamique {$type}
est le nom de base du fichier de modèle sans extension de fichier.
Par exemple, le filtre 'gm_template_data_single'
peut être utilisé pour transmettre des données au single.php
modèle.
Les rappels attachés à ces hooks doivent renvoyer un tableau , où les clés sont les noms des variables.
Par exemple, vous pouvez transmettre des métadonnées en tant que données de modèle comme ceci:
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;
};
Et puis, à l'intérieur du modèle, vous pouvez simplement utiliser:
<?= $this->extra_title ?>
Mode débogage
Lorsque les deux constantes WP_DEBUG
et WP_DEBUG_DISPLAY
sont vraies, la classe fonctionne en mode débogage. Cela signifie que si une variable n'est pas définie, une exception est levée.
Lorsque la classe n'est pas en mode débogage (probablement en production), l'accès à une variable non définie produira une chaîne vide.
Modèles de données
Une façon agréable et maintenable d'organiser vos données est d'utiliser des classes de modèles.
Il peut s'agir de classes très simples, qui renvoient des données à l'aide des mêmes filtres décrits ci-dessus. Il n'y a pas d'interface particulière à suivre, elles peuvent être organisées selon vos préférences.
Ci-dessous, il y a juste un exemple, mais vous êtes libre de le faire à votre manière.
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);
La __invoke()
méthode (qui s'exécute lorsqu'une classe est utilisée comme un rappel) renvoie une chaîne à utiliser pour la <title>
balise du modèle.
Grâce au fait que le deuxième argument passé 'gm_template_data'
est le nom du modèle, la méthode renvoie un titre personnalisé pour la page d'accueil.
Ayant le code ci-dessus, il est alors possible d'utiliser quelque chose comme
<title><?= $this->seo_title ?></title>
dans la <head>
section de la page.
Partiels
WordPress a des fonctions comme get_header()
ou get_template_part()
qui peuvent être utilisées pour charger des partiels dans le modèle principal.
Ces fonctions, comme toutes les autres fonctions WordPress, peuvent être utilisées dans les modèles lors de l'utilisation de la Engine
classe.
Le seul problème est qu'à l'intérieur des partiels chargés à l'aide des fonctions principales de WordPress, il n'est pas possible d'utiliser la fonctionnalité avancée d'obtenir des données de modèle personnalisé à l'aide $this
.
Pour cette raison, la Engine
classe a une méthode partial()
qui permet de charger un partiel (de manière entièrement compatible avec le thème enfant) et de pouvoir utiliser en partie les données du modèle personnalisé.
L'utilisation est assez simple.
En supposant qu'il existe un fichier nommé partials/content.php
dans le dossier thème (ou thème enfant), il peut être inclus en utilisant:
<?php $this->partial('partials/content') ?>
À l'intérieur de ce partiel, il sera possible d'accéder à toutes les données du thème parent de la même manière.
Contrairement aux fonctions WordPress, la Engine::partial()
méthode permet de transmettre des données spécifiques à des partiels, en passant simplement un tableau de données comme deuxième argument.
<?php $this->partial('partials/content', array('greeting' => 'Welcome!')) ?>
Par défaut, les partiels ont accès aux données disponibles dans le thème parent et aux données explicites transmises.
Si une variable passée explicitement à partial a le même nom qu'une variable de thème parent, alors la variable explicitement passée gagne.
Cependant, il est également possible d'inclure un partiel en mode isolé , c'est-à-dire que le partiel n'a pas accès aux données du thème parent. Pour ce faire, passez simplement true
le troisième argument à partial()
:
<?php $this->partial('partials/content', array('greeting' => 'Welcome!'), true) ?>
Conclusion
Même si elle est assez simple, la Engine
classe est assez complète, mais peut certainement être encore améliorée. Par exemple, il n'y a aucun moyen de vérifier si une variable est définie ou non.
Grâce à sa compatibilité à 100% avec les fonctionnalités de WordPress et la hiérarchie des modèles, vous pouvez l'intégrer sans problème avec du code existant et tiers.
Cependant, notez que ce n'est que partiellement testé, il est donc possible qu'il y ait des problèmes que je n'ai pas encore découverts.
Les cinq points sous "Qu'avons-nous gagné?" dans @kaiser réponse :
- Échangez facilement des modèles sans modifier la structure des données
- Ayez des tempaltes faciles à lire
- Évitez la portée mondiale
- Test unitaire
- Peut échanger le modèle / les données sans nuire aux autres composants
sont également valables pour ma classe.