Este es un enfoque alternativo para @kaiser respuesta de , que encontré bastante bien (+1 de mi parte) pero requiere un trabajo adicional para ser utilizado con las funciones principales de WP y está per se bajo integrado con la jerarquía de plantillas.
El enfoque que quiero compartir se basa en una sola clase (es una versión simplificada de algo en lo que estoy trabajando) que se encarga de procesar los datos para las plantillas.
Tiene algunas características interesantes (IMO):
- las plantillas son archivos de plantilla estándar de WordPress (single.php, page.php) obtienen un poco más de potencia
- Las plantillas existentes simplemente funcionan, por lo que puede integrar plantillas de temas existentes sin esfuerzo
- a diferencia del enfoque @kaiser , en las plantillas se accede a las variables usando
$this
palabras clave: esto le brinda la posibilidad de evitar avisos en la producción en caso de variables indefinidas
La Engine
clase
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 como Gist aquí).
Cómo utilizar
Lo único que se necesita es llamar al Engine::init()
método, probablemente en el 'template_redirect'
gancho. Eso se puede hacer en tema functions.php
o desde un complemento.
require_once '/path/to/the/file/Engine.php';
add_action('template_redirect', array('GM\Template\Engine', 'init'), 99);
Eso es todo.
Sus plantillas existentes funcionarán como expirado. Pero ahora tiene la posibilidad de acceder a datos de plantilla personalizados.
Datos de plantilla personalizados
Para pasar datos personalizados a las plantillas hay dos filtros:
'gm_template_data'
'gm_template_data_{$type}'
El primero se activa para todas las plantillas, el segundo es específico de la plantilla, de hecho, la parte dinámica {$type}
es el nombre base del archivo de plantilla sin extensión de archivo.
Por ejemplo, el filtro 'gm_template_data_single'
se puede usar para pasar datos a la single.php
plantilla.
Las devoluciones de llamada adjuntas a estos enlaces tienen que devolver una matriz , donde las claves son los nombres de las variables.
Por ejemplo, puede pasar metadatos como datos de plantilla como:
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;
};
Y luego, dentro de la plantilla puedes usar:
<?= $this->extra_title ?>
Modo de depuración
Cuando ambas constantes WP_DEBUG
y WP_DEBUG_DISPLAY
son verdaderas, la clase funciona en modo de depuración. Significa que si una variable no está definida, se lanza una excepción.
Cuando la clase no está en modo de depuración (probablemente en producción) acceder a una variable indefinida generará una cadena vacía.
Modelos de datos
Una forma agradable y sostenible de organizar sus datos es usar clases de modelo.
Pueden ser clases muy simples, que devuelven datos utilizando los mismos filtros descritos anteriormente. No hay una interfaz en particular a seguir, se pueden organizar según sus preferencias.
A continuación, solo hay un ejemplo, pero puede hacerlo a su manera.
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);
El __invoke()
método (que se ejecuta cuando una clase se usa como una devolución de llamada) devuelve una cadena que se usará para la <title>
etiqueta de la plantilla.
Gracias al hecho de que el segundo argumento pasado 'gm_template_data'
es el nombre de la plantilla, el método devuelve un título personalizado para la página de inicio.
Tener el código anterior, entonces es posible usar algo como
<title><?= $this->seo_title ?></title>
en la <head>
sección de la página.
Parciales
WordPress tiene funciones como get_header()
o get_template_part()
que se pueden usar para cargar parciales en la plantilla principal.
Estas funciones, al igual que todas las demás funciones de WordPress, se pueden usar en plantillas cuando se usa la Engine
clase.
El único problema es que dentro de los parciales cargados usando las funciones centrales de WordPress no es posible usar la función avanzada de obtener datos de plantillas personalizadas $this
.
Por esta razón, la Engine
clase tiene un método partial()
que permite cargar un parcial (de una manera totalmente compatible con temas secundarios) y aún así poder usar en parciales los datos de la plantilla personalizada.
El uso es bastante simple.
Suponiendo que hay un archivo llamado partials/content.php
dentro de la carpeta del tema (o tema secundario), se puede incluir usando:
<?php $this->partial('partials/content') ?>
Dentro de ese parcial, será posible acceder a todos los datos del tema principal de la misma manera.
A diferencia de las funciones de WordPress, el Engine::partial()
método permite pasar datos específicos a parciales, simplemente pasando una matriz de datos como segundo argumento.
<?php $this->partial('partials/content', array('greeting' => 'Welcome!')) ?>
De forma predeterminada, los parciales tienen acceso a los datos disponibles en el tema principal y a la explicilidad de datos aprobada.
Si alguna variable pasada explícitamente a parcial tiene el mismo nombre de una variable de tema principal, entonces la variable pasada explícitamente gana.
Sin embargo, también es posible incluir un parcial en modo aislado , es decir, el parcial no tiene acceso a los datos del tema principal. Para hacer eso, simplemente pase true
como tercer argumento a partial()
:
<?php $this->partial('partials/content', array('greeting' => 'Welcome!'), true) ?>
Conclusión
Incluso si es bastante simple, la Engine
clase es bastante completa, pero seguramente se puede mejorar aún más. Por ejemplo, no hay forma de verificar si una variable está definida o no.
Gracias a su compatibilidad al 100% con las funciones de WordPress y la jerarquía de plantillas, puede integrarlo con el código existente y de terceros sin problemas.
Sin embargo, tenga en cuenta que solo se prueba parcialmente, por lo que es posible que haya problemas que aún no he descubierto.
Los cinco puntos bajo "¿Qué ganamos?" en @kaiser respuesta :
- Intercambie plantillas fácilmente sin cambiar la estructura de datos
- Tener plantillas fáciles de leer
- Evitar alcance global
- Prueba unitaria de latas
- Puede intercambiar el modelo / los datos sin dañar otros componentes
Todos son válidos para mi clase también.