Vytváření vlastních filtrů

Filtry jsou výkonné nástroje pro formátování a úpravu dat přímo v šablonách Latte. Nabízejí čistou syntaxi pomocí symbolu roury (|) pro transformaci proměnných nebo výsledků výrazů do požadovaného výstupního formátu.

Co jsou filtry?

Filtry v Latte jsou v podstatě PHP funkce navržené speciálně pro transformaci vstupní hodnoty na výstupní hodnotu. Aplikují se pomocí zápisu s rourou (|) uvnitř výrazů šablony ({...}).

Pohodlnost: Filtry vám umožňují zapouzdřit běžné úlohy formátování (jako formátování datumů, změna velikosti písmen, zkracování) nebo manipulace s daty do znovupoužitelných jednotek. Místo opakování složitého PHP kódu ve vašich šablonách můžete jednoduše aplikovat filtr:

{* Místo složitého PHP pro zkrácení: *}
{$article->text|truncate:100}

{* Místo kódu pro formátování datumu: *}
{$event->startTime|date:'Y-m-d H:i'}

{* Aplikace více transformací: *}
{$product->name|lower|capitalize}

Čitelnost: Používání filtrů činí šablony přehlednějšími a více zaměřenými na prezentaci, protože transformační logika je přesunuta do definice filtru.

Kontextová citlivost: Klíčovou předností filtrů v Latte je jejich schopnost být kontextově citlivé. To znamená, že filtr může rozpoznat typ obsahu, se kterým pracuje (HTML, JavaScript, prostý text atd.), a aplikovat odpovídající logiku nebo escapování, což je zásadní pro bezpečnost a správnost, zejména při generování HTML.

Integrace s aplikační logikou: Stejně jako vlastní funkce může být PHP callable za filtrem uzávěr (closure), statická metoda nebo metoda instance. To umožňuje filtrům přistupovat k aplikačním službám nebo datům, pokud je to potřeba, i když jejich hlavním účelem zůstává transformace vstupní hodnoty.

Latte ve výchozím nastavení poskytuje bohatou sadu standardních filtrů. Vlastní filtry vám umožňují rozšířit tuto sadu o formátování a transformace specifické pro váš projekt.

Pokud potřebujete provádět logiku založenou na více vstupech nebo nemáte primární hodnotu k transformaci, je pravděpodobně vhodnější použít vlastní funkci. Pokud potřebujete generovat složitý markup nebo řídit tok šablony, zvažte vlastní tag.

Vytváření a registrace filtrů

Existuje několik způsobů, jak definovat a registrovat vlastní filtry v Latte.

Přímá registrace pomocí addFilter()

Nejjednodušší způsob, jak přidat filtr, je použití metody addFilter() přímo na objektu Latte\Engine. Zadáte název filtru (jak bude použit v šabloně) a odpovídající PHP callable.

$latte = new Latte\Engine;

// Jednoduchý filtr bez argumentů
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');

// Filtr s volitelným argumentem
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
	return mb_substr($s, 0, $len);
});

// Filtr zpracovávající pole
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));

Použití v šabloně:

{$name|initial}                 {* Vypíše 'J.' pokud je $name 'John' *}
{$description|shortify}         {* Použije výchozí délku 10 *}
{$description|shortify:50}      {* Použije délku 50 *}
{$prices|sum}                   {* Vypíše součet položek v poli $prices *}

Předávání argumentů:

Hodnota nalevo od roury (|) je vždy předána jako první argument funkci filtru. Jakékoliv parametry uvedené za dvojtečkou (:) v šabloně jsou předány jako následující argumenty.

{$text|shortify:30}
// Volá PHP funkci shortify($text, 30)

Registrace pomocí rozšíření

Pro lepší organizaci, zejména při vytváření znovupoužitelných sad filtrů nebo jejich sdílení jako balíčky, je doporučeným způsobem registrovat je v rámci rozšíření Latte:

namespace App\Latte;

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);
	}
}

// Registrace
$latte = new Latte\Engine;
$latte->addExtension(new App\Latte\MyLatteExtension);

Tento přístup udrží logiku vašeho filtru zapouzdřenou a registraci jednoduchou.

Použití načítače filtrů

Latte umožňuje registrovat načítač filtrů pomocí addFilterLoader(). Jde o jediné volatelné callable, které Latte požádá o jakýkoliv neznámý název filtru během kompilace. Načítač vrací PHP callable filtru nebo null.

$latte = new Latte\Engine;

// Načítač může dynamicky vytvářet/získávat callable filtry
$latte->addFilterLoader(function (string $name): ?callable {
	if ($name === 'myLazyFilter') {
		// Představte si zde náročnou inicializaci...
		$service = get_some_expensive_service();
		return fn($value) => $service->process($value);
	}
	return null;
});

Tato metoda byla primárně určena pro líné načítání filtrů s velmi náročnou inicializací. Avšak moderní praktiky vkládání závislostí (dependency injection) obvykle zvládají líné služby efektivněji.

Načítače filtrů přidávají složitost a obecně se nedoporučují ve prospěch přímé registrace pomocí addFilter() nebo v rámci rozšíření pomocí getFilters(). Používejte načítače pouze pokud máte závažný, specifický důvod související s výkonnostními problémy při inicializaci filtrů, které nelze řešit jinak.

Filtry používající třídu s atributy

Další elegantní způsob, jak definovat filtry, je použití metod ve vaší třídě parametrů šablony. Stačí přidat atribut #[Latte\Attributes\TemplateFilter] k metodě.

use Latte\Attributes\TemplateFilter;

class TemplateParameters
{
	public function __construct(
		public string $description,
		// další parametry...
	) {}

	#[TemplateFilter]
	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len);
	}
}

// Předání objektu do šablony
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);

Latte automaticky rozpozná a zaregistruje metody označené tímto atributem, když je objekt TemplateParameters předán do šablony. Název filtru v šabloně bude stejný jako název metody (shortify v tomto případě).

{* Použití filtru definovaného ve třídě parametrů *}
{$description|shortify:50}

Kontextové filtry

Někdy filtr potřebuje více informací než jen vstupní hodnotu. Může potřebovat znát typ obsahu řetězce, se kterým pracuje (např. HTML, JavaScript, prostý text) nebo ho dokonce upravit. To je situace pro kontextové filtry.

Kontextový filtr je definován stejně jako běžný filtr, ale jeho první parametr musí být typově označen jako Latte\Runtime\FilterInfo. Latte automaticky rozpozná tento podpis a při volání filtru předá objekt FilterInfo. Následující parametry obdrží argumenty filtru jako obvykle.

use Latte\Runtime\FilterInfo;
use Latte\ContentType;

$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
	// 1. Zkontrolujte vstupní typ obsahu (volitelné, ale doporučené)
	//    Povolte null (proměnný vstup) nebo prostý text. Odmítněte pokud je aplikován na HTML atd.
	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. Proveďte transformaci
	$formatted = number_format($amount, 2, '.', ',') . ' EUR';
	$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // Zajistěte správné escapování!

	// 3. Deklarujte výstupní typ obsahu
	$info->contentType = ContentType::Html;

	// 4. Vraťte výsledek
	return $htmlOutput;
});

$info->contentType je řetězcová konstanta z Latte\ContentType (např. ContentType::Html, ContentType::Text, ContentType::JavaScript, atd.) nebo null, pokud je filtr aplikován na proměnnou ({$var|filter}). Můžete tuto hodnotu číst, abyste zkontrolovali vstupní kontext, a zapisovat do ní, abyste deklarovali typ výstupního kontextu.

Nastavením typu obsahu na HTML sdělujete Latte, že řetězec vrácený vaším filtrem je bezpečné HTML. Latte pak na tento výsledek nebude aplikovat své výchozí automatické escapování. To je zásadní, pokud váš filtr generuje HTML markup.

Pokud váš filtr generuje HTML, jste zodpovědní za správné escapování jakýchkoliv vstupních dat použitých v tomto HTML (jako v případě volání htmlspecialchars($formatted) výše). Opomenutí může vytvořit XSS zranitelnosti. Pokud váš filtr vrací pouze prostý text, nemusíte nastavovat $info->contentType.

Filtry na blocích

Všechny filtry aplikované na bloky musí být kontextové. Je to proto, že obsah bloku má definovaný typ obsahu (obvykle HTML), kterého si filtr musí být vědom.

{block heading|money}1000{/block}
{* Filtr 'money' obdrží '1000' jako druhý argument
   a $info->contentType bude ContentType::Html *}

Kontextové filtry poskytují silnou kontrolu nad tím, jak jsou data zpracovávána na základě jejich kontextu, umožňují pokročilé funkce a zajišťují správné chování escapování, zejména při generování HTML obsahu.

verze: 3.0