In questa sede abbiamo già parlato di PHPTal e della semplicità e potenza di questa libreria che permette di utilizzare in PHP il famoso template system di Zope. L'autore della libreria non si è comunque limitato ad effettuare il porting della libreria di base e, per completezza, ha incluso anche i namespace ed i plugin aggiuntivi che hanno reso questo strumento una delle alternative più interessanti nell'ambito dei template engine.
METAL è uno di questi namespace e permette di aggiungere ai propri template il supporto per le macro. Le macro si comportano in modo molto simile alle funzioni; sono blocchi di codice TAL che possono essere richiamati da altre template e possono accettare dei parametri in ingresso.
Gli attributi di metal
Il namespace Metal aggiunge alcuni attributi molto interessanti a TAL, analizziamoli in dettaglio con qualche esempio:
metal:define-marco
Come già facilmente deducibile dal nome, questo attributo è semplicemente utilizzato per dichiarare una macro. Come già accennato in precedenza una macro è come una funzione o, più contestualmente un blocco di template, che è possibile richiamare in altre sedi senza doversi preoccupare di ripetere ogni volta il codice scritto ottenendo una buona riduzione delle operazioni ripetitive. Le macro ereditano le variabili utilizzabili dal contesto in cui sono richiamate.
<div metal:define-macro="user_box">
<ul>
<li tal:content="user/username">Username</li>
<a href="/edit_user">Modifica utente</a>
</ul>
<div>
Ultimo accesso: <span tal:content="user/last_access">Data dell'ultimo accesso</span>
</div>
</div>
Quando l'engine di PHPTal incontra un tag che contiene l'attributo metal:define-macro
non effettua il rendering del codice in esso contenuto ma si limita a salvarlo in memoria in modo che sia possibile richiamarlo successivamente all'interno del template.
metal:use-macro
Con questo attributo si utilizza una macro definita in precedenza attraverso metal:define-macro
. Il contenuto della macro richiamata viene quindi incluso come contenuto del tag che richiama la macro. L'attributo metal:use-macro
accetta come valore il nome della macro da richiamare; è possibile anche richiamare macro definite in altri file utilizzando come valore il nome del file da cui recuperare la macro seguito da uno slash ('/
')e dal nome della macro stessa.
<!--
Presupponiamo che alla template sia assegnato l'array user che
contiene i dati sull'utente corrente
-->
<div metal:define-macro="menu">
<ul>
<li><a href="#">Item 1</a></li>
<li><a href="#" tal:content="custom_item">Item 2</a></li>
</ul>
</div>
<div metal:use-macro="macros.html/user_box" />
<div tal:define="custom_item string:voce custom"
metal:use-macro="menu" />
Ovviamente il valore dell'attributo può anche contenere delle variabili, richiamate usando la shortcut dell'operatore path: (${ ... }
) che abbiamo visto nell'articolo precedente.
metal:define-slot
Degli slot in PHPTal sono delle porzioni di template che possono essere sostituite dinamicamente in fase di esecuzione da arbitrario codice XML. Uno slot deve essere obbligatoriamente definito all'interno della definizione di una macro. Utilizzando il sistema degli slot è possibile creare macro facilmente portabili tra i sistemi che si vanno a sviluppare.
<div metal:define-macro="top_box">
<div metal:define-slot="news_slot">
<h3>Ultime news</h3>
<ul tal:repeat="news latest_news">
<li href="#" tal:content="news/title" tal:attribute="href news/url">News title</li>
</ul>
</div>
</div>
metal:fill-slot
Con questo attributo invece ci si occupa di utilizzare uno slot definito in precedenza all'interno di una macro e di riempirlo con del codice XML.
<div metal:use-macro="boxes.html/top_box">
<div tal:condition="not: exists: show_news | exists: user" metal:fill-slot="news_slot">
Benvenuto <b>${user/surname} ${user/name}</b>,<br />
guarda le <a href="/show_news">ultime news</a>.
</div>
</div>
Utilizzando il codice precedente otterremo un risultato differente in base al fatto che sia definita la variabile user
oppure show_news
. Nel caso in cui non sia definito nulla oppure nel caso in cui sia definita la variabile show_news
, verrà semplicemente valutata la macro normalmente, e quindi il contenuto del div
verrà sostituito con l'elenco delle ultime news. Altrimenti, verrà visualizzato un semplice box contenente i dati dell'utente al posto dello slot che avrebbe dovuto contenere le news.
Utilizzando metal:fill-slot
viene solamente sostituito il codice dello slot, mentre il resto della macro viene lasciato inalterato.
Un esempio completo
Vediamo ora un esempio completo in cui utilizzeremo il namespace Metal all'interno dei nostri template PHPTal in modo da rendere il codice riutilizzabile in altre situazioni. Quello che vogliamo fare è visualizzare una lista di semplici messaggi ed una lista news prelevate da un altro sito internet; nel caso in cui l'utente fosse loggato come amministratore visualizzeremo anche dei link per modificare i post ed al posto delle news un menu a tendina per scegliere la fonte di queste ultime. Lo script non sarà completo, ma verranno evidenziate solamente le parti che più ci interessano per comprendere il funzionamento di PHPTal.
Listato 1: il file index.php
<?php
require_once 'PHPTAL.php';
require_once 'mylib.php';
$template = new PHPTAL('template.html');
$template->messages = getMessages();
$template->latest_news = getNews();
$user = getUser();
if(!is_null($user))
{
$template->user = $user;
$template->sources = getSources();
}
try {
echo $template->execute();
}
catch (Exception $e){
echo $e;
}
?>
Il file index.php
PHP include una libreria (mylib.php
) che definisce le funzioni utilizzate nel codice per recuperare i dati necessari per il template.
Listato 2: il file template.html
<html>
<head>
<title>Test PHPTAL</title>
</head>
<body>
<div metal:use-macro="macros.html/news_box">
<div tal:condition="exists: user" metal:fill-slot="news_list">
<form action="/change_source">
<select name="source">
<option value="#"
tal:repeat="source sources"
tal:attributes="value source/url"
tal:content="source/title">Source</option>
</select>
<input type="submit" value="salva" />
</form>
</div>
</div>
<div tal:repeat="message messages">
<div>
<h3 tal:content="message/title">Titolo</h3>
<p tal:content="message/content">Contenuto</p>
</div>
<div tal:condition="exists: user">
<a href="/edit/${message/id}">Modifica messaggio</a>
</div>
</div>
</body>
</html>
Il file template.html
visualizza la lista dei messaggi recuperati ed include il risultato della macro news_box
(definita nel file macros.html
) prima di questi. Nel caso in cui l'utente sia connesso viene visualizzato anche un link per la modifica del messaggio e viene riempito lo slot news_list
con una select in cui scegliere la sorgente delle ultime news.
Listato 3: il file macros.html
<div metal:define-macro="news_box">
<h3>Ultime news</h3>
<ul metal:define-slot="news_list">
<li tal:repeat="news latest_news">
<a href="#" tal:attributes="href news/url" tal:content="news/title">News 1</a>
</li>
</ul>
</div>
Infine il file macros.html
dove è definita l'unica macro che utilizziamo nel nostro script di prova.
Per semplicità e per permettervi di anche di testare lo script presentato, includo una semplice implementazione del file mylib.php
che si occupa di definire le funzioni che restituiscono gli array associativi utilizzati in fase di rendering.
Listato 4: il file mylib.php
<?php
function getMessages()
{
return array(
array('id' => 1, 'title' => 'titolo 1', 'content' => 'contenuto 1'),
array('id' => 2, 'title' => 'titolo 2', 'content' => 'contenuto 2'),
array('id' => 3, 'title' => 'titolo 3', 'content' => 'contenuto 3'),
array('id' => 4, 'title' => 'titolo 4', 'content' => 'contenuto 4')
);
}
function getUser()
{
return null;
}
function getNews()
{
return array(
array('id' => 1, 'title' => 'titolo 1', 'url' => 'url 1'),
array('id' => 2, 'title' => 'titolo 2', 'url' => 'url 2')
);
}
function getSources()
{
return array(
array('id' => 1, 'title' => 'titolo 1', 'url' => 'url 1'),
array('id' => 2, 'title' => 'titolo 2', 'url' => 'url 2')
);
}
?>
Modificando la funzione getUser
in modo da fargli restituire un valore vero, è possibile testare il comportamento del template nel caso in cui venga renderizzata quando un utente è loggato.
Conclusione
Con questo articolo chiudo la mini serie dedicata a PHPTAL nell'esempio mostrato poco fa è stato evidente come la scrittura di template risulti molto semplice usando questo template engine e come i namespace aggiuntivi donino completezza e potenza al sistema. Ci sono altri namespace che non ho trattato ed a cui probabilmente darò spazio nei prossimi articoli. Per chi ne fosse interessato comunque consiglio la lettura della documentazione che, anche se non molto completa, offre comunque una buona panoramica delle funzionalità.