Latte: jak zapisovat lokální/privátní bloky
- David Grudl
- Nette Core | 8227
V Latte jsou bloky sdíleny mezi více šablonami, pokud jsou součástí
hierarchie dědičnosti (tedy např. layout + stránka) nebo pokud je
importujeme pomocí {import}
(dříve {includeblock}
).
Někdy však v takových šablonách potřebujeme vytvořit blok, který by
neměl být k dispozici v jiných šablonách, ani by neměl přepisovat
další bloky, pokud se jejich náhodou názvy shodují.
V podstatě jde o private blok.
Konkrétní příklad, kterým vypisuji TOC v dokumentaci:
<ul>
<li><a href="#">{$page->title}</a></li>
{include toc, $page->toc}
</ul>
{define toc, $items}
{foreach $items as $item}
<li><a href="#{$item->id}">{$item->title}</a>
{if $item->children}
<ul>{include this $item->children}</ul>
{/if}
{/foreach}
{/define}
Kdyby náhodou v layoutu nebo jiné šabloně vznikl blok taktéž
pojmenovaný toc
, existence toho pomocného bloku ve stránce by ho
přepsala, což nechci. Tím že ho označím za privátní, bude existovat jen
pro šablonu ve které je definovaný a nebude nijak interagovat s jinými
bloky v jiných šablonách.
Takhle feature je už součástí Latte 2.9, co zbývá vymyslet je syntax zápisu.
Zatím používám tohle
- místo privátní říkám lokální, sedne mi to víc k šablonám
- zapisuje se pomocí prefixu
local:
...
{include local:toc, $page->toc}
</ul>
{define local:toc, $items}
...
Ale nejsem si jistý, jestli je to úplně ono a srozumitelné atd. Trošku mi i vadí, že je to zaměnitelné s pojmenovanými argumenty v PHP 8.
Nějaké nápady?
- medhi
- Generous Backer | 255
Neměla by ta lokálnost být spíš součástí maker define a block? Přijde mi to logické a je to i analogické k definici proměnných a funkcí v PHP, tedy public $foo, private $foo.
Kdybych měl hádat, jak asi bude makro – které bude dělat totéž, ale
lokálně – vypadat, hádal bych něco jako {definelocal toc}
resp. {blocklocal toc}
. Případně {localdefine toc}
a {localblock toc}
Pořád to není ono, ale pro mě lepší než přidávat další druh syntaxe, který vnáší zase něco nového do pravidel a tedy je komplikuje.
- Tomáš Jacík
- Člen | 147
Vynechal bych dvojtečku a jen detekoval, zda je za define
slovo
local
. Jinak se mi local
také líbí více než
private
.
A to {include this}
je pecka :)
Editoval Tomáš Jacík (11. 11. 2020 3:17)
- filsedla
- Člen | 101
- Zatím to možná není špatné, ale pokud se opravdu prosadí pojmenované parametry, mohla by se tahle syntax stát hodně matoucí.
- Nepřijde mi správné uvádět
local
u{define ...}
(pouze u{include ...}
).
Možná by bylo lepší použít dvě dvojtečky.
{include local::toc, $page->toc}
Mohlo by to být zajímavé, pokud bys implementoval i jiné „scopes“,
třeba child::someblock
nebo parent::block
. Pak by se
tohle mohlo jmenovat spíš self::toc
. Každopádně myslím, že
by nemělo být klíčové slovo určující „scope“ v definici, pouze
u použítí, kde tím zadáme „cestu“ k definovanému bloku.
Např. pomocí {include child::content}
bych vyřešil
{include subsubsubcontent}
, s čímž často bojuju.
Ale to už je jiná věc, nevím, jestli to chceš vést tímto směrem a jestli v současné implementaci si bloky pamatují „scope“/kontext/šablonu, ve které byly definovány, a může třeba existovat blok se stejným názvem v různých šablonách v hierarchii.
Edit: Vlastně, určení bloku jako „private“ přímo při defnici, je
ještě něco jiného, než o čem jsem psal. Tam by se mi zdálo nejlepší
{definelocal block}
. K takovému bloku by latte nepovolilo
přístup odjinud, aspoň při zadání pouze jména bloku.
Teď by se to možná dalo jednoduše vyřešit podtržítkem na začátku názvu bloku.
{define _toc, $items}
{include _toc, $page->toc}
Latte engine by pak pracovalo s takto nazvanými bloky tím novým
způsobem. Tahle syntax by aspoň okamžitě měla hatery, lidi, co
budou hned vědět, co to dělá, lol.
Editoval filsedla (11. 11. 2020 9:59)
- David Grudl
- Nette Core | 8227
medhi napsal(a):
Kdybych měl hádat, jak asi bude makro – které bude dělat totéž, ale lokálně – vypadat, hádal bych něco jako
{definelocal toc}
resp.{blocklocal toc}
. Případně{localdefine toc}
a{localblock toc}
Je to taky možnost. Otázkou je, jestli by pak mělo existovat i makro
{includelocal}
(což už má zavádějící význam) nebo
{include foo}
by měl přednostně includovat lokální blok?
Tomáš Jacík napsal(a):
Vynechal bych dvojtečku a jen detekoval, zda je za
define
slovolocal
. Jinak se milocal
také líbí více nežprivate
.
{define local foo}
Jo, tohle by řešilo problém se zaměnitelností za named arguments.
A to
{include this}
je pecka :)
Již od roku 2009 :-D
filsedla napsal(a):
Edit: Vlastně, určení bloku jako „private“ přímo při defnici, je ještě něco jiného, než o čem jsem psal.
Ano, tohle je o něčem jiném. Nechceš ty scopes rozepsat do samostatného vlákna? Úplně přesně nerozumím, k čemu by měly sloužit.
Teď by se to možná dalo jednoduše vyřešit podtržítkem na začátku názvu bloku.
{define _toc, $items}
Ta možnost tu skutečně je, protože podtržítko je na začátku názvu bloku zakázané, takže by nešlo o BC break. A je to taky zvyk z mnoha jazyků.
- SendiMyrkr
- Člen | 30
To podtržítko nepovažuji za dobrý nápad. Úplně vidím ty dotazy.
„Hele mám tu blok _abc a nejde mi includovat“. Nejvíc se mi asi líbí
to {define local abc, ...}
- Václav Pávek
- Backer | 100
SendiMyrkr napsal(a):
To podtržítko nepovažuji za dobrý nápad. Úplně vidím ty dotazy. „Hele mám tu blok _abc a nejde mi includovat“. Nejvíc se mi asi líbí to
{define local abc, ...}
Také se mi toto líbí nejvíce a hlavně nejde o BC a na rovinu říkám, že chci nadefinovat blok v „lokálním“ scope. Bez „local“ jej lze kdykoli přepsat.
- Kori
- Člen | 73
Z toho popisu mi prijde, ze jde o dve ruzne veci a latte by na to melo ruzne reagovat
- Někdy však v takových šablonách potřebujeme vytvořit blok, který by neměl být k dispozici v jiných šablonách
V tom pripade napr. to local a pokud pouziju v podrizene sablone stejny nazev bez definice, tak bych mel byt upozornen, ze block neexistuje
- ani by neměl přepisovat další bloky, pokud se jejich náhodou názvy shodují.
Napr. final? Pokud se budu snazit redefinovat block v podrizene sablone, melo by me to upozornit, ze block se stejnym jmenem existuje a menit ho nemuzu
- David Grudl
- Nette Core | 8227
Být k dispozici a přepisovat je z pohledu chování bloků vlastně to stejné.
- medhi
- Generous Backer | 255
Je to taky možnost. Otázkou je, jestli by pak mělo existovat i makro {includelocal} (což už má zavádějící význam) nebo > > {include foo} by měl přednostně includovat lokální blok?
Možná by se měla vyhodit chyba, pokud se budu snažit vytvořit takový blok lokálně, který už existuje globálně a naopak.
Block foo can't be defined both globally and locally in …
Tyhle věci nechci míchat. Buď vím, že block budu používat i v jiných šablonách a vytvořím ho normálně nebo vím, že ho chci jenom lokálně a nebudu na něj používat již existující název, dělalo by to akorát zmatky. Toto mě přinutí držet kód přehlednější.
- Kori
- Člen | 73
Musel jsem si to 10× precist, nez jsem z popisu konecne pochopil, ze je to vlastne stejne jako lokalni scope promenne :-)
Tj. pokud pouziju local, tak se vynecha pouziti nadrazenych includovanych bloku se stejnym jmenem.
Ale porad mi asi unika smysl :-) Proste bud se mi block prepise (jako ted) a nebo pouziju jiny nazev / kdyz je to v podstate jiny block v ramci sablony.
Jeste bych pochopil / uvital to „final“, ktere by zabranovalo one redefinici blocku
block.latte
{define final #block1}
aaa
{/define}
some.latte
{includeblock 'block.latte'}
{include #block1} //error, block is final
{define #block1}
bbb
{/define}
…ale mozna jen potrebuju vic kafe :-)
Editoval Kori (11. 11. 2020 14:57)
- kminekmatej
- Generous Backer | 38
Držel bych chování stejné jako dle jiných jazyků, tedy samotnou viditelnost bych nastavoval přímo v tom bloku, tedy varianty:
{define local abc}
vlastně odpovídá:
private function abc
Mě se víc líbí naopak spíš keyword private
a to právě
kvůli identitě napříč jinými jazyky – ale jako netrvám na tom ;).
{import abc}
by pak obdobně includoval nejdřív private (pokud
existuje), pak až public.
Styl podtržítko bych nedělal, příliš mi připomíná hrůzy dob dávno minulých.
Includování pomocí scope, tedy je imho něco navíc, protože:
{include self::abc}
je to samé jako
{define local abc} {include abc}
{include parent::abc}
je to samé jako {define abc}
(v parentu) a {include abc}
{include child::abc}
by mi dávalo smysl pouze když by šel block
definovat jako abstraktní, tedy {define abstract abc}
Jak naznačují ostatní příspěvky, tato diskuze může snadno rozrůst do dalších nastavovávatek viditelnosti, tedy např. final, protected apod. Je otázkou kde je ta lajna kde to prozatím ukončit :).
- xificurk
- Člen | 121
Docela chápu, že rozlišení pomocí jména bloku se může zdát pohodlné. Elegantně se tím vyřeší otázka, co by se mělo stát, když se v jedné šabloně objeví kolize běžného a lokálního bloku – k tomu prostě nemůže dojít.
Ale stejně bych se téhle konvenci raději vyhnul (a je jedno jestli jde o prefix „local:“, podtržítko, nebo něco jiného). Přijde mi to jako docela nebezpečný BC break, zejména ve spojení s dynamicky pojmenovanými bloky. Nechtěl bych něco takového debugovat.
Napadlo mne označení pomocí modifkátoru, ale to by moc nefungoval v n:attribute variantě.
Asi nedává moc smysl pro tuhle feature vymýšlet nějakou úplně novou
syntax, tím pádem jedinou rozumnou variantou je asi explcitně nově
pojemnovaná makra typu localBlock
(?)
- David Grudl
- Nette Core | 8227
medhi napsal(a):
Buď vím, že block budu používat i v jiných šablonách a vytvořím ho normálně nebo vím, že ho chci jenom lokálně a nebudu na něj používat již existující název, dělalo by to akorát zmatky.
V tom případě nepotřebuješ lokální bloky. Jejich smysl je právě v tom, že nemusím přemýšlet, jestli náhodou ten název není jinde použitý.
Viz ten úvodní příklad. Vytvořím si pojmenovaný blok kvůli rekurzivnímu vykreslení menu kdesi v sidebaru. A nechci v tu chvíli řešit, jestli náhodou v nějaké jiné šabloně není blok stejného jména a mohlo by to něco pokazit.
- David Grudl
- Nette Core | 8227
xificurk napsal(a):
Docela chápu, že rozlišení pomocí jména bloku se může zdát pohodlné.
Ne, to není pomocí jména. Na jméno to nemá vliv. Je to prostě „nějaké označení“, že tento blok je local/private.
- xificurk
- Člen | 121
David Grudl napsal(a):
xificurk napsal(a):
Docela chápu, že rozlišení pomocí jména bloku se může zdát pohodlné.
Ne, to není pomocí jména. Na jméno to nemá vliv. Je to prostě „nějaké označení“, že tento blok je local/private.
Aha, jak to tedy funguje v kombinaci s dynamicky pojemnovanými bloky?
{var $name = 'local:foo'}
{block $name} ...normální? blok
{block local:$name} ...lokální? blok
A interagují mezi sebou nějak normální a lokální bloky se stejným jménem, jak?
Nebo to jsou dvě úplně oddělené sady s tím, že i v případě
include
musím vždy explicitně specifikovat jestli chci lokální
blok, nebo ne? Nebyla by to tedy potom další známka toho, že by měla
existovat oddělená sada maker pro lokální bloky?
- Kori
- Člen | 73
David Grudl napsal(a):
Viz ten úvodní příklad. Vytvořím si pojmenovaný blok kvůli rekurzivnímu vykreslení menu kdesi v sidebaru. A nechci v tu chvíli řešit, jestli náhodou v nějaké jiné šabloně není blok stejného jména a mohlo by to něco pokazit.
A kdyz tu tvoji sablonu s private blockem pouziju v dalsi sablone, tak se to bude chovat jak? Jako ted je to chovani naprosto predvidatelne, block se proste bud pouzije a nebo prepise a stejne musim mit povedomi, kde jake blocky jsou.
Napr. mam block #title a jak se to bude chovat kdyz do sebe vkladam 3 sablony a druha bude mit #title private. Co ta treti?
Editoval Kori (11. 11. 2020 16:17)
- David Grudl
- Nette Core | 8227
xificurk napsal(a):
Aha, jak to tedy funguje v kombinaci s dynamicky pojemnovanými bloky?
Budu používat místo {block local:xxx}
syntax
{block local xxx}
na které tu byla většinová shoda, ale jinak
to funguje stejně.
{var $name = 'local foo'}
{block $name} // vytvoří normální blok s názvem `local foo`
{block local $name} // vytvoří lokální blok s názvem `local foo`
(přesněji řečeno, v tuto chvíli vyhodí {block local $name} výjimku, protože lokální dynamické bloky nejsou podporované, jelikož nevím jestli jsou potřeba)
A interagují mezi sebou nějak normální a lokální bloky se stejným jménem, jak?
Ne.
Nebo to jsou dvě úplně oddělené sady s tím, že i v případě
include
musím vždy explicitně specifikovat jestli chci lokální blok, nebo ne?
V tuto chvíli se to u include musí specifikovat. Ale jak jsem psal výše, bylo by možné i chování, kdy bere primárně lokální blok a pak by se to nespecifikovalo vůbec.
Nebyla by to tedy potom další známka toho, že by měla existovat oddělená sada maker pro lokální bloky?
Je to čistě věc syntaxe, jestli psát
{blockLocal foo}...{/blockLocal}
nebo
{block local foo}...{/block}
.
- David Grudl
- Nette Core | 8227
Kori napsal(a):
Napr. mam block #title a jak se to bude chovat kdyz do sebe vkladam 3 sablony a druha bude mit #title private. Co ta treti?
Zkus si to představit tak, že ke jménu každého privátního bloku se přilepí ještě jméno souboru se šablonou (technicky to funguje úplně jinak, ale to je fuk). Takový blok je pro ostatní šablony neviditelný.
- David Grudl
- Nette Core | 8227
ad {blockLocal foo}
vs {block local foo}
Bloky se kdysi v pravěku zapisovaly i takto {#foo} ... {/#}
,
ale kvůli srozumitelnosti se preferovalo uvedení slova block, občas tak
vznikalo {block #foo}
a dodnes ten znak #
někteří
používají.
Mřížku je stále potřeba uvádět pro rozlišení dynamických bloků v
{include #$name}
a {ifset #$name}
. Což dávno
plánuju opět kvůli srozumitelnost nějak nahradit.
Jedna z možností je {include block $name}
a
{ifset block $name}
, což je konzistentní syntax s
{block local foo}
.
Druhá možnost je jít cestou nových značek, jenže … to je
problém – schválně jestli tušíte jaký. Prostě
{includeblock}
existuje a dělá něco úplně jiného. Vlastně to
byl důvod jeho deprecatnutí a (částečné) nahrazení za
{import}
.
- David Grudl
- Nette Core | 8227
Milo napsal(a):
Nebo zavést něco jako namespace šablony. Kdybys includoval/volal, udal bys, z jakého namespace. A ty namespace by mohli mít viditelnost, něco co v PHP teď chybí.
To asi máš na mysli nějaký scope jako tu zmiňoval @filsedla. Do toho se pouštět neplánuju, nemám pro to usecase.
- Kori
- Člen | 73
Ok, pockam na finalni implementaci. Ja se na to hlavne dival (mozna blbe) z pohledu dedicnosti a tam bych chtel, aby to melo jasne a predvidatelne chovani. Ted to funguje jak v php, tj. pouzije se lokalni implementace nebo includovana / parent a.k.a
class A
{
public function getTitle()
}
class B extends A
{
public function getTitle()
}
Nebo to jsou dvě úplně oddělené sady s tím, že i v případě
include
musím vždy explicitně specifikovat jestli chci lokální blok, nebo ne?V tuto chvíli se to u include musí specifikovat. Ale jak jsem psal výše, bylo by možné i chování, kdy bere primárně lokální blok a pak by se to nespecifikovalo vůbec.
IMHO by se prave melo explicitne uvadet jaky blok chci pouzit, jestli
includovany (parent::getTitle()
) nebo lokalni, protoze muze nastat
situace, kdy budu chtit pouzit oba.
Nebyla by to tedy potom další známka toho, že by měla existovat oddělená sada maker pro lokální bloky?
Je to čistě věc syntaxe, jestli psát
{blockLocal foo}...{/blockLocal}
nebo{block local foo}...{/block}
.
Osobne jsem za {blockLocal foo}
at je to na prvni pohled
viditelne
- David Grudl
- Nette Core | 8227
Kori napsal(a):
Z pohledu dedicnosti a tam bych chtel, aby to melo jasne a predvidatelne chovani.
Privátní (resp lokální) prvky z pohledu dědičnosti neexistují.
- David Grudl
- Nette Core | 8227
Hele, nedělejme z toho kovbojku. Využití pro lokální blok jsem napsal v prvním postu. Nestává se tak často, že by došlo ke kolizí jmen pomocných bloků ve více šablonách, proto po nich nikdo nevolal. Ale připadá mi fajn mít možnost blok označit a mít tak jistotu, že ke kolizi nedojde nikdy.
Kvůli této drobnosti nebudu přidávat X dalších maker a zesložiťovat
dokumentaci atd. Použiji modifikátor local
.
Jinak v dev verzi to je, takže to můžete vyzkoušet.
- David Grudl
- Nette Core | 8227
Přemýšlím nad tím {include}
a myslím, že tam se prostě
local uvádět nemusí.
- pokud mám v šabloně lokální blok foo, pak {include foo} znamená že chci inkludovat tento lokální blok
- pokud mám v šabloně lokální blok a chci inkludovat nelokální blok foo (tedy vím o tom že existuje), tak přece nebudu dávat lokálnímu bloku název foo a dělat si v tom hokej
- v šabloně by mohl být lokální blok foo a zároveň nelokální (kvůli tomu, že přepisuje blok z jiné šablony). Technicky tomu nic nebrání, ale mám pokušení to nepovolit
- Šaman
- Člen | 2661
David Grudl napsal(a):
Technicky tomu nic nebrání, ale mám pokušení to nepovolit
Imho nepovolit, pokud někoho nenapadne reálně užitečný use-case. Striktnější pravidla způsobují méně WTF momentů. A dva stejně pojmenované bloky v jedné šabloně znamená mít v hlavě přesný algoritmus kdo má kdy přednost, jinak to ty WTF momenty způsobovat bude.
- Kori
- Člen | 73
David Grudl napsal(a):
Přemýšlím nad tím
{include}
a myslím, že tam se prostě local uvádět nemusí.
- pokud mám v šabloně lokální blok foo, pak {include foo} znamená že chci inkludovat tento lokální blok
- pokud mám v šabloně lokální blok a chci inkludovat nelokální blok foo (tedy vím o tom že existuje), tak přece nebudu dávat lokálnímu bloku název foo a dělat si v tom hokej
- v šabloně by mohl být lokální blok foo a zároveň nelokální (kvůli tomu, že přepisuje blok z jiné šablony). Technicky tomu nic nebrání, ale mám pokušení to nepovolit
Jan bych to zakazal a pouzivalo by se pouze include bez local. Sice to ted nabizi o neco vetsi flexibilitu, ale kdyz nahodou nekdo bude potrebovat prepsat include blok, tak si ten lokalni prejmenuje.