Nette Documentation Preview

syntax
Tworzenie własnych filtrów
*************************

.[perex]
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:
```latte
{* 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 |#Filtry 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|filters]. 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|custom functions]. Jeśli potrzebujesz generować złożony markup albo sterować przebiegiem szablonu, rozważ [własny tag|custom tags].


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.

```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:**

```latte
{$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.

```latte
{$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 |extending-latte#Latte Extension]:

```php
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 .{toc: Filtry używające klasy}
-------------------------------------------------------------------

Innym eleganckim sposobem definiowania filtrów jest użycie metod w [klasie parametrów szablonu |develop#Parametry jako klasa]. Wystarczy dodać do metody atrybut `#[Latte\Attributes\TemplateFilter]`.

```php
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`).

```latte
{* 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.

```php
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.

.[warning]
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 |tags#{block}] 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.

```latte
{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.

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.