Latte: jak zapisovat lokální/privátní bloky

David Grudl
Nette Core | 8082
+
+2
-

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
+
+5
-

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}{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 | 146
+
+2
-

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
+
0
-
  1. Zatím to možná není špatné, ale pokud se opravdu prosadí pojmenované parametry, mohla by se tahle syntax stát hodně matoucí.
  2. 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 | 8082
+
0
-

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}{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 slovo local. Jinak se mi local 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
+
+3
-

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 | 96
+
0
-

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
+
0
-

Z toho popisu mi prijde, ze jde o dve ruzne veci a latte by na to melo ruzne reagovat

  1. 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

  1. 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 | 8082
+
0
-

Být k dispozici a přepisovat je z pohledu chování bloků vlastně to stejné.

medhi
Generous Backer | 255
+
0
-

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
+
0
-

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 | 34
+
0
-

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
+
0
-

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 | 8082
+
+1
-

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 | 8082
+
0
-

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
+
+1
-

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
+
0
-

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 | 8082
+
+2
-

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 | 8082
+
+1
-

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ý.

Milo
Nette Core | 1283
+
0
-

A co lokální blok obklopit:

{local}
    {define...}
{/local}

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í.

David Grudl
Nette Core | 8082
+
+1
-

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 | 8082
+
0
-

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
+
0
-

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 | 8082
+
0
-

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 | 8082
+
+4
-

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.

Kori
Člen | 73
+
0
-

David Grudl napsal(a):

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í.

Proste si nerozumime, v pohode 🙂

David Grudl
Nette Core | 8082
+
0
-

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 | 2632
+
+1
-

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
+
0
-

Ok, tak jsem si to vyzkousel a funguje to, jak bych cekal (thumbs up)

Directivou local se da jasne urcit, jestli chci pouzit / prepsat includovany blok a nebo pouzit lokalni stejneho jmena.

Kori
Člen | 73
+
0
-

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.

David Grudl
Nette Core | 8082
+
+2
-

Udělal jsem to tak.