Sablonok öröklődése és újrafelhasználhatósága
A sablonok újrafelhasználási és öröklődési mechanizmusai növelik a termelékenységet, mivel minden sablon csak az egyedi tartalmát tartalmazza, az ismétlődő elemek és struktúrák pedig újrafelhasználhatók. Három koncepciót mutatunk be: elrendezés öröklődés, horizontális újrafelhasználás és egység öröklődés.
A Latte sablon öröklődési koncepciója hasonló a PHP osztályöröklődéséhez. Definiál egy szülő sablont, amelytől más gyermek sablonok örökölhetnek, és felülírhatják a szülő sablon részeit. Ez nagyszerűen működik, amikor az elemek közös struktúrán osztoznak. Bonyolultnak hangzik? Ne aggódjon, nagyon egyszerű.
Layout öröklődés {layout}
Nézzük meg a layout sablon öröklődését, azaz a layoutot, egy példával. Ez egy szülő sablon, amelyet például
layout.latte
-nak nevezünk, és amely meghatározza a HTML dokumentum vázát:
<!doctype html>
<html lang="hu">
<head>
<title>{block title}{/block}</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="content">
{block content}{/block}
</div>
<div id="footer">
{block footer}© Copyright 2008{/block}
</div>
</body>
</html>
A {block}
tagek három blokkot definiálnak, amelyeket a gyermek sablonok kitölthetnek. A block
tag
csak annyit tesz, hogy jelzi, hogy ezt a helyet a gyermek sablon felülírhatja egy saját, azonos nevű blokk
definiálásával.
Egy gyermek sablon így nézhet ki:
{layout 'layout.latte'}
{block title}Az én csodálatos blogom{/block}
{block content}
<p>Üdvözöllek a fantasztikus honlapomon.</p>
{/block}
A kulcs itt a {layout}
tag. Ez közli a Latte-val, hogy ez a sablon „kiterjeszt“ egy másik sablont. Amikor a
Latte rendereli ezt a sablont, először megtalálja a szülő sablont – ebben az esetben a layout.latte
-t.
Ezen a ponton a Latte észreveszi a három blokk taget a layout.latte
-ban, és ezeket a blokkokat a gyermek sablon
tartalmával helyettesíti. Mivel a gyermek sablon nem definiálta a footer
blokkot, helyette a szülő sablon
tartalma kerül felhasználásra. A {block}
tag tartalma a szülő sablonban mindig tartalékként használatos.
A kimenet így nézhet ki:
<!doctype html>
<html lang="hu">
<head>
<title>Az én csodálatos blogom</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="content">
<p>Üdvözöllek a fantasztikus honlapomon.</p>
</div>
<div id="footer">
© Copyright 2008
</div>
</body>
</html>
A gyermek sablonban a blokkok csak a legfelső szinten vagy egy másik blokkon belül helyezhetők el, azaz:
{block content}
<h1>{block title}Üdvözöllek a fantasztikus honlapomon{/block}</h1>
{/block}
Továbbá egy blokk mindig létrejön, függetlenül attól, hogy a környező {if}
feltétel igazra vagy hamisra
értékelődik-e ki. Tehát még ha nem is úgy tűnik, ez a sablon definiálja a blokkot.
{if false}
{block head}
<meta name="robots" content="noindex, follow">
{/block}
{/if}
Ha azt szeretné, hogy a blokkon belüli kimenet feltételesen jelenjen meg, használja helyette a következőt:
{block head}
{if $condition}
<meta name="robots" content="noindex, follow">
{/if}
{/block}
A gyermek sablonban a blokkokon kívüli tér a layout sablon renderelése előtt kerül végrehajtásra, így használhatja
őket változók definiálására, mint {var $foo = bar}
, és adatok terjesztésére az egész öröklődési láncon
keresztül:
{layout 'layout.latte'}
{var $robots = noindex}
...
Többszintű öröklődés
Annyi öröklődési szintet használhat, amennyire szüksége van. A layout öröklődés gyakori használati módja a következő háromszintű megközelítés:
- Hozzon létre egy
layout.latte
sablont, amely tartalmazza a webhely fő megjelenési vázát. - Hozzon létre egy
layout-SECTIONNAME.latte
sablont a webhely minden szakaszához. Példáullayout-news.latte
,layout-blog.latte
stb. Mindezek a sablonok kiterjesztik alayout.latte
-t, és tartalmazzák az egyes szakaszokra jellemző stílusokat és dizájnt. - Hozzon létre egyedi sablonokat minden oldaltípushoz, például egy hírcikkhez vagy blogbejegyzéshez. Ezek a sablonok a megfelelő szakasz sablonját terjesztik ki.
Dinamikus öröklődés
A szülő sablon neveként használható változó vagy bármilyen PHP kifejezés, így az öröklődés dinamikusan viselkedhet:
{layout $standalone ? 'minimum.latte' : 'layout.latte'}
A Latte API-t is használhatja a layout sablon automatikus kiválasztásához.
Tippek
Íme néhány tipp a layout öröklődéssel való munkához:
- Ha a sablonban
{layout}
-ot használ, annak a sablon első tagjének kell lennie. - A layout automatikusan kereshető (mint
például a presenterekben). Ebben az esetben, ha
a sablonnak nem kell layouttal rendelkeznie, ezt a
{layout none}
taggel jelzi. - A
{layout}
tagnek van egy aliasa:{extends}
. - A layout fájl neve a loadertől függ.
- Annyi blokkot hozhat létre, amennyit csak akar. Ne feledje, hogy a gyermek sablonoknak nem kell definiálniuk az összes szülő blokkot, így több blokkban is kitölthet ésszerű alapértelmezett értékeket, majd később csak azokat definiálhatja, amelyekre szüksége van.
Blokkok {block}
Lásd még az anonim {block}
A blokk egy módja annak, hogy megváltoztassuk egy sablonrész renderelésének módját, de semmilyen módon nem avatkozik be a körülötte lévő logikába. A következő példában bemutatjuk, hogyan működik a blokk, de azt is, hogyan nem:
{foreach $posts as $post}
{block post}
<h1>{$post->title}</h1>
<p>{$post->body}</p>
{/block}
{/foreach}
Ha ezt a sablont rendereli, az eredmény pontosan ugyanaz lesz a {block}
tagekkel és anélkül is. A blokkok
hozzáférhetnek a külső hatókörök változóihoz. Csak lehetőséget adnak arra, hogy egy gyermek sablon
felülírja őket:
{layout 'parent.latte'}
{block post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/block}
Most, amikor a gyermek sablont rendereljük, a ciklus a child.latte
gyermek sablonban definiált blokkot fogja
használni a parent.latte
-ban definiált blokk helyett; a futtatott sablon ekkor a következővel egyenértékű:
{foreach $posts as $post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/foreach}
Ha azonban egy nevesített blokkon belül új változót hozunk létre, vagy egy meglévő értékét helyettesítjük, a változás csak a blokkon belül lesz látható:
{var $foo = 'foo'}
{block post}
{do $foo = 'új érték'}
{var $bar = 'bar'}
{/block}
foo: {$foo} // kiírja: foo
bar: {$bar ?? 'nincs definiálva'} // kiírja: nincs definiálva
A blokk tartalmát szűrőkkel lehet módosítani. A következő példa eltávolítja az összes HTML-t és megváltoztatja a betűméretet:
<title>{block title|stripHtml|capitalize}...{/block}</title>
A taget n:attributumként is írható:
<article n:block=post>
...
</article>
Lokális blokkok
Minden blokk felülírja az azonos nevű szülő blokk tartalmát – kivéve a lokális blokkokat. Az osztályokban ez valami olyasmi lenne, mint a privát metódusok. Így anélkül hozhat létre sablont, hogy aggódnia kellene amiatt, hogy a blokknevek egyezése miatt más sablonból felülíródnának.
{block local helper}
...
{/block}
Blokkok renderelése {include}
Lásd még {include file}
Ha egy blokkot egy adott helyen szeretne kiírni, használja a {include blockname}
taget:
<title>{block title}{/block}</title>
<h1>{include title}</h1>
Lehetőség van egy másik sablonból származó blokk kiírására is:
{include footer from 'main.latte'}
A renderelt blokk nem fér hozzá az aktív kontextus változóihoz, kivéve, ha a blokk ugyanabban a fájlban van definiálva, ahol be van illesztve. Azonban hozzáfér a globális változókhoz.
Változókat így adhat át a blokknak:
{include footer, foo: bar, id: 123}
A blokk neveként használható változó vagy bármilyen PHP kifejezés. Ebben az esetben a változó elé még hozzáadjuk a
block
kulcsszót, hogy már a fordításkor tudja a Latte, hogy ez egy blokk, és nem egy sablon beillesztése, amelynek a neve szintén lehet egy változóban:
{var $name = footer}
{include block $name}
A blokk önmagán belül is renderelhető, ami például hasznos egy fastruktúra renderelésekor:
{define menu, $items}
<ul>
{foreach $items as $item}
<li>
{if is_array($item)}
{include menu, $item}
{else}
{$item}
{/if}
</li>
{/foreach}
</ul>
{/define}
A {include menu, ...}
helyett írhatjuk azt is, hogy {include this, ...}
, ahol a this
az
aktuális blokkot jelenti.
A renderelt blokkot szűrőkkel lehet módosítani. A következő példa eltávolítja az összes HTML-t és megváltoztatja a betűméretet:
<title>{include heading|stripHtml|capitalize}</title>
Szülő blokk
Ha a szülő sablonból származó blokk tartalmát kell kiírnia, használja a {include parent}
-et. Ez akkor
hasznos, ha csak ki szeretné egészíteni a szülő blokk tartalmát ahelyett, hogy teljesen felülírná.
{block footer}
{include parent}
<a href="https://github.com/nette">GitHub</a>
<a href="https://twitter.com/nettefw">Twitter</a>
{/block}
Definíciók {define}
A blokkok mellett a Latte-ban léteznek “definíciók” is. A hagyományos programozási nyelvekben ezeket a függvényekhez hasonlítanánk. Hasznosak a sablonrészletek újrafelhasználására, hogy ne ismételjük önmagunkat.
A Latte igyekszik egyszerűvé tenni a dolgokat, így alapvetően a definíciók ugyanazok, mint a blokkok, és minden, ami a blokkokról elmondható, a definíciókra is igaz. Abban különböznek a blokkoktól, hogy:
{define}
tagekbe vannak zárva- csak akkor renderelődnek, ha
{include}
-dal beillesztjük őket - paramétereket lehet nekik definiálni, hasonlóan a PHP függvényeihez
{block foo}<p>Hello</p>{/block}
{* kiírja: <p>Hello</p> *}
{define bar}<p>World</p>{/define}
{* nem ír ki semmit *}
{include bar}
{* kiírja: <p>World</p> *}
Képzeljen el egy segédsablont definíciók gyűjteményével, hogyan kell HTML űrlapokat rajzolni.
{define input, $name, $value, $type = 'text'}
<input type={$type} name={$name} value={$value}>
{/define}
{define textarea, $name, $value}
<textarea name={$name}>{$value}</textarea>
{/define}
Az argumentumok mindig opcionálisak, alapértelmezett értékük null
, hacsak nincs megadva alapértelmezett
érték (itt a 'text'
az alapértelmezett érték $type
-hoz). A paraméterek típusait is
deklarálhatjuk: {define input, string $name, ...}
.
A definíciókat tartalmazó sablont {import}
segítségével
töltjük be. Maguk a definíciók ugyanúgy renderelődnek, mint a blokkok:
<p>{include input, 'password', null, 'password'}</p>
<p>{include textarea, 'comment'}</p>
A definíciók nem férnek hozzá az aktív kontextus változóihoz, de hozzáférnek a globális változókhoz.
Dinamikus blokknevek
A Latte nagy rugalmasságot tesz lehetővé a blokkok definiálásában, mivel a blokk neve bármilyen PHP kifejezés lehet.
Ez a példa három blokkot definiál hi-Peter
, hi-John
és hi-Mary
néven:
{foreach [Peter, John, Mary] as $name}
{block "hi-$name"}Szia, {$name} vagyok.{/block}
{/foreach}
A gyermek sablonban ezután például csak egy blokkot definiálhatunk újra:
{block hi-John}Hello. Én {$name} vagyok.{/block}
Így a kimenet így fog kinézni:
Szia, Peter vagyok.
Hello. Én John vagyok.
Szia, Mary vagyok.
Blokkok létezésének ellenőrzése {ifset}
Lásd még {ifset $var}
A {ifset blockname}
teszt segítségével ellenőrizzük, hogy az aktuális kontextusban létezik-e egy blokk
(vagy több blokk):
{ifset footer}
...
{/ifset}
{ifset footer, header, main}
...
{/ifset}
A blokk neveként használható változó vagy bármilyen PHP kifejezés. Ebben az esetben a változó elé még hozzáadjuk a
block
kulcsszót, hogy egyértelmű legyen, hogy nem a változók létezésének teszteléséről van szó:
{ifset block $name}
...
{/ifset}
A blokkok létezését a hasBlock()
függvény is
ellenőrzi:
{if hasBlock(header) || hasBlock(footer)}
...
{/if}
Tippek
Néhány tipp a blokkokkal való munkához:
- Az utolsó legfelső szintű blokknak nem kell záró taggel rendelkeznie (a blokk a dokumentum végével ér véget). Ez leegyszerűsíti az egy elsődleges blokkot tartalmazó gyermek sablonok írását.
- A jobb olvashatóság érdekében megadhatja a blokk nevét a
{/block}
tagben, például{/block footer}
. A névnek azonban meg kell egyeznie a blokk nevével. Nagyobb sablonokban ez a technika segít megállapítani, hogy mely blokk tagek záródnak. - Ugyanabban a sablonban nem definiálhat közvetlenül több azonos nevű blokk taget. Ezt azonban dinamikus blokknevekkel lehet elérni.
- Használhat n:attribútumokat blokkok definiálására,
mint
<h1 n:block=title>Üdvözöllek a fantasztikus honlapomon</h1>
- A blokkok név nélkül is használhatók, csak a szűrők
alkalmazására:
{block|strip} hello {/block}
Horizontális újrafelhasználás {import}
A horizontális újrafelhasználás a Latte-ban a harmadik mechanizmus az újrafelhasználásra és öröklődésre.
Lehetővé teszi blokkok betöltését más sablonokból. Ez hasonló ahhoz, mint amikor PHP-ban létrehozunk egy fájlt
segédfüggvényekkel, amelyet aztán a require
segítségével töltünk be.
Bár a sablon layout öröklődése a Latte egyik legerősebb funkciója, egyszerű öröklődésre korlátozódik – egy sablon csak egy másik sablont terjeszthet ki. A horizontális újrafelhasználás egy módja a többszörös öröklődés elérésének.
Legyen egy fájlunk blokkdefiníciókkal:
{block sidebar}...{/block}
{block menu}...{/block}
Az {import}
paranccsal importáljuk az összes blokkot és definíciót, amelyek a
blocks.latte
-ban vannak definiálva, egy másik sablonba:
{import 'blocks.latte'}
{* most már használhatók a sidebar és menu blokkok *}
Ha a blokkokat egy szülő sablonban importálja (azaz {import}
-ot használ a layout.latte
-ban), a
blokkok minden gyermek sablonban is elérhetők lesznek, ami nagyon praktikus.
Az importálásra szánt sablon (pl. blocks.latte
) nem terjeszthet ki
másik sablont, azaz nem használhat {layout}
-ot. Azonban importálhat más sablonokat.
Az {import}
tagnek a {layout}
utáni első sablon tagjének kell lennie. A sablon neve bármilyen
PHP kifejezés lehet:
{import $ajax ? 'ajax.latte' : 'not-ajax.latte'}
A sablonban annyi {import}
parancsot használhat, amennyit csak akar. Ha két importált sablon ugyanazt a
blokkot definiálja, az első nyer. A legmagasabb prioritása azonban a fő sablonnak van, amely felülírhat bármilyen
importált blokkot.
A felülírt blokkok tartalmát megőrizhetjük úgy, hogy a blokkot ugyanúgy illesztjük be, mint a szülő blokkot:
{layout 'layout.latte'}
{import 'blocks.latte'}
{block sidebar}
{include parent}
{/block}
{block title}...{/block}
{block content}...{/block}
Ebben a példában a {include parent}
a sidebar
blokkot hívja meg a blocks.latte
sablonból.
Egység öröklődés {embed}
Az egység öröklődés kiterjeszti a layout öröklődés gondolatát a tartalomfragmentumok szintjére. Míg a layout öröklődés a „dokumentumvázakkal“ dolgozik, amelyeket a gyermek sablonok keltenek életre, az egység öröklődés lehetővé teszi kisebb tartalmi egységek vázainak létrehozását és újrafelhasználását bárhol, ahol csak akarja.
Az egység öröklődésben a kulcs a {embed}
tag. Kombinálja az {include}
és a
{layout}
viselkedését. Lehetővé teszi egy másik sablon vagy blokk tartalmának beágyazását, és opcionálisan
változók átadását, ugyanúgy, mint az {include}
esetében. Lehetővé teszi továbbá a beágyazott sablonon
belül definiált bármely blokk felülírását, mint a {layout}
használatakor.
Például használjunk egy harmonika elemet. Nézzük meg az elem vázát, amely a collapsible.latte
sablonban van
tárolva:
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}{/block}
</h4>
<div class="collapsible__content">
{block content}{/block}
</div>
</section>
A {block}
tagek két blokkot definiálnak, amelyeket a gyermek sablonok kitölthetnek. Igen, mint a layout
öröklődés szülő sablonja esetében. Láthatja a $modifierClass
változót is.
Használjuk az elemünket egy sablonban. Itt jön képbe az {embed}
. Ez egy rendkívül erőteljes tag, amely
lehetővé teszi számunkra, hogy minden dolgot megtegyünk: beágyazzuk az elem sablonjának tartalmát, változókat adjunk
hozzá, és blokkokat adjunk hozzá saját HTML-lel:
{embed 'collapsible.latte', modifierClass: my-style}
{block title}
Hello World
{/block}
{block content}
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing
elit. Nunc dapibus tortor vel mi dapibus sollicitudin.</p>
{/block}
{/embed}
A kimenet így nézhet ki:
<section class="collapsible my-style">
<h4 class="collapsible__title">
Hello World
</h4>
<div class="collapsible__content">
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing
elit. Nunc dapibus tortor vel mi dapibus sollicitudin.</p>
</div>
</section>
A beágyazott tageken belüli blokkok különálló réteget alkotnak, amely független a többi blokktól. Ezért lehet
ugyanaz a nevük, mint a beágyazáson kívüli blokkoknak, és semmilyen módon nem befolyásolják őket. Az include tag használatával az {embed}
tageken belül beillesztheti az itt
létrehozott blokkokat, a beágyazott sablonból származó blokkokat (amelyek nem lokálisak), valamint a fő sablonból származó blokkokat, amelyek viszont lokálisak. Más
fájlokból is importálhat blokkokat:
{block outer}…{/block}
{block local hello}…{/block}
{embed 'collapsible.latte', modifierClass: my-style}
{import 'blocks.latte'}
{block inner}…{/block}
{block title}
{include inner} {* működik, a blokk az embed-en belül van definiálva *}
{include hello} {* működik, a blokk lokális ebben a sablonban *}
{include content} {* működik, a blokk a beágyazott sablonban van definiálva *}
{include aBlockDefinedInImportedTemplate} {* működik *}
{include outer} {* nem működik! - a blokk a külső rétegben van *}
{/block}
{/embed}
A beágyazott sablonok nem férnek hozzá az aktív kontextus változóihoz, de hozzáférnek a globális változókhoz.
Az {embed}
segítségével nemcsak sablonokat, hanem más blokkokat is be lehet illeszteni, így az előző
példát így is le lehetne írni:
{define collapsible}
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}{/block}
</h4>
...
</section>
{/define}
{embed collapsible, modifierClass: my-style}
{block title}
Hello World
{/block}
...
{/embed}
Ha az {embed}
-be egy kifejezést adunk át, és nem egyértelmű, hogy ez egy blokk vagy egy fájl neve, akkor
hozzáadjuk a block
vagy file
kulcsszót:
{embed block $name} ... {/embed}
Használati esetek
A Latte-ban különböző típusú öröklődés és kód újrafelhasználás létezik. Foglaljuk össze a fő koncepciókat a nagyobb érthetőség kedvéért:
{include template}
Használati eset: header.latte
és footer.latte
használata a
layout.latte
-on belül.
header.latte
<nav>
<div>Kezdőlap</div>
<div>Rólunk</div>
</nav>
footer.latte
<footer>
<div>Copyright</div>
</footer>
layout.latte
{include 'header.latte'}
<main>{block main}{/block}</main>
{include 'footer.latte'}
{layout}
Használati eset: layout.latte
kiterjesztése a homepage.latte
és about.latte
fájlokban.
layout.latte
{include 'header.latte'}
<main>{block main}{/block}</main>
{include 'footer.latte'}
homepage.latte
{layout 'layout.latte'}
{block main}
<p>Kezdőlap</p>
{/block}
about.latte
{layout 'layout.latte'}
{block main}
<p>Rólunk oldal</p>
{/block}
{import}
Használati eset: sidebar.latte
a single.product.latte
és single.service.latte
fájlokban.
sidebar.latte
{block sidebar}<aside>Ez az oldalsáv</aside>{/block}
single.product.latte
{layout 'product.layout.latte'}
{import 'sidebar.latte'}
{block main}<main>Termék oldal</main>{/block}
single.service.latte
{layout 'service.layout.latte'}
{import 'sidebar.latte'}
{block main}<main>Szolgáltatás oldal</main>{/block}
{define}
Használati eset: Függvények, amelyeknek változókat adunk át, és valamit renderelnek.
form.latte
{define form-input, $name, $value, $type = 'text'}
<input type={$type} name={$name} value={$value}>
{/define}
profile.service.latte
{import 'form.latte'}
<form action="" method="post">
<div>{include form-input, username}</div>
<div>{include form-input, password}</div>
<div>{include form-input, submit, Submit, submit}</div>
</form>
{embed}
Használati eset: pagination.latte
beágyazása a product.table.latte
és
service.table.latte
fájlokba.
pagination.latte
<div id="pagination">
<div>{block first}{/block}</div>
{for $i = $min + 1; $i < $max - 1; $i++}
<div>{$i}</div>
{/for}
<div>{block last}{/block}</div>
</div>
product.table.latte
{embed 'pagination.latte', min: 1, max: $products->count}
{block first}Első termék oldal{/block}
{block last}Utolsó termék oldal{/block}
{/embed}
service.table.latte
{embed 'pagination.latte', min: 1, max: $services->count}
{block first}Első szolgáltatás oldal{/block}
{block last}Utolsó szolgáltatás oldal{/block}
{/embed}