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}&copy; 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">
		&copy; 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:

  1. Ustvarite predlogo layout.latte, ki vsebuje glavno okostje videza spletnega mesta.
  2. Ustvarite predlogo layout-SECTIONNAME.latte za vsak odsek vašega spletnega mesta. Na primer layout-news.latte, layout-blog.latte itd. Vse te predloge razširjajo layout.latte in vključujejo sloge & dizajn, specifične za posamezne odseke.
  3. 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:

  1. so zaprte v značkah {define}
  2. se izrišejo šele, ko jih vstavite preko {include}
  3. 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}
različica: 3.0