Někdy je vhodné si v Nette nadefinovat vlastní filtr, když nám výchozí filtry nestačí, i když se jedná o širokou nabídku rozmanitých funkcí, které v základu skutečně stačí. Dnes bych chtěl ukázat, jak si takový vlastní filtr, který zpravidla slouží k úpravě vstupu do nějaké cílové podoby, napsat.
Filtry se registrují do šablony a vždy lze registrovat anonymní funkci
nebo callback. Níže následuje ukázka registrace jednoduchého filtru, který
vrátí absolutní hodnotu čísla. Filtr je zpravidla vhodné registrovat
v metodě beforeRender
v BasePresenteru
nebo
v nějaké továrničce šablony.
...
protected function beforeRender()
{
$this->template->addFilter('abs', function ($number) {
return abs($number);
});
}
...
Filtr pak použijeme tak, jak jsme zvyklí:
{$number|abs}
Myslím, že použití je dost přímočaré a můžeme pokročit.
Na základě komentáře z diskuse od Aurielle (díky) si dovolím ještě doplnit jeho ukázku registrace filtru přímo v configu, která je také dost šikovná:
services:
nette.latteFactory:
setup:
- addFilter(abs, @petrjirasek\Latte\AbsFilter)
- petrjirasek\Latte\AbsFilter
Oddělený filtr ve vlastní třídě
Osobně mám rád ve věcech pořádek, proto není od věci si filtry psát odděleně a moci je pak snadněji přenášet. Jednoduchý filtr absolutní hodnoty by tedy mohl odděleně vypadat takto:
<?php
namespace petrjirasek\Latte;
use Nette\Object;
class AbsFilter extends Object
{
/**
* @param int $number
* @return int
*/
public function __invoke($number)
{
return abs($number);
}
}
Můžeme si všimnout, že byla použita magická metoda
__invoke
, která nám umožní volat objekt jako funkci. Při
registraci pak stačí inicializovat třídu filtru.
use petrjirasek\Latte\AbsFilter;
...
protected function beforeRender()
{
$this->template->addFilter('abs', new AbsFilter);
}
...
Filtr se závislostmi
Někdy může filtr disponovat závislostmi a vyžadovat některé služby
v jeho konstruktoru. V praxi se může jednat o situaci, kdy nějaký filtr
potřebuje svůj výstup cachovat nebo používá službu, která daný vstup
převádí na požadovány výstup. Jelikož se asi nikomu nechce předávat
závislosti ručně při inicializaci, je vhodné použít automatické
dosazování závislosti v kombinaci s anotací @inject
.
Řešení pak bude vypadat takto:
use petrjirasek\Latte\AbsFilter;
...
/** @var AbsFilter @inject */
public $absFilter;
...
protected function beforeRender()
{
$this->template->addFilter('abs', $this->absFilter);
}
...
Také je nezbytné, aby filtr byl zaregistrován jako služba
v config.neon
.
services:
- petrjirasek\Latte\AbsFilter
Při tomto řešení jsou závislosti automaticky při inicializaci předány
a filtr je v pořádku zaregistrován. Jen doplním, že AbsFilter
aktuálně žádnou závislost nemá, ale kdyby měl nebo kdyby jsme měli jiný
filtr se závislostmi, tak by byly skutečně předány.
Tipy na filtry
Pokud vás stále nenapadá, kdy by jste mohli vlastní Latte filtr uplatnit, tak zkusím naznačit pár případu užití.
Jedním z typických příkladů je třeba konverze data do tvaru vydáno
před X hodinami, před 5 dny atd. Můžete si napsat tak
vlastní filtr, jehož vstupem bude instance DateTime
označující
například datum vydání článku a filtr při aplikací vrátí relativní
vyjádření času k aktuálnímu okamžiku.
Dalším příkladem může být použití Texy. Buď dost nehezky předáte do šablony instanci Texy, zpracujete text a ještě to obalíte makrem pro cachování nebo můžete hezky vše zapouzdřit do Latte filtru, kde jednou ze závislostí bude jak Texy, tak nějaká cache, kam budete výstup pravděpodobně ukládat.
A do třetice mě napadá případ, kdy máte vícejazyčný web a datumy v nějakém vámi definovaném výchozím českém formátu. Pro každý jazyk ale může být výchozí podoba formátu různá a tak se nabízí situace, kdy filtru předáte informaci o jazyku a filtr pak bude aplikovat formát data dle daného jazyka.
A jak pracujete s filtry vy? Co se vám osvědčilo?