Creación de filtros personalizados
Los filtros son herramientas potentes para dar formato y modificar datos directamente en las plantillas de Latte.
Ofrecen una sintaxis limpia, con el símbolo de tubería (|), para transformar variables o resultados de expresiones
en el formato de salida deseado.
¿Qué son los filtros?
Los filtros de Latte son, en esencia, funciones PHP diseñadas específicamente para transformar un valor de entrada en un
valor de salida. Se aplican con la notación de tubería (|) dentro de las expresiones de la plantilla
({...}).
Comodidad: los filtros le permiten encapsular tareas de formateo habituales (formatear fechas, cambiar mayúsculas y minúsculas, truncar) o manipulaciones de datos en unidades reutilizables. En lugar de repetir código PHP complejo en sus plantillas, basta con aplicar un filtro:
{* En lugar de PHP complejo para recortar: *}
{$article->text|truncate:100}
{* En lugar de código para formatear fechas: *}
{$event->startTime|date:'Y-m-d H:i'}
{* Aplicando varias transformaciones: *}
{$product->name|lower|capitalize}
Legibilidad: usar filtros hace las plantillas más limpias y más centradas en la presentación, al trasladar la lógica de transformación a la definición del filtro.
Conciencia del contexto: una fortaleza clave de los filtros de Latte es que pueden ser sensibles al contexto. Esto significa que un filtro puede saber con qué tipo de contenido está trabajando (HTML, JavaScript, texto plano, etc.) y aplicar la lógica o el escapado adecuados, algo crucial para la seguridad y la corrección, sobre todo al generar HTML.
Integración con la lógica de la aplicación: igual que ocurre con las funciones personalizadas, el callable de PHP que hay detrás de un filtro puede ser un closure, un método estático o un método de instancia. Así, los filtros pueden acceder a servicios o datos de la aplicación si hace falta, aunque su cometido principal sigue siendo transformar el valor de entrada.
De forma predeterminada, Latte ofrece un rico conjunto de filtros estándar. Los filtros personalizados le permiten ampliarlo con las necesidades de formateo y transformación propias de su proyecto.
Si necesita ejecutar lógica basada en varias entradas o no tiene un valor principal que transformar, seguramente encaje mejor una función personalizada. Si necesita generar marcado complejo o controlar el flujo de la plantilla, considere una etiqueta personalizada.
Crear y registrar filtros
Hay varias formas de definir y registrar filtros personalizados en Latte.
Registro directo con addFilter()
La forma más sencilla de añadir un filtro es usar el método addFilter() directamente sobre el objeto
Latte\Engine. Indique el nombre del filtro (tal como se usará en la plantilla) y el callable de PHP
correspondiente.
$latte = new Latte\Engine;
// Filtro simple sin argumentos
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');
// Filtro con un argumento opcional
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
return mb_substr($s, 0, $len);
});
// Filtro que procesa un array
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));
Uso en la plantilla:
{$name|initial} {* Muestra 'J.' si $name es 'John' *}
{$description|shortify} {* Usa la longitud predeterminada 10 *}
{$description|shortify:50} {* Usa la longitud 50 *}
{$prices|sum} {* Muestra la suma de los elementos del array $prices *}
Paso de argumentos:
El valor situado a la izquierda de la tubería (|) se pasa siempre como primer argumento a la función del
filtro. Los parámetros indicados tras los dos puntos (:) en la plantilla se pasan como argumentos siguientes.
{$text|shortify:30}
// Llama a la función PHP shortify($text, 30)
Registro mediante una extensión
Para una mejor organización, sobre todo al crear conjuntos reutilizables de filtros o al compartirlos como paquetes, lo recomendable es registrarlos dentro de una extensión de Latte:
namespace App\Templating;
use Latte\Extension;
class MyLatteExtension extends Extension
{
public function getFilters(): array
{
return [
'initial' => $this->initial(...),
'shortify' => $this->shortify(...),
];
}
public function initial(string $s): string
{
return mb_substr($s, 0, 1) . '.';
}
public function shortify(string $s, int $len = 10): string
{
return mb_substr($s, 0, $len);
}
}
// Registro
$latte = new Latte\Engine;
$latte->addExtension(new MyLatteExtension);
Este enfoque mantiene encapsulada la lógica de sus filtros y simplifica el registro.
Filtros mediante una clase con atributos
Otra forma elegante de definir filtros es mediante métodos dentro de su clase de parámetros de la plantilla. Basta con añadir al
método el atributo #[Latte\Attributes\TemplateFilter].
use Latte\Attributes\TemplateFilter;
class TemplateParameters
{
public function __construct(
public string $description,
// otros parámetros...
) {}
#[TemplateFilter]
public function shortify(string $s, int $len = 10): string
{
return mb_substr($s, 0, $len);
}
}
// Pasa el objeto a la plantilla
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);
Latte detecta y registra automáticamente los métodos marcados con este atributo cuando se pasa a la plantilla el objeto
TemplateParameters. El nombre del filtro en la plantilla será el mismo que el del método (shortify en
este caso).
{* Usa el filtro definido en la clase de parámetros *}
{$description|shortify:50}
Filtros contextuales
A veces un filtro necesita más información que el mero valor de entrada. Puede necesitar saber el tipo de contenido de la cadena que está procesando (por ejemplo, HTML, JavaScript, texto plano) o incluso modificarlo. Para eso están los filtros contextuales.
Un filtro contextual se define igual que un filtro normal, pero su primer parámetro debe declararse con el tipo
Latte\Runtime\FilterInfo. Latte reconoce automáticamente esta firma y pasa el objeto FilterInfo al
llamar al filtro. Los parámetros siguientes reciben los argumentos del filtro como de costumbre.
use Latte\Runtime\FilterInfo;
use Latte\ContentType;
$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
// 1. Comprueba el tipo de contenido de entrada (opcional pero recomendable)
// Permite null (entrada de variable) o texto plano. Rechaza si se aplica sobre HTML, etc.
if (!in_array($info->contentType, [null, ContentType::Text], true)) {
$actualType = $info->contentType ?? 'mixed';
throw new \RuntimeException(
"Filter |money used in incompatible content type $actualType. Expected text or null."
);
}
// 2. Realiza la transformación
$formatted = number_format($amount, 2, '.', ',') . ' EUR';
$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // ¡Asegure el escapado correcto!
// 3. Declara el tipo de contenido de salida
$info->contentType = ContentType::Html;
// 4. Devuelve el resultado
return $htmlOutput;
});
$info->contentType es una constante de cadena de Latte\ContentType (por ejemplo,
ContentType::Html, ContentType::Text, ContentType::JavaScript, etc.), o null
si el filtro se aplica a una variable ({$var|filter}). Puede leerlo para comprobar el contexto de entrada y
escribir en él para declarar el tipo de contexto de salida.
Al fijar el tipo de contenido a HTML, le dice a Latte que la cadena devuelta por su filtro es HTML seguro. Latte no aplicará entonces su escapado automático predeterminado a ese resultado. Esto es crucial si su filtro genera marcado HTML.
Si su filtro genera HTML, usted es responsable de escapar correctamente todos los datos de entrada que
use dentro de ese HTML (como en la llamada htmlspecialchars($formatted) de arriba). No hacerlo puede abrir
vulnerabilidades XSS. Si su filtro solo devuelve texto plano, no necesita fijar $info->contentType.
Filtros sobre bloques
Los filtros aplicados a bloques con un tipo de contenido distinto de texto (normalmente HTML) deben ser contextuales. Y es que el contenido del bloque tiene un tipo de contenido definido que el filtro necesita conocer. Un filtro clásico, no contextual, solo puede aplicarse a un bloque cuyo contenido sea texto plano.
{block heading|money}1000{/block}
{* El filtro 'money' recibe '1000' como segundo argumento
and $info->contentType will be ContentType::Html *}
Los filtros contextuales ofrecen un control potente sobre cómo se procesan los datos según su contexto, lo que permite funciones avanzadas y garantiza un escapado correcto, sobre todo al generar contenido HTML.