Dedovanje in ponovna uporabnost predlog
Mehanizmi ponovne uporabe in dedovanja predlog bodo povečali vašo produktivnost, saj vsaka predloga vsebuje le svojo edinstveno vsebino, ponavljajoči se elementi in strukture pa se ponovno uporabijo. Predstavljamo tri koncepte: Dedovanje postavitve, Horizontalna ponovna uporabnost in Dedovanje enot.
Koncept dedovanja predlog Latte je podoben dedovanju razredov v PHP. Definirate nadrejeno predlogo, od katere lahko dedujejo druge podrejene predloge in lahko prepišejo dele nadrejene predloge. To deluje odlično, ko elementi delijo skupno strukturo. Zveni zapleteno? Ne skrbite, zelo enostavno je.
Dedovanje postavitve {layout}
Poglejmo si dedovanje predloge postavitve, torej layouta, kar s primerom. To je nadrejena predloga, ki jo bomo imenovali na
primer layout.latte
in ki definira okostje HTML dokumenta:
<!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>
Značke {block}
definirajo tri bloke, ki jih lahko podrejene predloge izpolnijo. Značka block naredi le to, da
sporoči, da lahko podrejena predloga to mesto prepiše z definiranjem lastnega bloka z enakim imenom.
Podrejena predloga lahko izgleda takole:
{layout 'layout.latte'}
{block title}My amazing blog{/block}
{block content}
<p>Welcome to my awesome homepage.</p>
{/block}
Ključna je tukaj značka {layout}
. Sporoča Latte, da ta predloga »razširja« drugo predlogo. Ko Latte izrisuje
to predlogo, najprej najde nadrejeno predlogo – v tem primeru layout.latte
.
V tem trenutku si Latte opazi tri blokovne značke v layout.latte
in te bloke nadomesti z vsebino podrejene
predloge. Ker podrejena predloga ni definirala bloka footer, se namesto tega uporabi vsebina iz nadrejene predloge. Vsebina
v znački {block}
v nadrejeni predlogi se vedno uporablja kot rezervna.
Izpis lahko izgleda takole:
<!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>
V podrejeni predlogi so lahko bloki postavljeni le na najvišji ravni ali znotraj drugega bloka, tj.:
{block content}
<h1>{block title}Welcome to my awesome homepage{/block}</h1>
{/block}
Prav tako bo blok vedno ustvarjen, ne glede na to, ali je okoliška {if}
pogoj ocenjen kot resničen ali
neresničen. Torej, čeprav ne izgleda tako, ta predloga blok definira.
{if false}
{block head}
<meta name="robots" content="noindex, follow">
{/block}
{/if}
Če želite, da se izpis znotraj bloka prikazuje pogojno, uporabite namesto tega naslednje:
{block head}
{if $condition}
<meta name="robots" content="noindex, follow">
{/if}
{/block}
Prostor izven blokov v podrejeni predlogi se izvaja pred izrisovanjem predloge layouta, zato ga lahko uporabite za definiranje
spremenljivk kot {var $foo = bar}
in za širjenje podatkov po celotni verigi dedovanja:
{layout 'layout.latte'}
{var $robots = noindex}
...
Večnivojsko dedovanje
Uporabite lahko toliko ravni dedovanja, kolikor jih potrebujete. Pogost način uporabe dedovanja postavitve je naslednji tristopenjski pristop:
- Ustvarite predlogo
layout.latte
, ki vsebuje glavno okostje videza spletnega mesta. - Ustvarite predlogo
layout-SECTIONNAME.latte
za vsak odsek vašega spletnega mesta. Na primerlayout-news.latte
,layout-blog.latte
itd. Vse te predloge razširjajolayout.latte
in vključujejo sloge & dizajn, specifične za posamezne odseke. - Ustvarite posamezne predloge za vsak tip strani, na primer novičarski članek ali vnos v blog. Te predloge razširjajo ustrezno predlogo odseka.
Dinamično dedovanje
Kot ime nadrejene predloge lahko uporabite spremenljivko ali kateri koli izraz PHP, zato se lahko dedovanje obnaša dinamično:
{layout $standalone ? 'minimum.latte' : 'layout.latte'}
Lahko uporabite tudi Latte API za samodejno izbiro predloge layouta.
Nasveti
Tukaj je nekaj nasvetov za delo z dedovanjem postavitve:
- Če v predlogi uporabite
{layout}
, mora biti to prva značka predloge v tej predlogi. - Layout se lahko samodejno poišče (kot
na primer v presenterjih). V tem primeru, če
predloga ne sme imeti layouta, to sporoči z značko
{layout none}
. - Značka
{layout}
ima alias{extends}
. - Ime datoteke layouta je odvisno od loaderja.
- Lahko imate toliko blokov, kolikor želite. Ne pozabite, da podrejene predloge ne rabijo definirati vseh nadrejenih blokov, zato lahko izpolnite primerne privzete vrednosti v več blokih in nato definirate le tiste, ki jih potrebujete kasneje.
Bloki {block}
Glejte tudi anonimni {block}
Blok predstavlja način, kako spremeniti način izrisovanja določenega dela predloge, vendar nikakor ne posega v logiko okoli njega. V naslednjem primeru si bomo pokazali, kako blok deluje, pa tudi kako ne deluje:
{foreach $posts as $post}
{block post}
<h1>{$post->title}</h1>
<p>{$post->body}</p>
{/block}
{/foreach}
Če to predlogo izrišete, bo rezultat popolnoma enak z značkami {block}
ali brez njih. Bloki imajo dostop do
spremenljivk iz zunanjih obsegov. Le dajejo možnost, da jih prepiše podrejena predloga:
{layout 'parent.Latte'}
{block post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/block}
Zdaj bo pri izrisovanju podrejene predloge zanka uporabljala blok, definiran v podrejeni predlogi child.Latte
,
namesto bloka, definiranega v parent.Latte
; zagnana predloga je potem ekvivalentna naslednji:
{foreach $posts as $post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/foreach}
Če pa ustvarimo novo spremenljivko znotraj poimenovanega bloka ali nadomestimo vrednost obstoječe, bo sprememba vidna le znotraj bloka:
{var $foo = 'foo'}
{block post}
{do $foo = 'new value'}
{var $bar = 'bar'}
{/block}
foo: {$foo} // izpiše: foo
bar: {$bar ?? 'not defined'} // izpiše: ni definirano
Vsebino bloka lahko uredite s pomočjo filtrov. Naslednji primer odstrani ves HTML in spremeni velikost črk:
<title>{block title|stripHtml|capitalize}...{/block}</title>
Značko lahko zapišete tudi kot n:atribut:
<article n:block=post>
...
</article>
Lokalni bloki
Vsak blok prepiše vsebino nadrejenega bloka z enakim imenom – razen lokalnih blokov. V razredih bi šlo za nekaj podobnega kot zasebne metode. Predlogo lahko tako ustvarjate brez skrbi, da bi bili zaradi ujemanja imen blokov prepisani iz druge predloge.
{block local helper}
...
{/block}
Izrisovanje blokov {include}
Glejte tudi {include file}
Če želite blok izpisati na določenem mestu, uporabite značko {include blockname}
:
<title>{block title}{/block}</title>
<h1>{include title}</h1>
Lahko izpišete tudi blok iz druge predloge:
{include footer from 'main.latte'}
Izrisovani blok nima dostopa do spremenljivk aktivnega konteksta, razen če je blok definiran v isti datoteki, kjer je tudi vstavljen. Ima pa dostop do globalnih spremenljivk.
Spremenljivke lahko v blok posredujete na ta način:
{include footer, foo: bar, id: 123}
Kot ime bloka lahko uporabite spremenljivko ali kateri koli izraz v PHP. V tem primeru pred spremenljivko še dopolnimo
ključno besedo block
, da bo že v času prevajanja Latte vedelo, da gre za blok, in ne za vstavljanje predloge, katere ime bi prav tako lahko bilo
v spremenljivki:
{var $name = footer}
{include block $name}
Blok lahko izrišete tudi znotraj sebe, kar je na primer koristno pri izrisovanju drevesne strukture:
{define menu, $items}
<ul>
{foreach $items as $item}
<li>
{if is_array($item)}
{include menu, $item}
{else}
{$item}
{/if}
</li>
{/foreach}
</ul>
{/define}
Namesto {include menu, ...}
lahko potem napišemo {include this, ...}
, kjer this
pomeni
trenutni blok.
Izrisovani blok lahko uredite s pomočjo filtrov. Naslednji primer odstrani ves HTML in spremeni velikost črk:
<title>{include heading|stripHtml|capitalize}</title>
Starševski blok
Če potrebujete izpisati vsebino bloka iz nadrejene predloge, uporabite {include parent}
. To je koristno, če
želite le dopolniti vsebino nadrejenega bloka namesto njegovega popolnega prepisa.
{block footer}
{include parent}
<a href="https://github.com/nette">GitHub</a>
<a href="https://twitter.com/nettefw">Twitter</a>
{/block}
Definicije {define}
Poleg blokov obstajajo v Latte tudi »definicije«. V običajnih programskih jezikih bi jih primerjali s funkcijami. Koristne so za ponovno uporabo fragmentov predloge, da se ne ponavljate.
Latte si prizadeva narediti stvari enostavne, zato so v osnovi definicije enake kot bloki in vse, kar je povedano o blokih, velja tudi za definicije. Od blokov se razlikujejo po tem, da:
- so zaprte v značkah
{define}
- se izrišejo šele, ko jih vstavite preko
{include}
- jim lahko definirate parametre podobno kot funkcijam v PHP
{block foo}<p>Hello</p>{/block}
{* izpiše: <p>Hello</p> *}
{define bar}<p>World</p>{/define}
{* ne izpiše ničesar *}
{include bar}
{* izpiše: <p>World</p> *}
Predstavljajte si, da imate pomožno predlogo z zbirko definicij, kako risati HTML obrazce.
{define input, $name, $value, $type = 'text'}
<input type={$type} name={$name} value={$value}>
{/define}
{define textarea, $name, $value}
<textarea name={$name}>{$value}</textarea>
{/define}
Argumenti so vedno izbirni s privzeto vrednostjo null
, če ni navedena privzeta vrednost (tukaj je
'text'
privzeta vrednost za $type
). Lahko deklarirate tudi tipe parametrov:
{define input, string $name, ...}
.
Predlogo z definicijami naložimo s pomočjo {import}
. Same
definicije se izrisujejo na enak način kot bloki:
<p>{include input, 'password', null, 'password'}</p>
<p>{include textarea, 'comment'}</p>
Definicije nimajo dostopa do spremenljivk aktivnega konteksta, imajo pa dostop do globalnih spremenljivk.
Dinamična imena blokov
Latte dovoljuje veliko fleksibilnost pri definiranju blokov, saj je lahko ime bloka kateri koli izraz PHP. Ta primer definira
tri bloke z imeni hi-Peter
, hi-John
in hi-Mary
:
{foreach [Peter, John, Mary] as $name}
{block "hi-$name"}Hi, I am {$name}.{/block}
{/foreach}
V podrejeni predlogi lahko potem ponovno definiramo na primer le en blok:
{block hi-John}Hello. I am {$name}.{/block}
Torej bo izpis izgledal takole:
Hi, I am Peter.
Hello. I am John.
Hi, I am Mary.
Preverjanje obstoja blokov {ifset}
Glejte tudi {ifset $var}
S pomočjo testa {ifset blockname}
preverimo, ali v trenutnem kontekstu blok (ali več blokov) obstaja:
{ifset footer}
...
{/ifset}
{ifset footer, header, main}
...
{/ifset}
Kot ime bloka lahko uporabite spremenljivko ali kateri koli izraz v PHP. V tem primeru pred spremenljivko še dopolnimo
ključno besedo block
, da bo jasno, da ne gre za test obstoja spremenljivk:
{ifset block $name}
...
{/ifset}
Obstoj blokov preverja tudi funkcija hasBlock()
:
{if hasBlock(header) || hasBlock(footer)}
...
{/if}
Nasveti
Nekaj nasvetov za delo z bloki:
- Zadnji blok najvišje ravni ne rabi imeti zaključne značke (blok se konča s koncem dokumenta). To poenostavlja pisanje podrejenih predlog, ki vsebujejo en primarni blok.
- Za boljšo berljivost lahko ime bloka navedete v znački
{/block}
, na primer{/block footer}
. Vendar se mora ime ujemati z imenom bloka. V večjih predlogah vam ta tehnika pomaga ugotoviti, katere značke bloka se zapirajo. - V isti predlogi ne morete neposredno definirati več značk blokov z enakim imenom. To pa lahko dosežete s pomočjo dinamičnih imen blokov.
- Lahko uporabite n:atribute za definiranje blokov
kot
<h1 n:block=title>Welcome to my awesome homepage</h1>
- Bloke lahko uporabite tudi brez imen samo za uporabo filtrov:
{block|strip} hello {/block}
Horizontalna ponovna uporabnost {import}
Horizontalna ponovna uporabnost je v Latte tretji mehanizem za ponovno uporabo in dedovanje. Omogoča nalaganje blokov iz
drugih predlog. Podobno je, kot če si v PHP ustvarimo datoteko s pomožnimi funkcijami, ki jo nato naložimo s pomočjo
require
.
Čeprav je dedovanje postavitve predloge ena najmočnejših funkcij Latte, je omejena na enostavno dedovanje – predloga lahko razširi le eno drugo predlogo. Horizontalna ponovna uporabnost je način, kako doseči večkratno dedovanje.
Imejmo datoteko z definicijami blokov:
{block sidebar}...{/block}
{block menu}...{/block}
S pomočjo ukaza {import}
uvozimo vse bloke in Definicije definirane v
blocks.latte
v drugo predlogo:
{import 'blocks.latte'}
{* zdaj je mogoče uporabiti bloka sidebar in menu *}
Če bloke uvozite v nadrejeni predlogi (tj. uporabite {import}
v layout.latte
), bodo bloki na voljo
tudi v vseh podrejenih predlogah, kar je zelo praktično.
Predloga, ki je namenjena uvozu (npr. blocks.latte
), ne sme razširjati
druge predloge, tj. uporabljati {layout}
. Lahko pa uvaža druge predloge.
Značka {import}
bi morala biti prva značka predloge po {layout}
. Ime predloge je lahko kateri koli
izraz PHP:
{import $ajax ? 'ajax.latte' : 'not-ajax.latte'}
V predlogi lahko uporabite toliko {import}
ukazov, kolikor želite. Če dve uvoženi predlogi definirata isti
blok, zmaga prva. Najvišjo prioriteto pa ima glavna predloga, ki lahko prepiše kateri koli uvožen blok.
Vsebino prepisanih blokov lahko ohranite tako, da blok vstavimo enako, kot se vstavlja Starševski blok:
{layout 'layout.latte'}
{import 'blocks.latte'}
{block sidebar}
{include parent}
{/block}
{block title}...{/block}
{block content}...{/block}
V tem primeru {include parent}
pokliče blok sidebar
iz predloge blocks.latte
.
Dedovanje enot {embed}
Dedovanje enot razširja idejo dedovanja postavitve na raven fragmentov vsebine. Medtem ko dedovanje postavitve dela z »okostjem dokumenta«, ki ga oživijo podrejene predloge, vam dedovanje enot omogoča ustvarjanje okostij za manjše enote vsebine in jih ponovno uporabljate kjerkoli želite.
V dedovanju enot je ključna značka {embed}
. Kombinira obnašanje {include}
in
{layout}
. Omogoča vstavljanje vsebine druge predloge ali bloka in izbirno posredovanje spremenljivk, enako kot
v primeru {include}
. Omogoča tudi prepis katerega koli bloka, definiranega znotraj vstavljene predloge, kot pri
uporabi {layout}
.
Na primer, uporabimo element harmonika. Poglejmo si okostje elementa, shranjeno v predlogi collapsible.latte
:
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}{/block}
</h4>
<div class="collapsible__content">
{block content}{/block}
</div>
</section>
Značke {block}
definirajo dva bloka, ki jih lahko podrejene predloge izpolnijo. Da, kot v primeru nadrejene
predloge pri dedovanju postavitve. Vidite tudi spremenljivko $modifierClass
.
Uporabimo naš element v predlogi. Tukaj pride v poštev {embed}
. Gre za izjemno zmogljivo značko, ki nam
omogoča narediti vse stvari: vstaviti vsebino predloge elementa, dodati vanjo spremenljivke in dodati vanjo bloke
z lastnim HTML:
{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}
Izpis lahko izgleda takole:
<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>
Bloki znotraj vstavljenih značk tvorijo samostojno plast, neodvisno od drugih blokov. Zato lahko imajo enako ime kot blok
izven vstavljanja in niso nikakor vplivani. S pomočjo značke include znotraj značk
{embed}
lahko vstavite bloke, tukaj ustvarjene, bloke iz vstavljene predloge (ki niso lokalni) in tudi bloke iz glavne predloge, ki pa nasprotno so lokalni. Lahko tudi uvažate bloke iz drugih datotek:
{block outer}…{/block}
{block local hello}…{/block}
{embed 'collapsible.latte', modifierClass: my-style}
{import 'blocks.latte'}
{block inner}…{/block}
{block title}
{include inner} {* deluje, blok je definiran znotraj embed *}
{include hello} {* deluje, blok je lokalen v tej predlogi *}
{include content} {* deluje, blok je definiran v vstavljeni predlogi *}
{include aBlockDefinedInImportedTemplate} {* deluje *}
{include outer} {* ne deluje! - blok je v zunanji plasti *}
{/block}
{/embed}
Vstavljene predloge nimajo dostopa do spremenljivk aktivnega konteksta, imajo pa dostop do globalnih spremenljivk.
S pomočjo {embed}
lahko vstavljate ne le predloge, ampak tudi druge bloke, in torej bi se prejšnji primer lahko
zapisal na ta način:
{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}
Če v {embed}
posredujemo izraz in ni očitno, ali gre za ime bloka ali datoteke, dopolnimo ključno besedo
block
ali file
:
{embed block $name} ... {/embed}
Primeri uporabe
V Latte obstajajo različni tipi dedovanja in ponovne uporabe kode. Povzemimo glavne koncepte za večjo razumljivost:
{include template}
Primer uporabe: Uporaba header.latte
in footer.latte
znotraj layout.latte
.
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}
Primer uporabe: Razširitev layout.latte
znotraj homepage.latte
in
about.latte
.
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}
Primer uporabe: sidebar.latte
v single.product.latte
in single.service.latte
.
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}
Primer uporabe: Funkcije, ki jim posredujemo spremenljivke in nekaj izrišejo.
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}
Primer uporabe: Vstavljanje pagination.latte
v product.table.latte
in
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}