Moștenirea și reutilizarea șabloanelor
Mecanismele de reutilizare și moștenire a șabloanelor vă vor crește productivitatea, deoarece fiecare șablon conține doar conținutul său unic, iar elementele și structurile repetitive sunt reutilizate. Vă prezentăm trei concepte: Moștenirea layout-ului, Reutilizarea orizontală și Moștenirea unitară.
Conceptul de moștenire a șabloanelor Latte este similar cu moștenirea claselor în PHP. Definiți un șablon părinte, de la care pot moșteni alte șabloane copil și pot suprascrie părți ale șablonului părinte. Funcționează excelent atunci când elementele partajează o structură comună. Sună complicat? Nu vă faceți griji, este foarte ușor.
Moștenirea layout-ului {layout}
Să vedem moștenirea șablonului de layout, adică a layout-ului, direct printr-un exemplu. Acesta este șablonul părinte, pe
care îl vom numi, de exemplu, layout.latte
și care definește scheletul documentului HTML:
<!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>
Tag-urile {block}
definesc trei blocuri pe care șabloanele copil le pot completa. Tag-ul block face doar atât:
anunță că acest loc poate fi suprascris de șablonul copil prin definirea propriului bloc cu același nume.
Un șablon copil poate arăta astfel:
{layout 'layout.latte'}
{block title}Blogul meu uimitor{/block}
{block content}
<p>Bun venit pe pagina mea minunată.</p>
{/block}
Cheia aici este tag-ul {layout}
. Acesta îi spune lui Latte că acest șablon „extinde” un alt șablon. Când
Latte redă acest șablon, mai întâi găsește șablonul părinte – în acest caz layout.latte
.
În acest moment, Latte observă cele trei tag-uri block în layout.latte
și înlocuiește aceste blocuri cu
conținutul șablonului copil. Deoarece șablonul copil nu a definit blocul footer, se va folosi în schimb conținutul din
șablonul părinte. Conținutul din tag-ul {block}
în șablonul părinte este întotdeauna folosit ca
alternativă.
Outputul poate arăta astfel:
<!doctype html>
<html lang="en">
<head>
<title>Blogul meu uimitor</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="content">
<p>Bun venit pe pagina mea minunată.</p>
</div>
<div id="footer">
© Copyright 2008
</div>
</body>
</html>
În șablonul copil, blocurile pot fi plasate doar la nivelul superior sau în interiorul altui bloc, adică:
{block content}
<h1>{block title}Bun venit pe pagina mea minunată{/block}</h1>
{/block}
De asemenea, un bloc va fi întotdeauna creat indiferent dacă condiția {if}
înconjurătoare este evaluată ca
adevărată sau falsă. Deci, chiar dacă nu pare așa, acest șablon va defini blocul.
{if false}
{block head}
<meta name="robots" content="noindex, follow">
{/block}
{/if}
Dacă doriți ca outputul din interiorul blocului să fie afișat condiționat, utilizați în schimb următoarele:
{block head}
{if $condition}
<meta name="robots" content="noindex, follow">
{/if}
{/block}
Spațiul din afara blocurilor în șablonul copil este executat înainte de redarea șablonului de layout, astfel încât îl
puteți folosi pentru a defini variabile precum {var $foo = bar}
și pentru a propaga datele în întregul lanț de
moștenire:
{layout 'layout.latte'}
{var $robots = noindex}
...
Moștenire pe mai multe niveluri
Puteți utiliza oricâte niveluri de moștenire aveți nevoie. O modalitate obișnuită de a utiliza moștenirea layout-ului este următoarea abordare pe trei niveluri:
- Creați un șablon
layout.latte
care conține scheletul principal al aspectului site-ului. - Creați un șablon
layout-SECTIONNAME.latte
pentru fiecare secțiune a site-ului dvs. De exemplu,layout-news.latte
,layout-blog.latte
etc. Toate aceste șabloane extindlayout.latte
și includ stiluri & design specifice fiecărei secțiuni. - Creați șabloane individuale pentru fiecare tip de pagină, de exemplu, un articol de știri sau o postare de blog. Aceste șabloane extind șablonul de secțiune corespunzător.
Moștenire dinamică
Ca nume al șablonului părinte se poate folosi o variabilă sau orice expresie PHP, astfel încât moștenirea se poate comporta dinamic:
{layout $standalone ? 'minimum.latte' : 'layout.latte'}
Puteți utiliza, de asemenea, API-ul Latte pentru a selecta automat șablonul de layout.
Sfaturi
Iată câteva sfaturi pentru lucrul cu moștenirea layout-ului:
- Dacă utilizați
{layout}
într-un șablon, acesta trebuie să fie primul tag de șablon din acel șablon. - Layout-ul poate fi căutat automat (așa
cum se întâmplă, de exemplu, în presentere).
În acest caz, dacă șablonul nu trebuie să aibă un layout, acesta o anunță prin tag-ul
{layout none}
. - Tag-ul
{layout}
are un alias{extends}
. - Numele fișierului de layout depinde de loader-ului.
- Puteți avea oricâte blocuri doriți. Amintiți-vă că șabloanele copil nu trebuie să definească toate blocurile părinte, așa că puteți completa valori implicite rezonabile în mai multe blocuri și apoi să definiți doar cele de care aveți nevoie mai târziu.
Blocuri {block}
Vezi și {block}
anonim
Un bloc reprezintă o modalitate de a schimba modul în care este redată o anumită parte a șablonului, dar nu interferează în niciun fel cu logica din jurul său. În exemplul următor, vom arăta cum funcționează un bloc, dar și cum nu funcționează:
{foreach $posts as $post}
{block post}
<h1>{$post->title}</h1>
<p>{$post->body}</p>
{/block}
{/foreach}
Dacă redați acest șablon, rezultatul va fi exact același cu sau fără tag-urile {block}
. Blocurile au acces
la variabilele din domeniile exterioare. Ele doar oferă posibilitatea de a fi suprascrise de un șablon copil:
{layout 'parent.Latte'}
{block post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/block}
Acum, la redarea șablonului copil, bucla va folosi blocul definit în șablonul copil child.Latte
în locul
blocului definit în parent.Latte
; șablonul executat este apoi echivalent cu următorul:
{foreach $posts as $post}
<article>
<header>{$post->title}</header>
<section>{$post->text}</section>
</article>
{/foreach}
Cu toate acestea, dacă creăm o nouă variabilă în interiorul unui bloc numit sau înlocuim valoarea uneia existente, modificarea va fi vizibilă doar în interiorul blocului:
{var $foo = 'foo'}
{block post}
{do $foo = 'valoare nouă'}
{var $bar = 'bar'}
{/block}
foo: {$foo} // afișează: foo
bar: {$bar ?? 'nedefinit'} // afișează: nedefinit
Conținutul blocului poate fi modificat folosind filtre. Următorul exemplu elimină tot HTML-ul și schimbă majusculele/minusculele:
<title>{block title|stripHtml|capitalize}...{/block}</title>
Tag-ul poate fi scris și ca n:attribut:
<article n:block=post>
...
</article>
Blocuri locale
Fiecare bloc suprascrie conținutul blocului părinte cu același nume – cu excepția blocurilor locale. În clase, acestea ar fi ceva de genul metodelor private. Astfel, puteți crea șablonul fără teama că, din cauza potrivirii numelor blocurilor, acestea ar fi suprascrise din alt șablon.
{block local helper}
...
{/block}
Redarea blocurilor {include}
Vezi și {include file}
Pentru a afișa un bloc într-un anumit loc, utilizați tag-ul {include blockname}
:
<title>{block title}{/block}</title>
<h1>{include title}</h1>
Se poate afișa și un bloc dintr-un alt șablon:
{include footer from 'main.latte'}
Blocul redat nu are acces la variabilele contextului activ, cu excepția cazului în care blocul este definit în același fișier în care este și inclus. Cu toate acestea, are acces la variabilele globale.
Puteți transmite variabile blocului în acest mod:
{include footer, foo: bar, id: 123}
Ca nume de bloc se poate folosi o variabilă sau orice expresie PHP. În acest caz, înainte de variabilă adăugăm cuvântul
cheie block
, pentru ca Latte să știe deja la momentul compilării că este vorba de un bloc și nu de includerea șablonului, al cărui nume ar putea fi, de asemenea,
într-o variabilă:
{var $name = footer}
{include block $name}
Blocul poate fi redat și în interiorul său, ceea ce este util, de exemplu, la redarea unei structuri arborescente:
{define menu, $items}
<ul>
{foreach $items as $item}
<li>
{if is_array($item)}
{include menu, $item}
{else}
{$item}
{/if}
</li>
{/foreach}
</ul>
{/define}
În loc de {include menu, ...}
putem scrie {include this, ...}
, unde this
înseamnă
blocul curent.
Blocul redat poate fi modificat folosind filtre. Următorul exemplu elimină tot HTML-ul și schimbă majusculele/minusculele:
<title>{include heading|stripHtml|capitalize}</title>
Blocul părinte
Dacă aveți nevoie să afișați conținutul unui bloc din șablonul părinte, utilizați {include parent}
. Acest
lucru este util dacă doriți doar să completați conținutul blocului părinte în loc să îl suprascrieți complet.
{block footer}
{include parent}
<a href="https://github.com/nette">GitHub</a>
<a href="https://twitter.com/nettefw">Twitter</a>
{/block}
Definiții {define}
Pe lângă blocuri, în Latte există și „definiții”. În limbajele de programare obișnuite, le-am compara cu funcțiile. Sunt utile pentru reutilizarea fragmentelor de șablon, pentru a nu vă repeta.
Latte încearcă să facă lucrurile simple, așa că, în principiu, definițiile sunt la fel ca blocurile și tot ce s-a spus despre blocuri se aplică și definițiilor. Se diferențiază de blocuri prin faptul că:
- sunt închise în tag-uri
{define}
- se redau doar atunci când le includeți prin
{include}
- li se pot defini parametri similar funcțiilor din PHP
{block foo}<p>Hello</p>{/block}
{* afișează: <p>Hello</p> *}
{define bar}<p>World</p>{/define}
{* nu afișează nimic *}
{include bar}
{* afișează: <p>World</p> *}
Imaginați-vă că aveți un șablon auxiliar cu o colecție de definiții despre cum să desenați formulare HTML.
{define input, $name, $value, $type = 'text'}
<input type={$type} name={$name} value={$value}>
{/define}
{define textarea, $name, $value}
<textarea name={$name}>{$value}</textarea>
{/define}
Argumentele sunt întotdeauna opționale cu valoarea implicită null
, dacă nu este specificată o valoare
implicită (aici 'text'
este valoarea implicită pentru $type
). Se pot declara și tipurile
parametrilor: {define input, string $name, ...}
.
Încărcăm șablonul cu definiții folosind {import}
. Definițiile
în sine se redau în același mod ca blocurile:
<p>{include input, 'password', null, 'password'}</p>
<p>{include textarea, 'comment'}</p>
Definițiile nu au acces la variabilele contextului activ, dar au acces la variabilele globale.
Denumiri dinamice de blocuri
Latte permite o mare flexibilitate în definirea blocurilor, deoarece numele blocului poate fi orice expresie PHP. Acest
exemplu definește trei blocuri cu numele hi-Peter
, hi-John
și hi-Mary
:
{foreach [Peter, John, Mary] as $name}
{block "hi-$name"}Salut, sunt {$name}.{/block}
{/foreach}
În șablonul copil, putem apoi redefini, de exemplu, doar un singur bloc:
{block hi-John}Bună. Sunt {$name}.{/block}
Deci outputul va arăta astfel:
Salut, sunt Peter.
Bună. Sunt John.
Salut, sunt Mary.
Verificarea existenței blocurilor {ifset}
Vezi și {ifset $var}
Folosind testul {ifset blockname}
, verificăm dacă în contextul curent există un bloc (sau mai multe
blocuri):
{ifset footer}
...
{/ifset}
{ifset footer, header, main}
...
{/ifset}
Ca nume de bloc se poate folosi o variabilă sau orice expresie PHP. În acest caz, înainte de variabilă adăugăm cuvântul
cheie block
, pentru a fi clar că nu este vorba de un test de existență a variabilelor:
{ifset block $name}
...
{/ifset}
Existența blocurilor este verificată și de funcția hasBlock()
:
{if hasBlock(header) || hasBlock(footer)}
...
{/if}
Sfaturi
Câteva sfaturi pentru lucrul cu blocurile:
- Ultimul bloc de nivel superior nu trebuie să aibă un tag de închidere (blocul se termină la sfârșitul documentului). Acest lucru simplifică scrierea șabloanelor copil care conțin un singur bloc principal.
- Pentru o mai bună lizibilitate, puteți specifica numele blocului în tag-ul
{/block}
, de exemplu{/block footer}
. Cu toate acestea, numele trebuie să corespundă numelui blocului. În șabloanele mai mari, această tehnică vă ajută să vedeți ce tag-uri de bloc se închid. - Nu puteți defini direct mai multe tag-uri de bloc cu același nume în același șablon. Acest lucru se poate realiza însă folosind denumiri dinamice de blocuri.
- Puteți utiliza n:atribute pentru a defini blocuri
precum
<h1 n:block=title>Bun venit pe pagina mea minunată</h1>
- Blocurile pot fi folosite și fără nume doar pentru a aplica filtre:
{block|strip} hello {/block}
Reutilizarea orizontală {import}
Reutilizarea orizontală este în Latte al treilea mecanism pentru reutilizare și moștenire. Permite încărcarea blocurilor
din alte șabloane. Este similar cu crearea unui fișier cu funcții auxiliare în PHP, pe care apoi îl încărcăm folosind
require
.
Deși moștenirea layout-ului șablonului este una dintre cele mai puternice funcții Latte, este limitată la moștenire simplă – un șablon poate extinde doar un alt șablon. Reutilizarea orizontală este o modalitate de a obține moștenire multiplă.
Să avem un fișier cu definiții de blocuri:
{block sidebar}...{/block}
{block menu}...{/block}
Folosind comanda {import}
, importăm toate blocurile și definițiile definite în
blocks.latte
într-un alt șablon:
{import 'blocks.latte'}
{* acum se pot folosi blocurile sidebar și menu *}
Dacă importați blocurile în șablonul părinte (adică utilizați {import}
în layout.latte
),
blocurile vor fi disponibile și în toate șabloanele copil, ceea ce este foarte practic.
Șablonul destinat importării (de exemplu, blocks.latte
) nu trebuie să extindă un alt șablon, adică să folosească {layout}
. Cu toate acestea, poate
importa alte șabloane.
Tag-ul {import}
ar trebui să fie primul tag de șablon după {layout}
. Numele șablonului poate fi
orice expresie PHP:
{import $ajax ? 'ajax.latte' : 'not-ajax.latte'}
În șablon puteți utiliza oricâte comenzi {import}
doriți. Dacă două șabloane importate definesc același
bloc, primul câștigă. Prioritatea cea mai mare o are însă șablonul principal, care poate suprascrie orice bloc
importat.
Conținutul blocurilor suprascrise poate fi păstrat prin includerea blocului în același mod în care se include blocul părinte:
{layout 'layout.latte'}
{import 'blocks.latte'}
{block sidebar}
{include parent}
{/block}
{block title}...{/block}
{block content}...{/block}
În acest exemplu, {include parent}
va apela blocul sidebar
din șablonul
blocks.latte
.
Moștenirea unitară {embed}
Moștenirea unitară extinde ideea moștenirii layout-ului la nivelul fragmentelor de conținut. În timp ce moștenirea layout-ului lucrează cu „scheletul documentului”, care este animat de șabloanele copil, moștenirea unitară vă permite să creați schelete pentru unități mai mici de conținut și să le reutilizați oriunde doriți.
În moștenirea unitară, cheia este tag-ul {embed}
. Acesta combină comportamentul {include}
și
{layout}
. Permite includerea conținutului unui alt șablon sau bloc și, opțional, transmiterea variabilelor, la
fel ca în cazul {include}
. Permite, de asemenea, suprascrierea oricărui bloc definit în interiorul șablonului
inclus, la fel ca la utilizarea {layout}
.
De exemplu, vom folosi elementul acordeon. Să vedem scheletul elementului stocat în șablonul
collapsible.latte
:
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}{/block}
</h4>
<div class="collapsible__content">
{block content}{/block}
</div>
</section>
Tag-urile {block}
definesc două blocuri pe care șabloanele copil le pot completa. Da, la fel ca în cazul
șablonului părinte în moștenirea layout-ului. Vedeți și variabila $modifierClass
.
Să folosim elementul nostru în șablon. Aici intervine {embed}
. Este un tag extrem de puternic care ne permite
să facem toate lucrurile: să includem conținutul șablonului elementului, să adăugăm variabile în el și să adăugăm
blocuri cu propriul HTML în el:
{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}
Outputul poate arăta astfel:
<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>
Blocurile din interiorul tag-urilor incluse formează un strat separat, independent de celelalte blocuri. Prin urmare, pot avea
același nume ca un bloc din afara includerii și nu sunt afectate în niciun fel. Folosind tag-ul include în interiorul tag-urilor {embed}
, puteți include blocurile create aici,
blocurile din șablonul inclus (care nu sunt locale) și, de asemenea, blocurile din
șablonul principal, care, dimpotrivă, sunt locale. Puteți, de asemenea, importa
blocuri din alte fișiere:
{block outer}…{/block}
{block local hello}…{/block}
{embed 'collapsible.latte', modifierClass: my-style}
{import 'blocks.latte'}
{block inner}…{/block}
{block title}
{include inner} {* funcționează, blocul este definit în interiorul embed *}
{include hello} {* funcționează, blocul este local în acest șablon *}
{include content} {* funcționează, blocul este definit în șablonul inclus *}
{include aBlockDefinedInImportedTemplate} {* funcționează *}
{include outer} {* nu funcționează! - blocul este în stratul exterior *}
{/block}
{/embed}
Șabloanele incluse nu au acces la variabilele contextului activ, dar au acces la variabilele globale.
Folosind {embed}
se pot include nu doar șabloane, ci și alte blocuri, deci exemplul anterior s-ar putea scrie
în acest mod:
{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}
Dacă transmitem o expresie în {embed}
și nu este clar dacă este vorba de numele unui bloc sau al unui
fișier, adăugăm cuvântul cheie block
sau file
:
{embed block $name} ... {/embed}
Cazuri de utilizare
În Latte există diferite tipuri de moștenire și reutilizare a codului. Să rezumăm principalele concepte pentru o mai mare claritate:
{include template}
Caz de utilizare: Utilizarea header.latte
și footer.latte
în interiorul
layout.latte
.
header.latte
<nav>
<div>Acasă</div>
<div>Despre</div>
</nav>
footer.latte
<footer>
<div>Copyright</div>
</footer>
layout.latte
{include 'header.latte'}
<main>{block main}{/block}</main>
{include 'footer.latte'}
{layout}
Caz de utilizare: Extinderea layout.latte
în interiorul homepage.latte
și
about.latte
.
layout.latte
{include 'header.latte'}
<main>{block main}{/block}</main>
{include 'footer.latte'}
homepage.latte
{layout 'layout.latte'}
{block main}
<p>Pagină de pornire</p>
{/block}
about.latte
{layout 'layout.latte'}
{block main}
<p>Pagină despre</p>
{/block}
{import}
Caz de utilizare: sidebar.latte
în single.product.latte
și
single.service.latte
.
sidebar.latte
{block sidebar}<aside>Aceasta este bara laterală</aside>{/block}
single.product.latte
{layout 'product.layout.latte'}
{import 'sidebar.latte'}
{block main}<main>Pagină produs</main>{/block}
single.service.latte
{layout 'service.layout.latte'}
{import 'sidebar.latte'}
{block main}<main>Pagină serviciu</main>{/block}
{define}
Caz de utilizare: Funcții cărora le transmitem variabile și care redau ceva.
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, Trimite, submit}</div>
</form>
{embed}
Caz de utilizare: Includerea pagination.latte
în product.table.latte
și
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}Prima pagină de produse{/block}
{block last}Ultima pagină de produse{/block}
{/embed}
service.table.latte
{embed 'pagination.latte', min: 1, max: $services->count}
{block first}Prima pagină de servicii{/block}
{block last}Ultima pagină de servicii{/block}
{/embed}