Sablon öröklődés és újrafelhasználhatóság
A sablonok újrafelhasználhatósága és az öröklési mechanizmusok azért vannak itt, hogy növeljék a termelékenységet, mivel minden sablon csak az egyedi tartalmát tartalmazza, és az ismétlődő elemek és struktúrák újrafelhasználásra kerülnek. Három fogalmat mutatunk be: az elrendezés öröklést, a horizontális újrafelhasználást és az egység öröklést.
A Latte sablon öröklés koncepciója hasonló a PHP osztály örökléshez. Meghatározunk egy szülő sablont, amelyből más gyermeksablonok kiterjeszthetők, és felülírhatják a szülő sablon egyes részeit. Nagyszerűen működik, ha az elemek közös szerkezetűek. Bonyolultnak hangzik? Ne aggódjon, nem az.
Layout öröklődés {layout}
Nézzük meg az elrendezési sablon öröklődését egy példával kezdve. Ez egy szülő sablon, amelyet például
layout.latte
-nak fogunk hívni, és egy HTML vázdokumentumot definiál.
<!doctype html>
<html lang="en">
<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}
címkék három blokkot határoznak meg, amelyeket a gyermek sablonok kitölthetnek. A blokkcímke csak
annyit tesz, hogy közli a sablonmotorral, hogy a gyermek sablonok felülbírálhatják a sablon ezen részeit a saját, azonos
nevű blokkjuk meghatározásával.
Egy gyermek sablon így nézhet ki:
{layout 'layout.latte'}
{block title}My amazing blog{/block}
{block content}
<p>Welcome to my awesome homepage.</p>
{/block}
A {layout}
címke itt a kulcs. Azt mondja a sablonmotornak, hogy ez a sablon egy másik sablont “bővít”.
Amikor a Latte rendereli ezt a sablont, először megkeresi a szülő sablont – ebben az esetben a
layout.latte
.
Ekkor a sablonmotor észreveszi a layout.latte
három blokkcímkét, és ezeket a blokkokat a gyermek sablon
tartalmával helyettesíti. Vegye figyelembe, hogy mivel a gyermek sablon nem definiálta a footer blokkot, helyette a
szülő sablon tartalmát használja. A szülő sablon {block}
címkén belüli tartalom mindig tartalékként
kerül felhasználásra.
A kimenet így nézhet ki:
<!doctype html>
<html lang="en">
<head>
<title>My amazing blog</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="content">
<p>Welcome to my awesome homepage.</p>
</div>
<div id="footer">
© Copyright 2008
</div>
</body>
</html>
A gyermek sablonban a blokkok csak vagy a legfelső szinten, vagy egy másik blokkon belül helyezkedhetnek el, azaz:
{block content}
<h1>{block title}Welcome to my awesome homepage{/block}</h1>
{/block}
A blokk mindig létrejön, függetlenül attól, hogy a környező {if}
feltétel igaznak vagy hamisnak
értékelődik-e ki. Ellentétben azzal, amit gondolhat, ez a sablon valóban definiál egy blokkot.
{if false}
{block head}
<meta name="robots" content="noindex, follow">
{/block}
{/if}
Ha azt szeretné, hogy a blokkban lévő 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 lévő blokkokon kívüli adatok a layout sablon megjelenítése előtt kerülnek végrehajtásra, így
használhatja a {var $foo = bar}
típusú változók definiálására és az adatok továbbítására az egész
öröklési láncban:
{layout 'layout.latte'}
{var $robots = noindex}
...
Többszintű öröklés
Annyi szintű öröklést használhat, amennyire csak szüksége van. Az elrendezés öröklésének egyik gyakori módja a következő háromszintű megközelítés:
- Hozzon létre egy
layout.latte
sablont, amely tartalmazza a webhely fő megjelenését. - Hozzon létre egy
layout-SECTIONNAME.latte
sablont a webhely minden egyes szakaszához. Például:layout-news.latte
,layout-blog.latte
stb. Ezek a sablonok mind alayout.latte
bővítik, és tartalmazzák a szekció-specifikus stílusokat/designt. - Hozzon létre egyedi sablonokat minden egyes oldaltípushoz, például egy hírcikkhez vagy blogbejegyzéshez. Ezek a sablonok kiterjesztik a megfelelő szakaszsablont.
Dinamikus elrendezés öröklődése
A szülő sablon neveként egy változót vagy bármilyen PHP-kifejezést használhat, így az öröklés dinamikusan viselkedhet:
{layout $standalone ? 'minimum.latte' : 'layout.latte'}
A Latte API-t használhatja az elrendezési sablon automatikus kiválasztására is.
Tippek
Íme néhány tipp az elrendezés örökléssel való munkához:
- Ha a
{layout}
címet használja egy sablonban, akkor annak a sablonban az első sabloncímkének kell lennie. - Az elrendezés automatikusan kereshető (mint az előadókban). Ebben az esetben, ha a sablonban
nem kell elrendezés, akkor ezt a
{layout none}
címkével jelzi. - A
{layout}
címkének a{extends}
aliasával rendelkezik. - A kiterjesztett sablon fájlneve a sablon betöltőjétől függ.
- Annyi blokkja lehet, amennyit csak akar. Ne feledje, hogy a gyermek sablonoknak nem kell minden szülői blokkot definiálniuk, így számos blokkban kitölthet ésszerű alapértelmezéseket, és csak azokat definiálhatja, amelyekre később szüksége van.
Blokkok {block}
Lásd még anonim {block}
A blokk lehetőséget biztosít arra, hogy megváltoztassuk a sablon egy bizonyos részének megjelenítési módját, de semmilyen módon nem avatkozik bele a körülötte lévő logikába. A következő példával szemléltetjük, hogyan működik egy blokk, és ami még fontosabb, hogyan nem működik:
{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 lenne a blokkcímkékkel vagy azok nélkül. A blokkok hozzáférnek a külső hatókörök változóihoz. Ez csak egy módja annak, hogy egy gyermek sablon által felülbírálhatóvá tegye:
{layout 'parent.Latte'}
{block post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/block}
Most a gyermek sablon megjelenítésekor a ciklus a child.Latte
gyermek sablonban definiált blokkot fogja
használni a parent.Latte
alapsablonban definiált helyett ; a végrehajtott sablon ekvivalens a következővel:
{foreach $posts as $post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/foreach}
Ha azonban egy új változót hozunk létre egy megnevezett blokkban, vagy kicseréljük egy meglévő változó értékét, a változás csak a blokkban lesz látható:
{var $foo = 'foo'}
{block post}
{do $foo = 'new value'}
{var $bar = 'bar'}
{/block}
foo: {$foo} // prints: foo
bar: {$bar ?? 'not defined'} // prints: not defined
A blokk tartalma szűrőkkel módosítható. A következő példa eltávolít minden HTML-t és címszavakat:
<title>{block title|stripHtml|capitalize}...{/block}</title>
A címke n:attribútumként is leírható:
<article n:block=post>
...
</article>
Helyi blokkok
Minden blokk felülírja az azonos nevű szülői blokk tartalmát. Kivéve a helyi blokkokat. Ezek olyanok, mint az osztály privát metódusai. Létrehozhatsz egy sablont anélkül, hogy aggódnod kellene, hogy – a blokknevek egybeesése miatt – a második sablon felülírja őket.
{block local helper}
...
{/block}
Blokkok nyomtatása {include}
Lásd még {include file}
Ha egy blokkot egy adott helyre szeretne kinyomtatni, használja a {include blockname}
címkét:
<title>{block title}{/block}</title>
<h1>{include title}</h1>
A blokkot egy másik sablonból is megjelenítheti:
{include footer from 'main.latte'}
A nyomtatott 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 szerepel. A globális változókhoz azonban hozzáférnek.
A változókat a következő módon adhatja át a blokknak:
{include footer, foo: bar, id: 123}
A blokk neveként használhat egy változót vagy bármilyen PHP-kifejezést. Ebben az esetben a változó elé írja a
block
kulcsszót, hogy már fordításkor tudható legyen, hogy egy blokkról van szó, és ne egy insert template-ről, amelynek a neve szintén szerepelhetne a
változóban:
{var $name = footer}
{include block $name}
A blokk önmagán belül is kiírható, ami hasznos például egy fa struktúra megjeleníté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 kinyomtatott tartalom szűrőkkel módosítható. A következő példa eltávolít minden HTML-t és címszavakat:
<title>{include heading|stripHtml|capitalize}</title>
Szülői blokk
Ha ki kell nyomtatnia a blokk tartalmát a szülő sablonból, akkor a {include parent}
utasítás megteszi a
hatását. Ez akkor hasznos, ha a szülő blokk tartalmához szeretne hozzáadni ahelyett, hogy teljesen felülírná azt.
{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 “definíciók” is vannak. Ezek a hagyományos programozási nyelvek függvényeihez hasonlíthatók. Hasznosak a sablonrészletek újrafelhasználásához, hogy ne ismételje magát.
A Latte igyekszik a dolgokat egyszerűen tartani, ezért a definíciók alapvetően ugyanazok, mint a blokkok, és minden, amit a blokkokról mondunk, a definíciókra is vonatkozik. Abban különböznek a blokkoktól:
{define}
címkékkel vannak körülvéve- csak akkor kerülnek megjelenítésre, amikor a következő módon kerülnek beillesztésre
{include}
- paramétereket definiálhatsz számukra, mint a PHP függvényeknél.
{block foo}<p>Hello</p>{/block}
{* prints: <p>Hello</p> *}
{define bar}<p>World</p>{/define}
{* prints nothing *}
{include bar}
{* prints: <p>World</p> *}
Képzeljük el, hogy van egy segédsablonunk, amely a HTML űrlapok rajzolásának definícióit tartalmazza.
{define input, $name, $value, $type = 'text'}
<input type={$type} name={$name} value={$value}>
{/define}
{define textarea, $name, $value}
<textarea name={$name}>{$value}</textarea>
{/define}
A definíciók argumentumai mindig opcionálisak, alapértelmezett értékük null
, kivéve, ha alapértelmezett
érték van megadva (itt a 'text'
az alapértelmezett érték a $type
számára). A paramétertípusok
is deklarálhatók: {define input, string $name, ...}
.
A definíciókat tartalmazó sablon betöltése a {import}
. Maguk a
definíciók ugyanúgy kerülnek megjelenítésre, 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 a globális változókhoz igen.
Dinamikus blokknevek
A Latte nagy rugalmasságot biztosít 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, amelyek neve hi-Peter
, hi-John
és hi-Mary
:
{foreach [Peter, John, Mary] as $name}
{block "hi-$name"}Hi, én vagyok {$name}.{/block}
{/foreach}
Egy gyermek sablonban például csak egy blokkot definiálhatunk újra:
{block hi-John}Hello. Én vagyok {$name}.{/block}
Így a kimenet így fog kinézni:
Hi, I am Peter.
Hello. I am John.
Hi, I am Mary.
{ifset}
Lásd még {ifset $var}
A {ifset blockname}
teszt segítségével ellenőrizheti, hogy egy blokk (vagy több blokk) létezik-e az
aktuális kontextusban:
{ifset footer}
...
{/ifset}
{ifset footer, header, main}
...
{/ifset}
A blokk neveként használhat egy változót vagy bármely PHP kifejezését. Ebben az esetben a változó elé írja be a
block
kulcsszót, hogy egyértelmű legyen, hogy nem a változót ellenőrzi:
{ifset block $name}
...
{/ifset}
A blokkok meglétét szintén a hasBlock()
:
{if hasBlock(header) || hasBlock(footer)}
...
{/if}
Tippek
Íme néhány tipp a blokkokkal való munkához:
- Az utolsó felső szintű blokknak nem kell záró taggel rendelkeznie (a blokk a dokumentum végével ér véget). Ez leegyszerűsíti a gyermek sablonok írását, amelyek egy elsődleges blokk.
- Az extra olvashatóság érdekében opcionálisan nevet adhat a
{/block}
tagnek, például{/block footer}
. A névnek azonban meg kell egyeznie a blokk nevével. Nagyobb sablonoknál ez a technika segít abban, hogy lássa, mely blokkcímkéket zárja le. - Egy sablonban közvetlenül nem definiálhat több azonos nevű blokkcímkét. Ez azonban dinamikus blokknevek használatával megvalósítható.
- Az n:attribútumokat használhatja a blokkok
definiálására, mint például
<h1 n:block=title>Welcome to my awesome homepage</h1>
- A blokkok nevek nélkül is használhatók, csak a szűrők
kimenetre történő alkalmazására:
{block|strip} hello {/block}
Vízszintes újrafelhasználás {import}
A horizontális újrafelhasználás a Latte harmadik újrafelhasználási és öröklési mechanizmusa. Lehetővé teszi a
blokkok betöltését más sablonokból. Ez hasonló ahhoz, mintha PHP-ben létrehoznánk egy fájlt segédfüggvényekkel, majd
betöltenénk a require
segítségével.
Bár a sablonok elrendezésének öröklése a Latte egyik legerősebb funkciója, az egyszerű öröklé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és elérésének.
Legyen egy sor blokkdefiníció:
{block sidebar}...{/block}
{block menu}...{/block}
A {import}
paranccsal importáljuk a blocks.latte
alatt definiált összes blokkot és definíciót egy másik sablonba:
{import 'blocks.latte'}
{* oldalsáv és menü blokkok mostantól használhatók *}
Ha a blokkokat a szülő sablonba importálja (azaz a {import}
parancsot használja a layout.latte
),
akkor a blokkok az összes gyermek sablonban is elérhetőek lesznek, ami nagyon praktikus.
Az importálni kívánt sablon (pl. blocks.latte
) nem bővíthet ki egy
másik sablont, azaz használja a {layout}
. Más sablonokat azonban importálhat.
A {import}
címkének kell lennie az első sabloncímkének a {layout}
után. A sablon neve
bármilyen PHP-kifejezés lehet:
{import $ajax ? 'ajax.latte' : 'not-ajax.latte'}
Annyi {import}
utasítást használhat egy adott sablonban, amennyit csak akar. Ha két importált sablon ugyanazt
a blokkot definiálja, akkor az első nyer. A legnagyobb prioritást azonban a fő sablon kapja, amely bármelyik importált
blokkot felülírhatja.
A felülírt blokkok tartalma megőrizhető a blokknak a szülő blokkhoz hasonló módon történő beillesztésével:
{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}
helyesen hívja meg a sidebar
blokkot a
blocks.latte
sablonból.
Egység öröklődése {embed}
Az egység-öröklés az elrendezés öröklés gondolatát a tartalomrészletek szintjére viszi át. Míg az elrendezésöröklés “dokumentumvázakkal” dolgozik, amelyeket a gyermeksablonok hívnak életre, addig az egységöröklés lehetővé teszi, hogy kisebb tartalmi egységek vázait hozza létre, és azokat bárhol újra felhasználja.
Az egységöröklésben a {embed}
címke a kulcs. Egyesíti a {include}
és a {layout}
viselkedését. Lehetővé teszi egy másik sablon vagy blokk tartalmának beépítését, és opcionálisan változók
átadását, ahogyan a {include}
is teszi. Azt is lehetővé teszi, hogy felülírja a bevont sablonon belül
definiált blokkokat, mint a {layout}
.
Példának okáért az összecsukható harmonika elemet fogjuk használni. Nézzük meg az elem vázát a
collapsible.latte
sablonban :
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}{/block}
</h4>
<div class="collapsible__content">
{block content}{/block}
</div>
</section>
A {block}
címkék két blokkot határoznak meg, amelyeket a gyermek sablonok tölthetnek ki. Igen, mint a szülő
sablon esetében az elrendezés öröklés sablonban. A $modifierClass
változót is láthatja.
Használjuk az elemünket a sablonban. Itt jön a képbe a {embed}
. Ez egy szuper erős készlet, amivel mindent
megtehetünk: bevonhatjuk az elem sablonjának tartalmát, hozzáadhatunk változókat, és hozzáadhatunk blokkokat egyéni
HTML-ekkel:
{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ágyazási címkéken belüli blokkok a többi blokkoktól független, külön réteget alkotnak. Ezért lehet ugyanolyan
nevük, mint a beágyazáson kívüli blokknak, és semmilyen módon nem befolyásolják őket. A {embed}
címkéken
belüli include tag használatával beilleszthetünk itt létrehozott blokkokat, a beágyazott
sablonból származó blokkokat (amelyek nem lokálisak), és a fő sablonból származó
blokkokat is, amelyek helyesek. Más fájlokból is importálhatsz 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 a beágyazáson belül van definiálva *}
{include hello} {* működik, a blokk ebben a sablonban helyi *}
{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 a globális változókhoz igen.
A {embed}
segítségével nemcsak sablonokat, hanem más blokkokat is beilleszthetünk, így az előző példát
így is meg 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 átadunk egy kifejezést a {embed}
címre, és nem egyértelmű, hogy az egy blokk vagy fájlnév, akkor adjuk
hozzá a block
vagy a file
kulcsszót:
{embed block $name} ... {/embed}
Felhasználási esetek
A Latte-ban az öröklésnek és a kód újrafelhasználásának különböző típusai vannak. Foglaljuk össze a főbb fogalmakat a nagyobb áttekinthetőség érdekében:
{include template}
Használati eset: A header.latte
& footer.latte
használata a layout.latte
oldalon belül.
header.latte
<nav>
<div>Home</div>
<div>About</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: A layout.latte
kiterjesztése a homepage.latte
és a
about.latte
oldalakon belül.
layout.latte
{include 'header.latte'}
<main>{block main}{/block}</main>
{include 'footer.latte'}
homepage.latte
{layout 'layout.latte'}
{block main}
<p>Homepage</p>
{/block}
about.latte
{layout 'layout.latte'}
{block main}
<p>About page</p>
{/block}
{import}
Használati eset: sidebar.latte
a single.product.latte
és single.service.latte
oldalon.
sidebar.latte
{block sidebar}<aside>This is sidebar</aside>{/block}
single.product.latte
{layout 'product.layout.latte'}
{import 'sidebar.latte'}
{block main}<main>Product page</main>{/block}
single.service.latte
{layout 'service.layout.latte'}
{import 'sidebar.latte'}
{block main}<main>Service page</main>{/block}
{define}
Használati eset: Egy függvény, amely megkap néhány változót és kiad néhány jelölést.
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: A pagination.latte
beágyazása a product.table.latte
&
service.table.latte
.
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}First Product Page{/block}
{block last}Last Product Page{/block}
{/embed}
service.table.latte
{embed 'pagination.latte', min: 1, max: $services->count}
{block first}First Service Page{/block}
{block last}Last Service Page{/block}
{/embed}