Creare filtri personalizzati
I filtri sono strumenti potenti per formattare e modificare i dati direttamente nei template Latte. Offrono una
sintassi pulita, basata sul simbolo pipe (|), per trasformare le variabili o i risultati delle espressioni nel
formato di output desiderato.
Cosa sono i filtri?
I filtri in Latte sono in sostanza funzioni PHP pensate appositamente per trasformare un valore di ingresso in un valore di
uscita. Si applicano con la notazione a pipe (|) all'interno delle espressioni del template
({...}).
Comodità: i filtri permettono di racchiudere in unità riutilizzabili le operazioni di formattazione più comuni (formattazione delle date, cambio di maiuscole e minuscole, troncamento) o le manipolazioni dei dati. Invece di ripetere codice PHP complesso nei template, potete semplicemente applicare un filtro:
{* invece di codice PHP complesso per il troncamento: *}
{$article->text|truncate:100}
{* invece del codice per formattare la data: *}
{$event->startTime|date:'Y-m-d H:i'}
{* applicando più trasformazioni: *}
{$product->name|lower|capitalize}
Leggibilità: l'uso dei filtri rende i template più puliti e più concentrati sulla presentazione, spostando la logica di trasformazione nella definizione del filtro.
Consapevolezza del contesto: un punto di forza fondamentale dei filtri di Latte è la possibilità di essere sensibili al contesto. Un filtro può cioè conoscere il tipo di contenuto su cui sta lavorando (HTML, JavaScript, testo semplice e così via) e applicare la logica o l'escaping appropriati, cosa essenziale per la sicurezza e la correttezza, soprattutto quando si genera HTML.
Integrazione con la logica applicativa: come per le funzioni personalizzate, il callable PHP dietro a un filtro può essere una closure, un metodo statico o un metodo di istanza. I filtri possono quindi accedere, se necessario, ai servizi o ai dati dell'applicazione, anche se il loro scopo principale resta trasformare il valore di ingresso.
Per impostazione predefinita Latte offre un ricco insieme di filtri standard. I filtri personalizzati permettono di ampliarlo con le esigenze di formattazione e trasformazione specifiche del vostro progetto.
Se dovete eseguire una logica basata su più valori di ingresso oppure non avete un valore principale da trasformare, è probabile che una funzione personalizzata sia più adatta. Se dovete generare markup complesso o controllare il flusso del template, considerate un tag personalizzato.
Creare e registrare i filtri
Ci sono diversi modi di definire e registrare filtri personalizzati in Latte.
Registrazione diretta con addFilter()
Il modo più semplice per aggiungere un filtro è usare il metodo addFilter() direttamente sull'oggetto
Latte\Engine. Indicate il nome del filtro (con cui verrà usato nel template) e il corrispondente callable PHP.
$latte = new Latte\Engine;
// filtro semplice senza argomenti
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');
// filtro con un argomento facoltativo
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
return mb_substr($s, 0, $len);
});
// filtro che elabora un array
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));
Uso nel template:
{$name|initial} {* stampa 'J.' se $name è 'John' *}
{$description|shortify} {* usa la lunghezza predefinita 10 *}
{$description|shortify:50} {* usa la lunghezza 50 *}
{$prices|sum} {* stampa la somma degli elementi dell'array $prices *}
Passaggio degli argomenti:
Il valore alla sinistra della pipe (|) viene sempre passato come primo argomento alla funzione del filtro.
Gli eventuali parametri indicati dopo i due punti (:) nel template vengono passati come argomenti successivi.
{$text|shortify:30}
// chiama la funzione PHP shortify($text, 30)
Registrazione tramite estensione
Per una migliore organizzazione, soprattutto quando si creano insiemi di filtri riutilizzabili o li si distribuisce come pacchetti, il modo consigliato è registrarli in un'estensione di 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);
}
}
// registrazione
$latte = new Latte\Engine;
$latte->addExtension(new MyLatteExtension);
Questo approccio mantiene la logica dei filtri ben racchiusa e rende immediata la registrazione.
Filtri tramite una classe con attributi
Un altro modo elegante di definire i filtri è usare i metodi della vostra classe dei parametri del template. Basta aggiungere al
metodo l'attributo #[Latte\Attributes\TemplateFilter].
use Latte\Attributes\TemplateFilter;
class TemplateParameters
{
public function __construct(
public string $description,
// altri parametri...
) {}
#[TemplateFilter]
public function shortify(string $s, int $len = 10): string
{
return mb_substr($s, 0, $len);
}
}
// passa l'oggetto al template
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);
Latte individua e registra automaticamente i metodi contrassegnati con questo attributo quando l'oggetto
TemplateParameters viene passato al template. Il nome del filtro nel template coincide con il nome del metodo (in
questo caso shortify).
{* usa il filtro definito nella classe dei parametri *}
{$description|shortify:50}
Filtri contestuali
A volte a un filtro serve più del semplice valore di ingresso. Può aver bisogno di conoscere il tipo di contenuto della stringa che sta elaborando (per esempio HTML, JavaScript, testo semplice) o addirittura di modificarlo. Ed è qui che entrano in gioco i filtri contestuali.
Un filtro contestuale si definisce esattamente come un filtro normale, ma il suo primo parametro deve essere dichiarato
di tipo Latte\Runtime\FilterInfo. Latte riconosce automaticamente questa firma e, chiamando il filtro, gli passa
l'oggetto FilterInfo. I parametri successivi ricevono gli argomenti del filtro come al solito.
use Latte\Runtime\FilterInfo;
use Latte\ContentType;
$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
// 1. controlla il tipo di contenuto in ingresso (facoltativo, ma consigliato)
// ammette null (variabile in ingresso) o testo semplice. Rifiuta l'uso su HTML ecc.
if (!in_array($info->contentType, [null, ContentType::Text], true)) {
$actualType = $info->contentType ?? 'mixed';
throw new \RuntimeException(
"Filtro |money usato con il tipo di contenuto incompatibile $actualType. Atteso testo o null."
);
}
// 2. esegue la trasformazione
$formatted = number_format($amount, 2, '.', ',') . ' EUR';
$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // garantite un escaping corretto!
// 3. dichiara il tipo di contenuto in uscita
$info->contentType = ContentType::Html;
// 4. restituisce il risultato
return $htmlOutput;
});
$info->contentType è una costante stringa di Latte\ContentType (per esempio
ContentType::Html, ContentType::Text, ContentType::JavaScript ecc.), oppure
null se il filtro è applicato a una variabile ({$var|filter}). Potete leggerlo per controllare
il contesto di ingresso e scriverlo per dichiarare il tipo di contesto in uscita.
Impostando il tipo di contenuto su HTML dite a Latte che la stringa restituita dal vostro filtro è HTML sicuro. Latte quindi non eseguirà su questo risultato il suo escaping automatico predefinito. È fondamentale se il vostro filtro genera markup HTML.
Se il vostro filtro genera HTML, siete voi i responsabili del corretto escaping dei dati in ingresso
usati in quell'HTML (come nella chiamata htmlspecialchars($formatted) qui sopra). Non farlo può creare
vulnerabilità XSS. Se il vostro filtro restituisce solo testo semplice, non è necessario impostare
$info->contentType.
Filtri sui blocchi
I filtri applicati ai blocchi con un tipo di contenuto diverso dal testo (di norma HTML) devono essere contestuali. Il motivo è che il contenuto del blocco ha un tipo di contenuto definito, di cui il filtro deve essere consapevole. Un filtro classico, non contestuale, può essere applicato solo a un blocco il cui contenuto è testo semplice.
{block heading|money}1000{/block}
{* il filtro 'money' riceve '1000' come secondo argomento
e $info->contentType sarà ContentType::Html *}
I filtri contestuali offrono un controllo potente su come i dati vengono elaborati in base al loro contesto: permettono funzionalità avanzate e garantiscono un comportamento di escaping corretto, soprattutto quando si genera contenuto HTML.