Přeskočit obsah
V
Pro vývojáře
Architektura, konvence, jádro systému a bezpečnost
UI vrstva / Šablony Latte

Š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#

  1. Zjisti, jestli patří do existujícího rozšíření. Formátování ceny do PriceLatteExtension, práce s textem do StringLatteExtension.
  2. Když ne, založ nové rozšíření v app/Core/Latte/Extensions/, extends Latte\Extension, a implementuj getFilters() / getFunctions() / getTests().
  3. Do konfigurace nic nepiš. Rozšíření se samo najde: LatteExtensionsCompiler prochází robotloaderem složky Extensions/ a Macros/, zaregistruje každou instanciovatelnou třídu dědící z Latte\Extension a navěsí ji na LatteFactory.
  4. Logiku dej do helperu, ne do těla uzávěry — filtr má být jednořádkové přesměrování.
  5. 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.

{snippetArea linksSnippetArea}
    {snippet linksSnippet}
        …
    {/snippet}
{/snippetArea}
$this->redrawControl('linksSnippet');

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

Nastavení cookies