Komponenty#
Znovupoužitelný kus stránky s vlastní logikou, šablonou a případně vlastními
signály. Základ: app/Core/Base/BaseComponent.php.
Kdy komponentu vůbec dělat#
| Situace | Řešení |
|---|---|
| Kus markupu se opakuje, ale nemá logiku | partial v šabloně (_neco.latte) |
| Opakuje se i s logikou (načítá data, reaguje na kliknutí) | komponenta |
| Potřebuji překreslit jen část stránky přes AJAX | komponenta se {snippet} |
| Jde o jednu stránku a nikde jinde | nechte to v presenteru |
Komponenta není složka navíc — je to hranice
Co je uvnitř, může mít vlastní stav a signály a dá se to překreslit samo o sobě. Když to nepotřebujete, partial stačí a je levnější na pochopení.
Struktura složky#
Components/AdvertListComponent/
AdvertList.php komponenta
AdvertListFactory.php rozhraní továrny
Traits/ rozklad logiky (načítání, filtry, akce)
Templates/
Default/
Style_1.latte výchozí podoba
Style_2.latte jiná podoba téhož
Elements/
Actions.latte kus sdílený mezi styly
Části uvnitř Elements/, ne trojí kopie
Co se opakuje ve všech stylech (cena, tlačítka, odznak uživatele), patří do
Elements/. Trojí kopie se pozná tak, že se změna udělá na dvou místech ze tří
— a vypadá to správně, dokud někdo nepřepne styl.
Jak vzniká#
Komponenta se nevytváří přímo. Napíše se rozhraní továrny, kontejner implementaci vygeneruje a presenter si o ni řekne:
// AdvertListFactory.php
interface AdvertListFactory
{
public function create(): AdvertList;
}
// v presenteru
public function __construct(private readonly AdvertListFactory $advertListFactory) {}
protected function createComponentAdvertList(): AdvertList
{
return $this->advertListFactory->create();
}
V šabloně pak {control advertList}, případně {control advertList, $argument}.
Dva traity se stejně pojmenovanou injektovanou vlastností se NESLOUČÍ
Projeví se to chybou při sestavení kontejneru, ne při psaní kódu — takže to vypadá jako chyba konfigurace, i když je to kolize jmen v traitech.
Jak si komponenta hledá šablonu#
Ve třech krocích, první existující vyhrává:
1. Templates/<Šablona>/<Varianta>/Style_1.latte (přesně tahle šablona a zařízení)
2. Templates/<Šablona>/default/Style_1.latte (tahle šablona, všechna zařízení)
3. Templates/Default/Style_1.latte (společná výchozí podoba)
Podoba se volí argumentem při vykreslení; renderStyle2() se automaticky přeloží
na Style_2.latte, takže se kvůli tomu nemusí psát metoda.
Nová šablona nemusí kopírovat komponenty
Přebere Templates/Default/ a přepíše jen to, co chce jinak.
V šabloně komponenty NENÍ překladač s doménou
Na rozdíl od presenteru. Texty se překládají v PHP a do šablony jdou hotové.
Cesty v {include} jsou relativní k souboru
Přesun šablony rozbije všechny její vnořené includy.
Sdílené modální okno#
Modál se v Latte nekreslí. V layoutu je jednou namountovaný sdílený shell a komponenty do něj jen vloží obsah a otevřou ho.
$this->getModalWindow() // z ModalAwareTrait
->setTitle('Odpověď na inzerát')
->setSize('modal-lg')
->setContent($html) // hotové HTML, typicky vyrenderovaný partial
->open();
| Vlastnost | Jak to je |
|---|---|
| Kde je shell | jednou v admin @layout.latte jako {control modalWindow} |
| Kdo ho plní | kterýkoli presenter nebo komponenta pod ním |
| Stav | žádný — titulek ani obsah se neukládají do session, žijí jen v tomhle requestu |
| Otevření přes AJAX | překreslí se jen {snippet modalBody} a do odpovědi se zapíše modalShow s id; zobrazení dělá modals.js |
| Víc oken naráz | nepodporuje se — je to jeden sdílený shell |
Proto se do modálu dá vložit i formulář z jiné komponenty
Obsah je obyčejný řetězec HTML. Komponenta si vyrenderuje svůj partial do řetězce a pošle ho do modálu; modál o ní nic neví a nemusí.
Komponenta musí běžet pod presenterem, který modál poskytuje
getModalWindow() to kontroluje a jinak vyhodí výjimku se jménem komponenty.
Není to obtěžování — bez shellu v layoutu by se okno nemělo kde zobrazit.
Povinné pole ve skrytém modálu zablokuje odeslání celého formuláře
Prohlížeč odmítne odeslat formulář kvůli poli, které uživatel nevidí, a neřekne proč — stránka jen přestane reagovat na odeslání. Povinnost se proto musí přepínat podle toho, jestli je modál otevřený.
Signály#
Metoda handle*() obsluhuje akci komponenty (smazat, přepnout, načíst další).
Volá se běžnou adresou, kterou si komponenta vygeneruje.
Signál je veřejná adresa — musí si ověřit původ požadavku
Bez kontroly ho jde vyvolat z cizí stránky jménem přihlášeného uživatele.
Kontrola stejného původu je u handle*() povinná, ne volitelná.
AJAX překreslí jen VNITŘEK snippetu
Atributy elementu, na kterém snippet visí (třídy, data-), zůstanou původní.
Co se má měnit, musí být uvnitř.
Nová komponenta krok za krokem#
- Založit složku podle vzoru výš — komponenta, rozhraní továrny,
Templates/Default/. - Logiku rozdělit do traitů, když přeroste jeden soubor (načítání zvlášť od akcí).
- Zaregistrovat továrnu v konfiguraci a vytvořit
createComponentXxx()v presenteru. - Šablonu psát do
Templates/Default/— ať ji převezme každá šablona. - Texty překládat v PHP, do šablony posílat hotové.
- Prokliknout — včetně AJAX cesty, pokud komponenta překresluje.
Oprava sdíleného základu je změna ve všech modulech, které ho používají
Komponenta se společným předkem pro tři moduly znamená, že oprava kvůli jednomu zasáhne všechny tři. Projděte je všechny — ne jen ten, kvůli kterému opravujete.
Hotové komponenty k okoukání#
| Komponenta | Kde | Čím je zajímavá |
|---|---|---|
| DataGrid | app/UI/Admin/System/Components/ |
tabulka s filtry, řazením a hromadnými akcemi |
| Picker | tamtéž | výběr entity místo <select> |
| Modální okno | tamtéž | sdílený shell popsaný výš |
| Výpis inzerátů | app/UI/Front/Bazaar/Components/AdvertRendererComponent/ |
tři podoby + Elements/ |
Vizuální podobu komponent frontu ukazuje katalog komponent.