Tworzenie własnych filtrów
Filtry to potężne narzędzia do formatowania i modyfikowania danych bezpośrednio w szablonach Latte. Oferują
czystą składnię z użyciem znaku potoku (|), która przekształca zmienne lub wyniki wyrażeń do pożądanej
postaci.
Czym są filtry?
Filtry w Latte to w istocie funkcje PHP zaprojektowane specjalnie po to, aby przekształcić wartość wejściową w
wartość wyjściową. Stosuje się je zapisem z potokiem (|) wewnątrz wyrażeń szablonu
({...}).
Wygoda: Filtry pozwalają zamknąć typowe zadania formatujące (jak formatowanie dat, zmiana wielkości liter, skracanie) albo operacje na danych w jednostki wielokrotnego użytku. Zamiast powtarzać w szablonach złożony kod PHP, wystarczy zastosować filtr:
{* zamiast złożonego PHP do skracania: *}
{$article->text|truncate:100}
{* zamiast kodu formatującego datę: *}
{$event->startTime|date:'Y-m-d H:i'}
{* zastosowanie kilku przekształceń: *}
{$product->name|lower|capitalize}
Czytelność: Używanie filtrów sprawia, że szablony są czystsze i bardziej skupione na prezentacji, bo logika przekształceń przenosi się do definicji filtra.
Świadomość kontekstu: Kluczową siłą filtrów Latte jest to, że mogą być kontekstowe. Oznacza to, że filtr może rozpoznać typ treści, na której działa (HTML, JavaScript, zwykły tekst itd.), i zastosować odpowiednią logikę lub escapowanie, co ma kluczowe znaczenie dla bezpieczeństwa i poprawności, zwłaszcza przy generowaniu HTML.
Integracja z logiką aplikacji: Tak samo jak przy własnych funkcjach, callable PHP stojący za filtrem może być domknięciem, metodą statyczną albo metodą instancji. Dzięki temu filtry mogą w razie potrzeby sięgać po usługi lub dane aplikacji, choć ich głównym zadaniem pozostaje przekształcanie wartości wejściowej.
Domyślnie Latte udostępnia bogaty zestaw standardowych filtrów. Własne filtry pozwalają rozszerzyć go o potrzeby formatowania i przekształcania specyficzne dla Twojego projektu.
Jeśli potrzebujesz logiki opartej na wielu wejściach albo nie masz głównej wartości do przekształcenia, lepiej pasować będzie własna funkcja. Jeśli potrzebujesz generować złożony markup albo sterować przebiegiem szablonu, rozważ własny tag.
Tworzenie i rejestrowanie filtrów
Własne filtry można definiować i rejestrować w Latte na kilka sposobów.
Bezpośrednia rejestracja przez addFilter()
Najprostszy sposób dodania filtra to metoda addFilter() wywołana bezpośrednio na obiekcie
Latte\Engine. Podajesz nazwę filtra (taką, jaka będzie używana w szablonie) i odpowiadający jej
callable PHP.
$latte = new Latte\Engine;
// prosty filtr bez argumentów
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');
// filtr z opcjonalnym argumentem
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
return mb_substr($s, 0, $len);
});
// filtr przetwarzający tablicę
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));
Użycie w szablonie:
{$name|initial} {* wypisze 'J.', jeśli $name to 'John' *}
{$description|shortify} {* użyje domyślnej długości 10 *}
{$description|shortify:50} {* użyje długości 50 *}
{$prices|sum} {* wypisze sumę elementów tablicy $prices *}
Przekazywanie argumentów:
Wartość po lewej stronie potoku (|) jest zawsze przekazywana jako pierwszy argument funkcji filtra.
Wszelkie parametry podane w szablonie po dwukropku (:) są przekazywane jako kolejne argumenty.
{$text|shortify:30}
// wywoła funkcję PHP shortify($text, 30)
Rejestracja przez rozszerzenie
Dla lepszej organizacji, zwłaszcza gdy tworzysz zestawy filtrów wielokrotnego użytku albo udostępniasz je jako pakiety, zalecanym sposobem jest zarejestrowanie ich w rozszerzeniu 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);
}
}
// rejestracja
$latte = new Latte\Engine;
$latte->addExtension(new MyLatteExtension);
Takie podejście utrzymuje logikę filtrów w jednym miejscu i upraszcza rejestrację.
Filtry używające klasy z atrybutami
Innym eleganckim sposobem definiowania filtrów jest użycie metod w klasie
parametrów szablonu. Wystarczy dodać do metody atrybut #[Latte\Attributes\TemplateFilter].
use Latte\Attributes\TemplateFilter;
class TemplateParameters
{
public function __construct(
public string $description,
// inne parametry...
) {}
#[TemplateFilter]
public function shortify(string $s, int $len = 10): string
{
return mb_substr($s, 0, $len);
}
}
// przekazujemy obiekt do szablonu
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);
Latte automatycznie wykryje i zarejestruje metody oznaczone tym atrybutem, gdy obiekt TemplateParameters zostanie
przekazany do szablonu. Nazwa filtra w szablonie będzie taka sama jak nazwa metody (w tym przypadku shortify).
{* użycie filtra zdefiniowanego w klasie parametrów *}
{$description|shortify:50}
Filtry kontekstowe
Czasem filtr potrzebuje więcej informacji niż tylko wartość wejściowa. Może potrzebować wiedzieć, jakiego typu treści jest przetwarzany łańcuch (np. HTML, JavaScript, zwykły tekst), a nawet ten typ zmienić. Do tego służą filtry kontekstowe.
Filtr kontekstowy definiuje się tak samo jak zwykły, ale jego pierwszy parametr musi mieć typ
Latte\Runtime\FilterInfo. Latte automatycznie rozpoznaje taką sygnaturę i przy wywołaniu filtra przekazuje obiekt
FilterInfo. Kolejne parametry otrzymują argumenty filtra jak zwykle.
use Latte\Runtime\FilterInfo;
use Latte\ContentType;
$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
// 1. sprawdzamy typ treści wejściowej (opcjonalne, ale zalecane)
// dopuszczamy null (wejście zmienne) albo zwykły tekst; odrzucamy przy HTML itd.
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. wykonujemy przekształcenie
$formatted = number_format($amount, 2, '.', ',') . ' EUR';
$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // zadbaj o poprawne escapowanie!
// 3. deklarujemy typ treści wyjściowej
$info->contentType = ContentType::Html;
// 4. zwracamy wynik
return $htmlOutput;
});
$info->contentType to stała łańcuchowa z Latte\ContentType (np. ContentType::Html,
ContentType::Text, ContentType::JavaScript itd.) albo null, jeśli filtr został
zastosowany do zmiennej ({$var|filter}). Możesz ją odczytać, aby sprawdzić kontekst wejściowy,
i zapisać, aby zadeklarować typ kontekstu wyjściowego.
Ustawiając typ treści na HTML, mówisz Latte, że łańcuch zwrócony przez Twój filtr jest bezpiecznym HTML. Latte nie zastosuje wtedy do tego wyniku swojego domyślnego automatycznego escapowania. Ma to kluczowe znaczenie, jeśli Twój filtr generuje markup HTML.
Jeśli Twój filtr generuje HTML, odpowiadasz za poprawne escapowanie wszystkich danych wejściowych
użytych w tym HTML (jak w powyższym wywołaniu htmlspecialchars($formatted)). Zaniedbanie tego może stworzyć
podatności XSS. Jeśli Twój filtr zwraca tylko zwykły tekst, nie musisz ustawiać $info->contentType.
Filtry na blokach
Filtry stosowane do bloków o typie treści innym niż tekst (zwykle HTML) muszą być kontekstowe. Wynika to z tego, że treść bloku ma zdefiniowany typ, którego filtr musi być świadomy. Klasyczny, niekontekstowy filtr można zastosować tylko do bloku, którego treść jest zwykłym tekstem.
{block heading|money}1000{/block}
{* filtr 'money' otrzyma '1000' jako drugi argument,
a $info->contentType będzie ContentType::Html *}
Filtry kontekstowe dają potężną kontrolę nad tym, jak dane są przetwarzane w zależności od kontekstu, umożliwiają zaawansowane funkcje i zapewniają poprawne escapowanie, zwłaszcza przy generowaniu treści HTML.