Šablony Latte#
Tahle kapitola je o PHP straně šablon: co si systém do Latte přidal, jak napsat vlastní filtr a jak spolu drží snippety a signály.
Kam patří co
Jak se šablona dělí na soubory, jak si systém vybere layout a co do šablony přiteče z presenteru — to je v sekci pro kodéry: Dělení šablony na Latte soubory. Tady je jen to, co potřebuje programátor, který do šablon něco přidává z PHP.
Vlastní rozšíření#
app/Core/Latte/Extensions/ — třináct tříd, každá přidává filtry, funkce nebo
testy:
| Rozšíření | Co přidává |
|---|---|
Url |
práce s adresami |
Date |
formátování data a času |
Price |
formátování ceny podle měny (priceFormat), sazby DPH (vatRate) a množství (quantity) |
Email, Phone |
formátování kontaktů |
String |
textové úpravy (např. nahrazení zakázaných slov) |
FormPair |
vykreslení dvojice popisek–pole |
SafeLink |
funkce safeLink() — odkaz, který nespadne na chybějícím cíli |
Theme |
themeAsset(), themeUrl(), test isTheme() |
ApproxPrice |
filtr approxPrice (přibližný přepočet do měny webu), funkce currencyById() |
CanonicalUrl |
filtr canonical — adresa pro <link rel="canonical"> |
Outline |
filtr outline — osnova textové stránky a kotvy nadpisů |
UiTexts |
funkce uiTexts() — podstrom překladů pro JavaScript |
K tomu tři makra v app/Core/Latte/Macros/: MultiControlMacros, SafeLinkMacro
a ThemeMacro.
Postup: přidávám vlastní filtr#
- Zjisti, jestli patří do existujícího rozšíření. Formátování ceny do
PriceLatteExtension, práce s textem doStringLatteExtension. - Když ne, založ nové rozšíření v
app/Core/Latte/Extensions/,extends Latte\Extension, a implementujgetFilters()/getFunctions()/getTests(). - Do konfigurace nic nepiš. Rozšíření se samo najde:
LatteExtensionsCompilerprochází robotloaderem složkyExtensions/aMacros/, zaregistruje každou instanciovatelnou třídu dědící zLatte\Extensiona navěsí ji naLatteFactory. - Logiku dej do helperu, ne do těla uzávěry — filtr má být jednořádkové přesměrování.
- Ověř v obou sekcích, pokud má filtr fungovat na frontu i v administraci.
Formátování patří do filtru, ne do šablony
Podmínka na měnu uprostřed šablony se za rok objeví na patnácti místech — a na čtrnácti z nich se zapomene opravit.
Nové rozšíření se projeví až po smazání cache kontejneru
Registrace běží při sestavení DI kontejneru. Dokud se nesmaže temp/cache, filtr
v šabloně neexistuje a Latte hlásí neznámý filtr — což vypadá jako překlep
v názvu.
Filtry a funkce, které mají háček#
U většiny filtrů stačí název. U těchhle šesti se dá udělat chyba, která nespadne, jen tiše udělá něco jiného.
| Filtr / funkce | Co dělá | Na co si dát pozor |
|---|---|---|
canonical (filtr) |
z adresy vyhodí parametry, které nemění obsah (_theme, _skin, view, order, count, spec, lspec, tag, categoryId, do, _fid a každý parametr komponenty s pomlčkou), relativní adresu doplní o schéma a host |
page zůstává, druhá stránka je jiný obsah; parametry v cestě (routa) se nemění |
outline (filtr) |
vrátí ['html' => …, 'items' => […]]; nadpisům (výchozí h2, jiný druhým argumentem) doplní id |
obsah vypiš z $outline['html'], kotvy jsou jen v té kopii; nadpis bez textu se přeskočí, id od redaktora se nechá, shodné kotvy dostanou -2, -3 (jinak by druhý odkaz osnovy skočil na první oddíl) |
uiTexts() (funkce) |
ploché pole relativní.klíč => text pro JS helper i18n.js (t(key, params)) |
výčet klíčů bere z výchozí locale (cs), překlad z aktuální; %name% dosazuje až JS |
approxPrice (filtr) |
„≈ €4,100“ v měně webu, nebo prázdný řetězec, když se měny shodují nebo přepočet nejde | originál dál vypisuje priceFormat; s druhým argumentem true přidá závorku a nezlomitelnou mezeru, piš ho proto hned za originál bez mezery |
quantity (filtr) |
množství bez koncových nul: „5“ z '5.000', „1,5“ z '1.500', oddělovač podle lokalizace (dokladu, jinak požadavku) |
do value a data atributů piš \|quantity:true (tečka); bez něj by „1,5“ v <input type=number> neprošlo. Argument je přepínač strojového tvaru, ne oddělovač: ten se bere vždy z lokalizace |
safeLink() (funkce) |
odkaz, který nikdy nepoloží render; nesestavitelný cíl dá '#', selhané sestavení zapíše jeden řádek do log/warning.log za cíl a požadavek |
'#' v odkazu je příznak vadných dat (typicky vazba stránky na routu), ne chyba šablony |
<link rel="canonical" href="{$canonicalUrl|canonical}">
{var $outline = ($contentHtml|outline)}
<script type="application/json" id="front-ui-i18n">{uiTexts('front.general.ui')}</script>
{$price|priceFormat:$currency}{$price|approxPrice:$currency, true}
{$item['quantity']|quantity} {$item['unit']}
{var $link = safeLink(':Front:System:Page:show', ['slug' => $slug])}
Bez |canonical je každá kombinace filtrů sama sobě kanonická
Canonical se skládá z link('this', getParameters()), tedy z celého požadavku. Bez
filtru vyhledávač vidí každou kombinaci přepínače šablon, zobrazení, řazení a filtrů
jako samostatnou stránku a hlásí je jako duplicity.
Proč safeLink() přepíná invalidLinkMode
Presenter::link() vadný odkaz nevyhodí jako výjimku: v ladicím režimu z něj udělá
E_USER_WARNING. config/Shared/tracy.neon má strictMode zapnutý, takže Tracy
z warningu udělá bluescreen a ukončí render uprostřed stránky (catch warning
nezachytí). Jediná vadná vazba stránky na routu tak na dev serveru položí každou
stránku, zatímco produkce tiše vrací '#'. safeLink() proto po dobu volání vynutí
Presenter::InvalidLinkException a ve finally vrátí původní režim. Vlastní helper
pro odkazy, které nesmí shodit stránku, stav vrať stejně.
Text pro JS nepřidávej do data-* atributů
Frontové šablony mají každá vlastní kopii layoutu. Nový text pro JS komponentu proto
patří do podstromu front.general.ui v .neon; uiTexts() ho vydá všem šablonám
najednou. Klíč chybějící v en nebo sk se přeloží fallbackem a ukáže se v Tracy panelu
jako chybějící.
Snippety a jejich PHP protějšek#
Jméno snippetu je zároveň argument redrawControl(). Konvence:
krátké lowerCamelCase jméno, Wrapper zkrácené na Wrap, obalová oblast se jmenuje
jako vnitřní snippet plus Area.
Název snippetu musí být LITERÁL
Jméno složené z proměnné se nezaregistruje a redrawControl() pak tiše nic
neudělá. Nevypíše se chyba — stránka se jen nepřekreslí.
AJAX překreslí jen VNITŘEK snippetu
Atributy elementu, který snippet nese, zůstanou staré — třída i data- atribut.
Když má překreslení změnit stav vyjádřený třídou, musí být ta třída uvnitř.
{var} uvnitř {snippet} má vlastní rozsah
Proměnná definovaná vně snippetu v něm po překreslení nemusí existovat — při AJAX překreslení se totiž vykresluje jen ten snippet, ne celá šablona kolem.
Překreslovat naslepo se nemá
redrawControl() bez argumentu překreslí všechno. Cíl vždycky pojmenuj — jinak se
do odpovědi dostanou i snippety, které se mezitím změnily z jiného důvodu.
Podrobně Komponenty a Konvence kódu.
Překlady v šabloně#
Tři různá chování podle toho, kde jste — Front presenter potřebuje
{translator $translatorDomain}, Admin presenter a komponenty ne. Je to nejčastější
zdroj „proč se to nepřeložilo“ a má vlastní kapitolu: Překlady.
Chybějící překlad v routě může shodit celou jazykovou verzi
Ne prázdné místo — chybu stránky. Týká se to textů, ze kterých se skládá adresa, ne popisků. Podrobně Překlady, oddíl o databázové vrstvě.
Vyhrazené názvy proměnných#
Některá jména jsou obsazená šablonovým systémem
$form, $flashes, $control, $presenter, $lang, $locale, $theme
a další. Přepsání se neprojeví chybou, ale podivným chováním — typicky tím, že
přestane fungovat něco úplně jiného na téže stránce.
Ladicí režim a AJAX#
Zastaralé volání v ladicím režimu rozbije AJAX odpověď
Tracy přidá do odpovědi výpis, který rozbije JSON. Projeví se to jako „AJAX nefunguje" — bez varování a bez chyby v konzoli, protože odpověď se prostě nedá naparsovat.
Kam sáhnout#
| Chci | Kde |
|---|---|
| rozšíření a jejich filtry | app/Core/Latte/Extensions/ |
| makra | app/Core/Latte/Macros/ |
| filtr canonical a seznam zahazovaných parametrů | app/Core/Latte/Extensions/CanonicalUrlLatteExtension.php (DROP) |
| osnovu textové stránky | app/Core/Utils/Helpers/HtmlOutline.php |
| automatickou registraci | app/Core/DI/LatteExtensionsCompiler.php |
| nastavení engine | config/Shared/latte.neon |
| strukturu šablon a layoutů | Dělení šablony na Latte soubory |
Navazující kapitoly: Komponenty · Formuláře · Překlady · Témata frontendu