Development

Documentation/it_IT/book/1.1/07-Inside-the-View-Layer

You must first sign up to be able to contribute.

Version 10 (modified by Fabrizio, 9 years ago)
--

Capitolo 7 - All'Interno del Layer Vista

La view o vista è responsabile della rappresentazione dell'output legato ad una particolare azione. In symfony, la vista è composta da diverse parti, dove ogni componente è progettato per essere facilmente modificabile dalla persona che lavora con essa.

  • I web designer generalmente lavorano sui template (rappresentazione dei dati di output dell'azione corrente) e sul layout (il contenitore del codice comune a tutte le pagine). Questi sono scritti in HTML ed includono piccole porzioni di codice PHP, che principalmente sono chiamate agli helper.
  • Per questioni di riusabilità, gli sviluppatori solitamente raggruppano queste parti di codice in partial o component. Questi utilizzano gli slot e i component slot per modificare una o più zone del layout. Anche i web designer possono lavorare su queste parti di codice.
  • Gli sviluppatori si occupano del file YAML per la configurazione della view (impostando le proprietà della rappresentazione e degli altri elementi dell'interfaccia) e degli oggetti della rappresentazione. Nei template, lavorando con le variabili, il rischio di cross-site scripting non deve essere ignorato e per questo è richiesta una buona conoscenza delle tecniche di escaping così da garantire la sicurezza dei dati dei vostri utenti.

Qualunque sia il tuo ruolo, troverai strumenti utili per velocizzare il noioso lavoro della gestione della rappresentazione dell'output dell'action. Questo capitolo tratterà tutti questi strumenti.

Templating

Il Listato 7-1 mostra un tipico template di symfony. Esso contiene alcune parti di codice HTML e un po' di semplice codice PHP, solitamente chiamate alle variabili definite nella action (attraverso $this->name = 'foo';) ed agli helper.

Listato 7-1 - Esempio di un semplice template per indexSuccess.php

[php]
<h1>Welcome</h1>
<p>Welcome back, <?php echo $name ?>!</p>
<ul>What would you like to do?
  <li><?php echo link_to('Read the last articles', 'article/read') ?></li>
  <li><?php echo link_to('Start writing a new one', 'article/write') ?></li>
</ul>

Come spiegato nel Capitolo 4, nei template è preferibile usare la sintassi PHP alternativa così da renderli più leggibili anche ai non sviluppatori PHP. Dovresti limitare al minimo il codice PHP nei template, poiché questi file sono quelli utilizzati per realizzare la GUI dell'applicazione e potrebbero essere creati e mantenuti da un altro team specializzato nella rappresentazione ma non nell'application logic. Mantenere la logica all'interno dell'azione rende inoltre più semplice avere diversi template per una sola action, senza duplicarne il codice.

Helper

Gli Helper sono funzioni PHP che generano codice HTML e possono essere usati nei template. Nel Listato 7-1, la funzione link_to() è un helper. A volte, gli helper sono dei salva-tempo, pacchettizzando frammenti di codice usati frequentemente nei template. Per esempio puoi facilmente immaginare la definizione della funzione di questo helper:

[php]
<?php echo input_tag('nickname') ?>
 => <input type="text" name="nickname" id="nickname" value="" />

Potrebbe apparire come nel Listato 7-2.

Listato 7-2 - Esempio di un semplice Helper

[php]
function input_tag($name, $value = null)
{
  return '<input type="text" name="'.$name.'" id="'.$name.'"value="'.$value.'" />';
}

In verità la funzione input_tag() di symfony è leggermente più complicata di questa, dato che accetta un terzo parametro per aggiungere altri attributi al tag <input>. Puoi visionare la completa sintassi e tutte le opzioni sulla documentazione online delle API (http://www.symfony-project.org/api/1_1/).

Il più delle volte, gli helper con la loro intelligenza ti aiutano nelle lunghe e complesse operazioni di programmazione:

[php]
<?php echo auto_link_text('Visita il nostro sito www.example.com') ?>
 => Visita il nostro sito <a href="http://www.example.com">www.example.com</a>

Gli Helper facilitano il processo di creazione dei template e danno origine al miglior codice HTML possibile in termini di performance ed accessibilità. Puoi sempre continuare ad utilizzare il semplice HTML, ma gli helper solitamente velocizzano la programmazione.

TIP Potresti rimanere sorpreso dal fatto che gli helper vengano denominati tramite l'uso del carattere underscore piuttosto che tramite la convenzione camelCase, globalmente utilizzata in symfony. Questo perché gli helper sono funzioni e tutte le funzioni del core di PHP utilizzano la convenzione della denominazione attraverso il carattere underscore.

Utilizzo degli Helper

I file contenenti le definizioni degli helper non vengono caricati automaticamente (in quanto contengono funzioni e non classi). Gli helper sono raggruppati per scopo. Per esempio, tutte le funzioni helper che hanno a che fare con il testo sono definite in un file chiamato TextHelper.php, chiamato gruppo helper di testo. Così se hai bisogno di utilizzare un helper in un template, devi prima caricarne il gruppo helper relativo dichiarandolo tramite la funzione use_helper() all'interno del template stesso. Il Listato 7-3 mostra un template che utilizza l'helper auto_link_text(), che fa parte del gruppo helper di testo.

Listato 7-3 - Dichiarazione d'uso di un Helper

[php]
// Utilizza uno specifico gruppo di helper in questo template
<?php echo use_helper('Text') ?>
...
<h1>Descrizione</h1>
<p><?php echo auto_link_text($description) ?></p>

TIP Se hai bisogno di richiamare più di un gruppo di helper, puoi aggiungere ulteriori argomenti alla chiamata use_helper(). Per esempio, per caricare sia il gruppo helper di Testo che quello Javascript in un template, utilizza <?php echo use_helper('Text', 'Javascript') ?>.

Alcuni helper sono automaticamente disponibili in ogni template, senza bisogno di dichiararli. Questi sono gli helper facenti parte dei seguenti gruppi di helper:

  • Helper: richiesti per l'inclusione degli helper (la funzione use_helper() è, di fatto, un helper)
  • Tag: helper per tag basilari, utilizzati da quasi ogni helper
  • Url: helper per la gestione di Links e URL
  • Asset: helper per la generazione del sezione <head> nel codice HTML, e forniscono una facile inclusione di assets esterni (immagini, JavaScript, e fogli di stile)
  • Partial: helper che permettono l'inclusione di frammenti di template
  • Cache: helper che manipolano i frammenti di template nella cache
  • Form: helper per la gestione dei Form

La lista degli helper standard, caricati di default in ogni template, è configurabile nel file settings.yml. Così se sai di non aver bisogno degli helper del gruppo helper Cache, o che utilizzerai soltanto quelli del gruppo helper di Testo, modifica l'opzione standard_helpers a tuo piacere. Questo velocizzerà un po' la tua applicazione. Non puoi eliminare i primi quattro gruppi helper della lista precedente (Helper, Tag, Url, e Asset), perché essi sono obbligatori per il corretto funzionamento del motore dei template. Di conseguenza, questi non appaiono neppure nella lista degli helpers standard.

TIP Se mai avessi bisogno di utilizzare un helper al di fuori dei template, puoi sempre caricare il gruppo relativo da qualsiasi punto della tua applicazione richiamando sfLoader::loadHelpers($helpers), dove $helpers è il nome del gruppo helper o un array di nomi dei gruppi. Per esempio, se volessi utilizzare auto_link_text() in una action, dovrai prima richiamare sfLoader::loadHelpers('Text').

Helper utilizzati di frequente

Studierai nel dettaglio alcuni helper nei prossimi capitoli, relazionandoli alle funzionalità che semplificano. Il listato 7-4 mostra una breve lista degli helper utilizzati più spesso, assieme al codice HTML che generano.

Listato 7-4 - Helper comuni

[php]
// Helper group
<?php echo use_helper('NomeHelper') ?>
<?php echo use_helper('NomeHelper1', 'NomeHelper2', 'NomeHelper3') ?>

// Tag group
<?php echo tag('input', array('name' => 'foo', 'type' => 'text')) ?>
<?php echo tag('input', 'name=foo type=text') ?>  // Sintassi alternativa per le opzioni
 => <input name="foo" type="text" />
<?php echo content_tag('textarea', 'dummy content', 'name=foo') ?>
 => <textarea name="foo">dummy content</textarea>

// Url group
<?php echo link_to('click me', 'mymodule/myaction') ?>
=> <a href="/route/to/myaction">click me</a>  // Dipende dalle impostazioni di routing

// Asset group
<?php echo image_tag('myimage', 'alt=foo size=200x100') ?>
 => <img src="/images/myimage.png" alt="foo" width="200" height="100"/>
<?php echo javascript_include_tag('myscript') ?>
 => <script language="JavaScript" type="text/javascript" src="/js/myscript.js"></script>
<?php echo stylesheet_tag('style') ?>
 => <link href="/stylesheets/style.css" media="screen" rel="stylesheet"type="text/css" />

Ci sono molti altri helper in symfony e ci vorrebbe un libro intero per descriverli tutti. Il miglior riferimento per gli helper è la documentazione online delle API (http://www.symfony-project.org/api/1_1/), dove tutti gli helper sono ben documentati, con la loro sintassi, opzioni ed esempi.

Aggiungere i tuoi helper

Symfony mette a disposizione un sacco di helper per vari scopi, ma se non trovi quello che ti serve nella documentazione delle API, vorrai creare un nuovo helper. Questo è molto semplice da fare.

Le funzioni dell'helper (regolari funzioni PHP che restituiscono codice HTML) dovrebbero essere salvate in un file chiamato FooBarHelper.php, dove FooBar è il nome dell gruppo di helper. Posiziona il file nella directory apps/myapp/lib/helper/ (o in ogni altra directory helper/ creata sotto una delle cartelle lib/ del tuo progetto) in modo da essere automaticamente trovato dall'helper use_helper('FooBar') per essere incluso.

TIP Questo sistema ti permette anche di scavalcare gli helper di symfony esistenti. Per esempio, per ridefinire tutti gli helper del gruppo Testo, basta creare un file TextHelper.php nella tua directory apps/frontend/lib/helper/. Ogni volta che tu usi use_helper('Text'), symfony utilizzerà il tuo gruppo helper piuttosto che il suo. Ma stai attento: visto che il file originale non viene caricato devi ridefinire tutte le funzioni del gruppo helper per scavalcarlo, altrimenti alcuni degli helper originali non saranno per nulla disponibili.

Layout della Pagina

Il template mostrato nel Listato 7-1 non è un documento XHTML valido. La definizione DOCTYPE e i tag <html> e <body> non sono presenti. Questo perché sono posizionati da un'altra parte nell'applicazione, in un file chiamato layout.php, che contiene il layout della pagina. Questo file, chiamato anche template globale, contiene il codice HTML comune a tutte le pagine dell'applicazione per evitare di ripeterlo in ogni template. Il contenuto del template è integrato nel layout, o, se si cambia punto di vista, il layout "decora" il template. Questa è una applicazione del decorator design pattern, illustrato in Figura 7-1.

TIP Per maggiori informazioni riguardo al decorator e altri design patter, vedi Patterns of Enterprise Application Architecture di Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0).

Figura 7-1 - Decorazione di un template con un layout

Decorazione di un template con un layout

Il Listato 7-5 mostra il layout di pagina di default, situato nella directory dell'applicazione templates/.

Listato 7-5 - Layout di default, in myproject/apps/frontend/templates/layout.php

[php]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="it" lang="it">
  <head>
    <?php echo include_http_metas() ?>
    <?php echo include_metas() ?>
    <?php echo include_title() ?>
    <link rel="shortcut icon" href="/favicon.ico" />
  </head>
  <body>

  <?php echo $sf_content ?>

  </body>
</html>

Gli helper richiamati nella sezione <head> prendono informazioni dal response object e dalla configurazione della view. Il tag <body> estrae il risultato del template. Con questo Layout, la configurazione di default e il template di esempio del Listato 7-1, la view elaborata è simile al Listato 7-6.

Listato 7-6 - Il Layout, la Configurazione della View, e il Template Assemblati

[php]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="it" lang="it">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta name="title" content="progetto symfony" />
    <meta name="robots" content="index, follow" />
    <meta name="description" content="progetto symfony" />
    <meta name="keywords" content="symfony, progetto" />
    <title>progetto symfony</title>
    <link rel="stylesheet" type="text/css" href="/css/main.css" />
    <link rel="shortcut icon" href="/favicon.ico">
  </head>
  <body>

  <h1>Benvenuto</h1>
  <p>Bentornato, <?php echo $name ?>!</p>
  <ul>Cosa vorresti fare?
    <li><?php echo link_to('Leggere gli ultimi articoli', 'article/read') ?></li>
    <li><?php echo link_to('Iniziare a scriverne uno nuovo', 'article/write') ?></li>
  </ul>

  </body>
</html>

Il template globale può essere completamente personalizzato per ogni applicazione. Aggiungi qualsiasi codice HTML di cui hai bisogno. Questo layout è spesso utilizzato per contenere la navigazione del sito, il logo e così via. Puoi anche avere più di un layout e decidere quale deve essere utilizzato per ogni action. Non preoccuparti dell'inclusione di Javascript e fogli di stile per adesso; la sezione "Configurazione della View" più avanti in questo capitolo mostra come occuparsi di ciò.

Scorciatoie nei Template

Nei template alcune variabili di symfony sono sempre disponibili. Queste scorciatoie danno accesso all'informazioni più comunemente necessarie nei template, attraverso gli oggetti base di symfony:

  • $sf_context: l'intero oggetto del contesto (istanza di sfContext)
  • $sf_request: l'oggetto della richiesta (istanza di sfRequest)
  • $sf_params: i parametri dell'oggetto richiesta
  • $sf_user: l'attuale oggetto della sessione utente (istanza di sfUser)

Il precedente capitolo ha illustrato dei metodi utili degli oggetti sfRequest e sfUser. Effettivamente puoi chiamare questi metodi nei template tramite le variabili $sf_request e $sf_user. Per esempio, se la richiesta include un parametro total, il suo valore sarà disponibile nel template come segue:

[php]
// Versione lunga
<?php echo $sf_request->getParameter('total'); ?>

// Versione corta
<?php echo $sf_params->get('total'); ?>

// Equivalente al seguente codice della action
echo $request->getParameter('total');

Frammenti di Codice

Spesso potresti aver bisogno di includere un po' di codice HTML o PHP in diverse pagine. Per evitare di ripetere quel codice l'istruzione PHP include() il più delle volte è sufficiente.

Per esempio, se molti dei template della tua applicazione hanno bisogno di utilizzare lo stesso frammento di codice, salvalo in un file chiamato myFragment.php nella directory dei template globali (myproject/apps/frontend/templates/) ed includilo nel tuo template come segue:

[php]
<?php include(sfConfig::get('sf_app_template_dir').'/myFragment.php') ?>

Ma questo non è un modo molto pulito per pacchettizzare un frammento, più che altro perché potresti avere differenti nomi di variabile tra il frammento e i vari template che lo includono. In aggiunta il sistema di cache di symfony (descritto nel Capitolo 12) non può in alcun modo rilevare un include, quindi il frammento non può essere messo in cache indipendentemente dal template. Symfony fornisce tre intelligenti tipi diversi di frammenti di codice per rimpiazzare gli include:

  • Se la logica è leggera, vorrai includere un file template che ha accesso agli stessi dati che gli passi. Per far questo utilizzerai un partial.
  • Se la logica è più pesante (per esempio se devi accedere al data model e/o modificare il contenuto conformemente alla sessione), preferirai separare la presentazione dalla logica. Per far ciò utilizzerai un component.
  • se il frammento è scritto per rimpiazzare una specifica parte del layout, per il quale potrebbe già esistere un contenuto standard, utilizzerai un slot.

Note Un altro tipo di frammento di codice, chiamato component slot, deve essere utilizzato quando la natura del frammento dipende dal contesto (per esempio se il frammento deve essere differente per le action di un dato modulo). i component slot sono descritti più avanti in questo capitolo.

L'inclusione di questi frammenti si ottiene tramite gli helper del gruppo Partial. Questi helper sono disponibili in ogni template symfony, senza dichiarazione iniziale.

Partial

Un partial è un pezzo di codice di template riutilizzabile. Per esempio, in una applicazione per pubblicazioni, il codice del template che visualizza un articolo è utilizzata nella pagina del dettaglio dell'articolo e anche nella lista dei migliori articoli e la lista degli ultimi articoli. Questo codice è il prefetto candidato per un partial, come illustrato nella Figura 7-2.

Figure 7-2 - Riutilizzare i partial nei template

Riutilizzare i partial nei template

Come i template i partial sono file posizionati nella directory templates/, e contengono codice HTML con PHP inserito in esso. Il nome del file partial comincia sempre con un trattino basso (_), questo aiuta a distinguere i partial dai template, visto che sono posizionati nella stessa cartella templates/.

Un template può includere partial sia nel suo stesso modulo, in un altro modulo o nella directory globale templates/. Inserisci un partial utilizzando l'helper include_partial() specificando il modulo e il nome del partial come parametri (tralasciando l'underscore iniziale e il .php finale), come mostrato nel Listato 7-7.

Listato 7-7 - Includere un Partial in un Template del Modulo mymodule

[php]
// Include il partial frontend/modules/mymodule/templates/_mypartial1.php
// Visto che il template e il partial sono nello stesso modulo,
// puoi omettere in nome del modulo
<?php include_partial('mypartial1') ?>

// Include il partial frontend/modules/foobar/templates/_mypartial2.php
// Il nome del modulo è obbligatorio in questo caso
<?php include_partial('foobar/mypartial2') ?>

// Include il partial frontend/templates/_mypartial3.php
// È considerato parte del modulo 'global'
<?php include_partial('global/mypartial3') ?>

I Partial hanno accesso agli helper di symfony e alle scorciatoie dei template. Anche se i partial possono essere richiamati in qualsiasi parte dell'applicazione, non hanno accesso automatico alle variabili definite nell'action che richiama il template che li include, a meno che non siano passate esplicitamente come argomento. Per esempio se vuoi che un partial abbia accesso alla variabile $total, l'action deve passarla al template e poi il template all'helper come secondo parametro della funzione include_partial(), come mostrato nei Listati 7-8, 7-9 e 7-10.

Listato 7-8 - La action definisce una variabile , in mymodule/actions/actions.class.php

[php]
class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    $this->total = 100;
  }
}

Listato 7-9 - Il template passa la variabile al partial, in mymodule/templates/indexSuccess.php

[php]
<p>Ciao, mondo!</p>
<?php include_partial('mypartial', array('mytotal' => $total)) ?>

Listato 7-10 - Il partial può ora utilizzare la varabile, in mymodule/templates/_mypartial.php

[php]
<p>Total: <?php echo $mytotal ?></p>

TIP Finora tutti gli helper sono stati richiamati con <?php echo functionName() ?>. L'helper del partial invece è chiamato semplicemente con <?php include_partial() ?>, senza echo, per far in modo che si comporti similmente alla regolare istruzione PHP include(). Semmai avessi bisogno di una funzione che ritorna il contenuto di un partial senza visualizzarla effettivamente, utilizza invece get_partial(). Tutti gli helper include_ descritti in questo capitolo hanno una controparte get_ che può essere richiamata assieme ad una istruzione echo.

Nuovo in symfony 1.1: invece di risultare in un template, una action può restituire un partial. I metodi renderPartial() e renderComponent() della classe action promuovono la riusabilità del codice. Inoltre, si avvantaggiano dell'abilità di caching dei partial (vedi Capitolo 12). Le variabili definite nella action saranno passate automaticamente al partial o al component, a meno che non si definisca una array associativa di variabili come secondo parametro del metodo.

[php]
public function executePippo()
{
  // fai qualcosa
  $this->pippo = 1234;
  $this->pluto = 4567;

  return $this->renderPartial('mymodule/mypartial');
}

In questo esempio, il partial avrà accesso a $pippo e $pluto. Se la action termina con la seguente riga: return $this->renderPartial('mymodule/mypartial', array('pippo' => $this->pippo)); Allora il partial avrà accesso solo a $pippo.

Component

Nel Capitolo 2, il primo script di esempio è stato diviso in due parti per separare la logica dalla presentazione. Proprio come il pattern MVC si applica ad action e template, hai bisogno di dividere un partial in una parte logica e in una parte di presentazione. In questo caso, dovresti usare un component.

Un component è come una action, ma è molto più veloce. La logica del component sta in una classe che eredita da sfComponents, situata in un file action/components.class.php. La parte di presentazione sta in un partial. I metodi della classe sfComponents iniziano con la parola execute, proprio come le action, a possono passare delle variabili alla loro controparte nello stesso modo in cui le action passano variabili. I partial che servono da presentazione per i component prendono il loro nome dal component (senza execute all'inizio, ma con un trattino basso al suo posto). La Tabella 7.1 confronta le convenzioni sui nomi per action e component.

Tabella 7-1 - Convenzioni sui nomi di Action e Component

convenzione Action Component
File della logica actions.class.php components.class.php
Classe della logica estende sfActions sfComponents
Nome del metodo executeMyAction() executeMyComponent()
Nome del file della presentazione myActionSuccess.php _myComponent.php

TIP Nello stesso modo in cui puoi separare i file delle action, la classe sfComponents ha una controparte sfComponent che consente di usare singoli file separati con lo stesso tipo di sintassi.

Per esempio, supponiamo di avere una barra laterale che mostra le ultime notizie su un dato argomento, in base al profilo dell'utente, che viene usata in diverse pagine. Le query necessarie per recuperare le notizie sono troppo complesse per stare in un semplice partial, quindi devono essere spostate in un component. La Figura 7-3 illustra l'esempio.

Figura 7-3 Usare i component nei template usare i component nei template

Listato 7-11 - La classe Components, in modules/news/actions/components.class.php

[php]

class newsComponents extends sfComponents
{
  public function executeHeadlines()
  {
    $c = new Criteria();
    $c->addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
    $c->setLimit(5);
    $this->news = NewsPeer::doSelect($c);
  }
}

Listato 7-12 - Il partial, in modules/news/templates/_headlines.php

<div>
  <h1>Ultime notizie</h1>
  <ul>
  <?php foreach($news as $headline): ?>
    <li>
      <?php echo $headline->getPublishedAt() ?>
      <?php echo link_to($headline->getTitle(),'news/show?id='.$headline->getId()) ?>
    </li>
  <?php endforeach ?>
  </ul>
</div>

Ora, ogni volta che hai bisogno di un component in un template, basta richiamare:

[php]
<?php include_component('news', 'headlines') ?>

Proprio come i partial, i component accettano parametri addizionali in forma di array associativa. I parametri sono disponibili al partial con il loro nome, e il component tramite l'oggetto $this. Si veda il Listato 7-13 per un esempio.

Listato 7-13 - Passare parametri ad un component e al suo template

// Chiama il component
<?php include_component('news', 'headlines', array('pippo' => 'pluto')) ?>

// Nel component stesso
echo $this->pippo;
 => 'pluto'

// Nel partial _headlines.php
echo $pippo;
 => 'pluto'

Puoi includere component in altri component, o nel layout globale, come in un normale template. Come le action, i metodi execute dei component possono passare variabili al corrispettivo partial ed avere accesso alle stesse scorciatoie. Ma le similitudini si fermano qui. Un component non gestisce la sicurezza o la validazione, non può essere chiamato da Internet (solo dall'applicazione stessa), e non ha le varie possibilità di ritorno. È per questo che un component è più veloce di una action.

Slot

I partial e i component sono ottimi per la riusabilità. Ma in molti casi, i frammenti di codice devono riempire un layout con più di una zona dinamica. Per esempio, supponi di voler aggiungere alcuni tag personalizzati nella sezione <head> del layout a seconda del contenuto della action. Oppure, supponi che il layout abbia una grande zona dinamica, riempita dal risultato di una action, più diverse altre piccole zone, con un contenuto di default definito nel layout ma che può essere sovrascritto a livello di template.

Per queste situazioni, la soluzione è uno slot. In parole povere, uno slot è un segnaposto che puoi inserire in qualsiasi elemento della vista (nel layout, in un template, in un partial). Riempire questo segnaposto è proprio come impostare una variabile. Il codice contenuto viene memorizzato globalmente nella risposta, quindi puoi eseguirlo dopo il template (è il processo di decorazione), e i partial sono eseguiti quando sono chiamati nel template. Sembra troppo astratto? Vediamo un esempio.

Immagina un layout con una zona per il template e due slot: uno per la barra laterale e l'altro per il footer. I valori degli slot sono definiti nei template. Durante il processo di decorazione, il codice layout contorna il codice del template, e gli slot sono riempiti con i valori precedentemente definiti, come illustrato in Figura 7-4. La barra laterale e il footer possono essere contestuali alla action principale. È come avere un layout con più di un "buco".

Figura 7-4 - Slot di layout definiti in un template

Slot di layout definiti in un template

Un po' di codice chiarirà le cose. Per includere uno slot, usa l'helper include_slot(). L'helper has_slot() restituisce true se lo slot è già stato definito, fornendo un meccanismo di salvataggio. Per esempio, definiamo un segnaposto per uno slot barra laterale nel layout e il suo contenuto di default come mostrato nel Listato 7-14.

Listato 7-14 - Includere uno Slot barra laterale nel Layout

<div id="sidebar">
<?php if (has_slot('sidebar')): ?>
  <?php include_slot('sidebar') ?>
<?php else: ?>
  <!-- codice della barra laterale -->
  <h1>Zona contestuale</h1>
  <p>Questa zona contiene link e informazioni relative al contenuto principale della pagina.</p>
<?php endif; ?>
</div>

Essendo piuttosto comune visualizzare un contenuto di default se lo slot non è definito, l'helper include_slot restituisce un booleano che indica se lo slot è stato definito. Il Listato 7-15 mostra come prendere in considerazione questo valore per semplificare il codice.

Listato 7-15 - Includere uno Slot barra laterale nel Layout

<div id="sidebar">
<?php if (!include_slot('sidebar')): ?>
 <!-- codice di default della barra laterale -->
  <h1>Zona contestuale</h1>
  <p>Questa zona contiene link e informazioni relative al contenuto principale della pagina.</p>
<?php endif; ?>
</div>

Ogni template ha la possibilità di definire i contenuti di uno slot (anche i partial possono farlo). Siccome gli slot sono pensati per contenere codice HTML, symfony offre un modo conveniente per definirli: basta scrivere il codice dello slot tra una chiamata agli helper slot() e end_slot(), come nel Listato 7-16.

Listato 7-16 - Sovrascrivere lo Slot barra laterale in un Template

// ...

<?php slot('sidebar') ?>
  <!-- barra laterale personalizzata per il template corrente -->
  <h1>Dettagli utente</h1>
  <p>name:  <?php echo $user->getName() ?></p>
  <p>email: <?php echo $user->getEmail() ?></p>
<?php end_slot() ?>

Il codice tra i due helper viene eseguito nel contesto del template, quindi ha accesso a tutte le variabili che sono state definite nella action. Symfony inserirà automaticamente il risultato di questo codice nell'oggetto risposta. Non sarà mostrato nel template, ma reso disponibile per future chiamate a include_slot(), come nel Listato 7-14.

Gli slot sono molto utili per definire zone pensate per mostrare un contenuto contestuale. Possono essere usati anche per aggiungere codice HTML al layout solo per alcune action. Per esempio, un template che mostra la lista delle ultime notizie potrebbe voler aggiungere un link al feed RSS nella parte <head> del layout. Si può fare semplicemente aggiungendo uno slot "feed" al layout e sovrascrivendolo nel template della lista.

Se il contenuto dello slot è molto breve, come per esempio nel caso di uno slot che definisce un title, puoi semplicemente passare il contenuto come secondo argomento del metodo slot(), come mostrato nel Listato 7-17.

Listato 7-17 - Usare slot() per definire un valore corto

<?php slot('title', 'Il titolo') ?>

SIDEBAR

Dover trovare i frammenti di template Le persone che lavorano sui template sono solitamente web designer, che potrebbero non conoscere symfony molto bene ed avere difficoltà a trovare i frammenti di template, poiché questi sono sparpagliati nell'applicazione. Queste poche linee guida li faranno trovare più a loro agio con il sistema di template di symfony.

Innanzitutto, sebbene un progetto symfony contenga diverse cartelle, tutti i layout, i template ed i frammenti di template sono in cartelle che si chiamano templates/. Quindi per quello che interessa a un web designer, la struttura di un progetto può essere ridotta a qualcosa del tipo:

myproject/ apps/ application1/ templates/ # Layout per l'applicazione 1 modules/ module1/ templates/ # Template e partial per il modulo 1 module2/ templates/ # Template e partial per il modulo 2 module3/ templates/ # Template e partial per il modulo 3

Tutte le altre cartelle possono essere ignorate

Quando incontrano un include_partial(), i web designer hanno solamento bisogno di sapere che solo il primo argomento è importante. Lo schema di questo argomento è nome_modulo/nome_partial, e questo significa che il codice di presentazione può essere trovato in modules/nome_modulo/templates/_nome_partial.php. Per l'helper include_component(), il nome del modulo e il nome del partial sono i primi due argomenti. Come per il resto, un'idea generale su cosa sono gli helper e su quali helper sono più comuni nei template dovrebbe essere abbastanza per iniziare a disegnare template per le applicazioni symfony.

Configurazione della Vista

In symfony, la vista consiste di due parti distinte:

  • La presentazione HTML del risultato (memorizzata nel template, nel layout, e nei frammenti di template)
  • Tutto il resto, incluso quello che segue: o Dichiarazioni meta: Keywords, description, o durata della cache. o Titolo della pagina: Non solo aiuta gli utenti a trovare le finestre del browser, ma è anche molto importante per l'indicizzazione dei motori di ricerca. o Inclusioni di file: JavaScript e fogli di stile. o Layout: Alcune action richiedono un layout personalizzato (popup, banner, ecc.) oppure nessun (per esempio le action Ajax).

Nella vista, tutto ciò che non è HTML è chiamato configurazione, e symfony fornisce due modi per manipolarla. Il modo solito è tramite il file di configurazione view.yml. Questo file può essere usato ogni volta che i valori non dipendono dal contesto o da query su database. Se hai bisogno di impostare valori dinamici, il modo alternativo è di impostare la configurazione con gli attributi dell'oggetto sfResponse direttamente nella action.

Note Se imposti un parametro di configurazione sia con l'oggetto sfResponse che tramite il file view.yml, sfResponse ha precedenza.

Il file view.yml

Ogni modulo può avere un file view.yml che definisce le impostazioni delle sue viste. Questo consente di definire le impostazioni per le viste di un intermo modulo in un singolo file. Le chiavi di primo livello di view.yml sono i nomi delle viste del modulo. Il Listato 7-18 mostra un esempio di configurazione.

Listato 7-18 - Esempio di view.yml a livello di modulo

editSuccess:
  metas:
    title: Edit your profile

editError:
  metas:
    title: Error in the profile edition

all:
  stylesheets: [my_style]
  metas:
    title: My website

*CAUTION Fai attenzione alle chiavi principali nel file view.yml, sono nomi di viste e non nomi di action. Come promemoria, il nome di una vista è composto da un nome di action e da un suffisso. Per esempio, se la action edit restiuisce sfView::SUCCESS (o non restituisce nulla, che è lo stesso), allora il nome della vista è editSuccess.

Le impostazioni predefinite per il modulo sono definite sotto la chiave `all:nel fileview.ymldel modulo. Le impostazioni predefinite per tutte le viste dell'applicazione sono definite nel file view.yml dell'applicazione. Ancora una volta, si può riconoscere il principio della configurazione a cascata: * In apps/frontend/modules/mymodule/config/view.yml, le definizioni per le viste si applicano solo ad una vista e sovrascrivono le definizioni a livello di modulo.

  • In apps/frontend/modules/mymodule/config/view.yml, le definizioni all: si applicano a tutte le action del modulo e sovrascrivono le definizioni a livello di applicazione.

  • In apps/frontend/config/view.yml, le definizioni default: si applicano a tutti i moduli e a tutte le action dell'applicazione.

TIP I file view.yml a livello di modulo non esistono di default. la prima volta in cui hai bisogno di modificare i parametri di configurazione della vista per un modulo, devi creare un view.yml vuoto nella cartella config/.

Dopo aver visto il template di default nel Listato 7-5 ed un esempio di risposta finale nel Listato 7-6, potresti chiederti da dove vengono i valori dell'header. Di fatto, sono i valori predefiniti della vista, definiti nel view.yml dell'applicazione e mostrati nel Listato 7-19.

Listato 7-19 - Configurazione predefinita della Vista a livello di Applicazione, in apps/frontend/config/view.yml

default:
  http_metas:
    content-type: text/html

  metas:
    #title:        progetto symfony
    #description:  progetto symfony
    #keywords:     symfony, progetto
    #language:     it
    robots:        index, follow

  stylesheets:    [main]

  javascripts:    []

  has_layout:     on
  layout:         layout

Ognuna di queste impostazioni sarà descritta in dettaglio nella sezione "Impostazioni della Configurazione della Vista".

L'Oggetto Risposta

Sebbene sia parte del livello vista, l'oggetto risposta è spesso modificato dalla action. Le action possono accedere all'oggetto risposta di symfony, chiamato sfResponse, tramite il metodo getResponse(). Il Listato 7-20 elenca alcuni dei metodi sfResponse usati spesso dentro le action.

Listato 7-20 - Le Azioni hanno accesso ai Metodi dell'Ogetto sfResponse

[php]
class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    $response = $this->getResponse();

    // header HTTP
    $response->setContentType('text/xml');
    $response->setHttpHeader('Content-Language', 'it');
    $response->setStatusCode(403);
    $response->addVaryHttpHeader('Accept-Language');
    $response->addCacheControlHttpHeader('no-cache');

    // Cookie
    $response->setCookie($name, $content, $expire, $path, $domain);

    // Meta e header di pagina
    $response->addMeta('robots', 'NONE');
    $response->addMeta('keywords', 'pippo pluto');
    $response->setTitle('La mia pagina Pippo');
    $response->addStyleSheet('custom_style');
    $response->addJavaScript('custom_behavior');
  }
}

Oltre ai metodi setter qui mostrati, la classe sfResponse ha dei getter che restiuiscono il valore corrente degli attributi di risposta.

I setter dell'header sono molto potenti in symfony. Gli header sono inviati il più tardi possibile (nel filtrosfRenderingFilter), in modo che possano essere alterati a piacimento in ogni momento. Ci sono anche delle scorciatorie molto utili. Per esempio, se non si specifica un set di caratteri chiamando setContentType(), symfony aggiunge automaticamente il set di caratteri definito nel file settings.yml.

$response->setContentType('text/xml');
echo $response->getContentType();
 => 'text/xml; charset=utf-8'

Il codice di stato delle risposte in symfony è conforme alla specifica HTTP. Le eccezioni restituiscono il codice 500, le pagine non trovate il codice 404, le pagine normali il codice 200, le pagine modificate possono essere ridotte ad un header semplice con il codice 304 (vedi il Capitolo 12 per i dettagli), e così via. Ma si possono sovrascrivere questi valori predefiniti impostando i proprio codici di stato nella action con il metodo setStatusCode(). Si può specificare un codice personalizzato ed un messaggio personalizzato, o solamente un codice personalizzato -- in questo caso, symfony aggiungerà un messaggio generico.

$response->setStatusCode(404, 'Pagina non trovata');

TIP Prima di inviare gli header, symfony normalizza i loro nomi. Quindi non hai bisogno di scrivere content-language invece di Content-Language in una chiamata a setHttpHeader(), perché symfony capirà il primo e lo trasformerà automaticamente nel secondo.

Impostazioni di Configurazione della Vista

Forse hai notato che ci sono due tipi di impostazioni di configurazione:

  • Quelle con un valore unico (il valore è una stringa nel file view.yml e la risposta usa un metodo set per questi)
  • Quelle con valori multipli (per i quali view.yml usa delle array e la risosta un metodo add)

Tieni a mente che la configurazione a cascata cancella le impostazioni con valore unico, ma impila le impostazione a valori multipli. La cosa diventerà più evidente man mano che progredirai in questo capitolo.

Configurazione dei Tag Meta

Le informazioni scritte nei tag <meta> nella risposta non sono visualizzati in un browser ma sono utitli per i robot e i motori di ricerca. Esse controllano anche le impostazioni della cache di ogni pagina. Definire questi tag sotto le chiavi http_metas: e metas: in view.yml, come nel Listato 7-21, oppure con i metodi addHttpMeta() e addMeta() nella action, come nel Listato 7-22.

Listato 7-21 - Definizioni dei Meta come coppie chiave/valore in view.yml

http_metas:
  cache-control: public

metas:
  description:   Finanza in Italia
  keywords:      finanza, Italia

Listato 7-22 - Definizioni dei Meta come impostazioni della risposta nella Action

$this->getResponse()->addHttpMeta('cache-control', 'public');
$this->getResponse()->addMeta('description', 'Finanza in Italia');
$this->getResponse()->addMeta('keywords', 'finanza, Italia');

Aggiungere una chiave esistente rimpiazzerà il contenuto attuale per default. Per i tag meta, puoi aggiungere un terzo parametro ed impostarlo a false per far sì che il metodo addHttpMeta() (e anche setHttpHeader()) appenda il valore a quello esistente, invece di rimpiazzarlo.

$this->getResponse()->addHttpMeta('accept-language', 'en');
$this->getResponse()->addHttpMeta('accept-language', 'it', false);
echo $this->getResponse()->getHttpHeader('accept-language');
 => 'en, it'

Per far comparire questi tag meta nel documento finale, nella sezione <head> devono essere chiamati gli helper include_http_metas() e include_metas() (questo è il caso del layout predefinito; si veda il Listato 7-5). Symfony aggrega automaticamente le impostazioni da tutti i file view.yml (incluso quello predefinito mostrato nelListato 7-18) e l'attributo di risposta per mostrare propriamente i tag <meta>. L'esempio nel Listato 7-21 alla fine diventa come nel Listato 7-23.

Listato 7-23 - Output dei tag Meta nella pagina finale

<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="cache-control" content="public" />
<meta name="robots" content="index, follow" />
<meta name="description" content="Finanza in Italia" />
<meta name="keywords" content="finanza, Italia" />

Come bonus, l'header HTTP della risposta viene influenzato anche dalla definizione http-metas:, anche se non hai include_http_metas() nel layout, o se non hai nessun layout. Per esempio, se hai bisogno di inviare una pagina come testo semplice, definisci il seguente view.yml:

http_metas:
  content-type: text/plain

has_layout: false

Configurazione del Titolo

Il titolo di una pagina è una parte chiave per l'indicizzazione dei motori di ricerca. È anche molto utile con i browser moderni che forniscono la navigazione a schede. In HTML, il titolo è sia un tag ('title') che un'informazione meta della pagina, quindi il file view.yml vede la chiave title: come figlio della chiave metas:. Il Listato 7-24 mostra la definizione del titolo in view.yml, e il Listato 7-25 mostra la definizione nella action.

Listato 7-24 - Definizione del Titolo in view.yml

indexSuccess:
  metas:
    title: I tre porcellini

Listato 7-25 - Definizione del Titolo nella Action--Consente titoli dinamici

$this->getResponse()->setTitle(sprintf('I %d porcellini', $number));

Nella sezione <head> del documento finale, la definizione del titolo imposta il tag <meta name="title"> se è presente l'helper include_metas(), e il tag <title> se è presente l'helper include_title(). Se sono presenti entrambi (come nel Listato 7-5), il titolo appare due volte nel sorgente del documento, (vedi Listato 7-6), il che è innocuo.

Configurazione dell'Inclusione dei File

Aggiungere un foglio di stile o un JavaScript ad una vista è facile, come dimostra il Listato 7-26.

Listato 7-26 - Inclusione di File Asset

// In view.yml
indexSuccess:
  stylesheets: [mystyle1, mystyle2]
  javascripts: [myscript]

[php]
// Nella Action
$this->getResponse()->addStylesheet('mystyle1');
$this->getResponse()->addStylesheet('mystyle2');
$this->getResponse()->addJavascript('myscript');

// Nel Template
<?php use_stylesheet('mystyle1') ?>
<?php use_stylesheet('mystyle2') ?>
<?php use_javascript('myscript') ?>

In ogni caso, l'argomento è un nome di file. Se il file ha un'estensione logica (.css per un foglio di stile e .js per un JavaScript), può essere omessa. Se il file ha una locazione logica (/css/ per un foglio di stile e /js/ per un JavaScript), può essere omessa anch'essa. Symfony è abbastanza intelligente da capire l'estensione e la locazione corrette.

Diversamente dalle definizioni dei meta e del titolo, le definizioni delle inclusioni di file non richiedono nessun helper nel template o nel layout. Questo vuol dire che le impostazioni viste sopra visualizzeranno il codice HTML del Listato 7-27, quale che sia il contenuto del template e del layout.

Listato 7-27 - Risultato della Inclusioni di File -- Non serve chiamare nessun helper

<head>
...
<link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle1.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle2.css" />
<script language="javascript" type="text/javascript" src="/js/myscript.js">
</script>
</head>

NOTE Le inclusioni di fogli di stile e di Javascript sono eseguite da un filtro chiamato sfCommonFilter. Questo cerca un tag <head> nella risposta e aggiunge <link> e <script> subito prima di </head>. Questo viol dire che l'inclusione non può avere luogo se non c'è un tag <head> nel layout o nei template.

Ricorda che si applica il principio della configurazione a cascata, quindi qualsiasi inclusione di file definita in view.yml dell'applicazione appare in ogni pagina dell'applicazione. I Listati 7-28, 7-29, e 7-30 dimostrano questo principio.

Listato 7-28 - Esempio di view.yml nell'Applicazione

default:
  stylesheets: [main]

Listato 7-29 - Esempio di view.yml nel Modulo

indexSuccess:
  stylesheets: [special]

all:
  stylesheets: [additional]

Listato 7-30 - Vista risultante indexSuccess

<link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/additional.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/special.css" />

Se hai bisogno di rimuovere un file definiti ad un livello superiore, basta aggiungere un segno meno (-) davanti al nome del file in una definizione a livello inferiore, come mostrato nel Listato 7-31.

Listato 7-31 - Esempio di view.yml nel Modulo che rimuove un File definito a Livello Applicazione

indexSuccess:
  stylesheets: [-main, special]

all:
  stylesheets: [additional]

Per rimuovere tutti i fogli di stile o Javascript, usa -* come nome di file, come mostrato nel Listato 7-32.

Listato 7-32 - Esempio di view.yml nel Modulo che rimuove tutti i File definiti a Livello Applicazione

indexSuccess:
  stylesheets: [-*]
  javascripts: [-*]

Puoi essere più accurato e definire un parametro aggiuntivo per forzare la posizione in cui includere il file (prima o ultima posizione), come mostrato nel Listato 7-33. Questo funziona sia per i fogli di stile che per i Javascript.

Listato 7-33 - Definire la Posizione del File Asset Incluso

// In view.yml
indexSuccess:
  stylesheets: [special: { position: first }]

[php]
// Nella action
$this->getResponse()->addStylesheet('special', 'first');

// Nel template
<?php use_stylesheet('special', 'first') ?>

'''Novità in symfony 1.1''': Puoi anche decidere di bypassare la trasformazione del nome del file asset, in modo che il tag <link> o <script> risultante si riferisca esattamente alla locazione specificiata, come mostrato nel Listato 7-34.

Listato 7-34 - Inclusione di un foglio di stile con nome diretto

// In view.yml
indexSuccess:
  stylesheets: [main, paper: { raw_name: true }]

[php]
// Nella Action
$this->getResponse()->addStylesheet('paper', '', array('raw_name' => true));

// Nel template
<?php use_stylesheet('paper', '', array('raw_name' => true)) ?>

// Vista risultante
<link rel="stylesheet" type="text/css" media="print" href="paper" />

Per specificare l'attributo media di un foglio di stile, puoi cambiare le opzioni del tag, come mostrato nel Listato 7-35.

Listato 7-35 - Foglio di stile con Media

// In view.yml
indexSuccess:
  stylesheets: [main, paper: { media: print }]

[php]
// Nella Action
$this->getResponse()->addStylesheet('paper', '', array('media' => 'print'));

// Nel template
<?php use_stylesheet('paper', '', array('media' => 'print')) ?>

// Vista risultante
<link rel="stylesheet" type="text/css" media="print" href="/css/paper.css" />

Configurazione del Layout

A seconda dell'organizzazione del tuo sito, puoi avere diversi layout. Di solito un sito ne ha almeno due: il layout default e il layout per i popup.

Abbiamo già visto che il layout di default è myproject/apps/frontend/templates/layout.php. Altri layout possono essere aggiunti nella stessa cartella templates/. Se vuoi che una vista usi il file frontend/templates/my_layout.php, usa la sintassi mostrata nel Listato 7-36.

Listato 7-36 - Definizione di un Layout

// In view.yml
indexSuccess:
  layout: my_layout

[php]
// Nella action
$this->setLayout('my_layout');

// Nel template
<?php decorate_with('my_layout') ?>

Alcune viste non necessitano di layout (per esempio, pagine in testo semplice o feed RSS). In questo caso, imposta has_layout a false, come mostrato nel Listato 7-37.

Listato 7-37 - Rimozione di un Layout

// In `view.yml`
indexSuccess:
  has_layout: false

[php]
// Nella Action
$this->setLayout(false);

// Nel template
<?php decorate_with(false) ?>

NOTE Le viste delle action Ajax di default non hanno layout.

Component Slot

Combinare la potenza dei component della vista e la configurazione delle vsite porta ad una nuova prospettiva dello sviluppo delle viste: il sistema dei component slot. È un'alternativa agli slot che si focalizza sulla riusabilità e sulla separazione dei livelli. Quindi i component slot sono più strutturati degli slot, ma leggermente più lenti da eseguire.

Proprio come gli slot, i component slot sono segnaposti nominali che puoi dichiarare negli elementi della vista. La differenza sta nel modo in cui il codice di riempimento viene determinato. Per uno slot, il codice è impostato in un altro elemento della vista; per un component slot, il codice è il risultato dell'esecuzione di un componente, e il nome di questo componente deriva dalla configurazione della vista. Capirai meglio i component slot dopo averli visti in azione.

Per impostare il segnaposto di un component slot, usa l'helper include_component_slot(). Questa funzione si aspetta un'etichetta come parametro. Per esempio, supponi che il file layout.php dell'applicazione contenga un menù laterale contestuale. Il Listato 7-38 mostra come l'helper del component slot viene incluso.

Listato 7-38 - Includere un Component Slot chiamato 'sidebar'

...
<div id="sidebar">
  <?php include_component_slot('sidebar') ?>
</div>

Definire la corrispondenza tra l'etichetta di un component slot ed il nome del component nella configurazione della vista. Per esempio, impostare il component di default per il component slot sidebar nel file view.yml dell'applicazione, sotto l'header components. La chiave è l'etichetta del component slot; il valore deve essere un'array contenente un modulo ed un nome di componente. Il Listato 7-39 mostra un esempio.

Listato 7-39 - Definire lo Slot Component default 'sidebar', in frontend/config/view.yml

default:
  components:
    sidebar:  [bar, default]

Quindi quando viene eseguito il layout, il component slot sidebar viene riempito col risultato del metodo executeDefault() della classe barComponents che si trova nel modulo bar, e questo metodo mostrerà il partial _default.php che si trova in modules/bar/templates/.

La cascata di configurazioni ti dà l'opportunità di sovrascrivere questa impostazione per un dato modulo. Per esempio, in un modulo user, potresti volere che il component contestuale mostri il nome utente ed il numero di articoli che l'utente ha pubblicato. In questo caso, specializza le impostazioni dello slot sidebar nel modulo view.yml, come mostrato nel Listato 7-40.

Listato 7-40 - Specializzare nello Slot Component 'sidebar', in frontend/modules/user/config/view.yml

all:
  components:
    sidebar:  [bar, user]

Le definizioni del component per gestire questo slot dovrebbero essere come quelle nel Listato 7-41.

Listato 7-41 - Component usati dallo Slot 'sidebar', in modules/bar/actions/components.class.php

class barComponents extends sfComponents
{
  public function executeDefault()
  {
  }

  public function executeUser()
  {
    $this->current_user = $this->getUser()->getCurrentUser();
    $c = new Criteria();
    $c->add(ArticlePeer::AUTHOR_ID, $this->current_user->getId());
    $this->nb_articles = ArticlePeer::doCount($c);
  }
}

Il Listato 7-42 mostra le viste per questi due component.

Listato 7-42 - Partial usati dagli Slot Component 'sidebar', in modules/bar/templates/

// _default.php
<p>Questa zona contiene informazioni contestuali.</p>

// _user.php
<p>Nome utente: <?php echo $current_user->getName() ?></p>
<p><?php echo $nb_articles ?> articoli pubblicati</p>

I component slot possono essere usati per i breadcrumb, per le navigazioni contestuali, e per gli inserimenti dinamici di ogni tipo. Come component, possono essere usati nel layout globale e nei template normali, o anche in altri component. L'impostazione di configurazione del component di uno slot è sempre presa dalla configurazione dell'ultima action richiamata.

Se hai bisogno di sospendere l'uso di un component slot per un dato modulo, basta dichiarare un modulo/component vuoto per esso, come mostrato nel Listato 7-43.

Listato 7-43 - Disabiltare un Component Slot in view.yml

all:
  components:
    sidebar:  []

Escaping dell'Output

Quando inserisci dati dinamici in un template, devi sempre essere sicuro dell'integrità dei dati. Per esempio, se i dati provengono da un form riempito da un utente anonimo, c'è il rischio che possa includere script malevoli intesi a lanciare attacchi di tipo XSS. Devi riuscire a fare l'escaping dei dati di output, in modo che ogni tag HTML che esso contiene sia innocuo.

Come esempio, supponi che un utente riempia un campo input col seguente valore:

<script>alert(document.cookie)</script>

Se mostri questo valore senza cautele, Javascript lo eseguirà sul browser e consentirà attacchi ben più pericolosi che mostrare un avviso. Questo è il motivo per cui devi fare l'escaping del valore prima di mostrarlo, in modo che diventi qualcosa come:

&lt;script&gt;alert(document.cookie)&lt;/script&gt;

Potresti fare manualmente l'escaping racchiudendo ogni valore insicuro in una chiamata a htmlspeciachars(), ma questo approccio sarebbe molto ripetitivo ed esposto ad errori. Invece, symfony fornisce un sistema speciale, chiamato escaping dell'output, che fa automanticamente l'escaping di ogni variabile mostrata in un template. Viene attivato da un semplice parametro in settings.yml dell'applicazione.

Attivare l'Esacaping dell'Output

L'escaping dell'output è configurato globalmente per un'applicazione nel file settings.yml. Due parameteri controllano il modo in cui funziona l'escaping dell'output: la strategia determina come le variabili sono rese disponibili alla vista, a il metodo è la funzione predefinita di escaping da applicare ai dati.

In parole povere, tutto quello che hai bisogno di fare per attivare l'escaping dell'output è impostare il parametro escaping_strategy a on al posto del suo valore predefinito di off, come mostrato nel Listato 7-44.

Listato 7-44 - Attivare l'Escaping dell'Output, in frontend/config/settings.yml

all:
  .settings:
    escaping_strategy: on
    escaping_method:   ESC_SPECIALCHARS

Questo aggiungerà htmlspecialchars() di default all'output di tutte le variabili. Per esempio, supponi di aver definito una variabile test in una action, come segue:

$this->test = '<script>alert(document.cookie)</script>';

Con l'escaping dell'output attivato, l'echo di questa variabile mostrerà i dati con escaping:

echo $test;
 => &lt;script&gt;alert(document.cookie)&lt;/script&gt;

Inoltre, ogni template ha accesso alla variabile $sf_data, che è un oggetto contenitore che fa riferimento a tutte le variabili con escaping. Quindi puoi anche mostrare la variabile test come segue:

echo $sf_data->get('test');
=> &lt;script&gt;alert(document.cookie)&lt;/script&gt;

TIP L'oggetto $sf_data implementa l'interfaccia Array, quindi invece di usare $sf_data->get('myvariable'), puoi recuperare i valori con escaping chiamando $sf_data['myvariable']. Ma non è una vera array, quindi funzioni come print_r() non funzioneranno come ti aspetti.

$sf_data dà accesso anche ai dati senza escaping, o dati grezzi. Questo è utile quando una variabile contiene del codice HTML pensato per essere interpretato dal browser, a patto che tu ti fidi di tale variabile. Richiama il metodo getRaw() quando hai bisogno di mostrare dati grezzi.

echo $sf_data->getRaw('test');
 => <script>alert(document.cookie)</script>

Accederai ai dati grezzi ogni volte che avrai bisogno di variabili che contengono HTML da interpretare veramente come HTML. Puoi capire perché il layout predefinito usi $sf_data->getRaw('sf_content') per includere il template, piuttosto che il più semplice $sf_content, che non va bene quando l'escaping dell'output è attivo.

Quando escaping_strategy è a off, $sf_data è sempre disponibile, ma restituisce sempre dati grezzi.

TIP Symfony 1.0 aveva due altri possibili valori per escaping_strategy. bc ora ritorna su off, e both ora ritorna su on. L'utilizzo di tali valori funziona ancora, ma inserire un errore nei log.

Helper per l'Escaping

Gli helper per l'escaping sono funzioni che restituiscono una versione con escaping del loro input. Possono essere usati come escaping_method di default nel file settings.yml oppure per specificare un metodo di escaping per un valore specifico nella vista. I seguenti helper di escaping sono disponibili:

  • ESC_RAW: Non fa escaping.
  • ESC_SPECIALCHARS: Applica la funzione PHP htmlspecialchars().
  • ESC_ENTITIES: Applica la funzione PHP htmlentities() con ENT_QUOTES.
  • ESC_JS: Fa escaping di un valore da inserire in una stringa Javascript da usare come HTML. Utile per fare escaping di parti in cui l'HTML deve essere cambiato dinamicamente usando Javascript.
  • ESC_JS_NO_ENTITIES: Fa escaping di un valore da inserire in una string Javascript ma non aggiunge le entità. Utile se il valore deve essere mostrato usando un box di dialogo (per esempio una variabile myString usata in javascript:alert(myString);).

Escaping di Array e Oggetti

L'escaping dell'output non funziona solo per le stringhe, ma anche per array e oggetti. Ogni valore che è un oggetto o array passerà al suo stato con escaping nei suoi figli. Ipotizzando che la strategia sia a on, il Listato 7-45 dimostra la cascata dell'escaping.

Listato 7-45 - Escaping anche per Array e Oggetti

// Definizione di una classe
class myClass
{
  public function testSpecialChars($value = '')
  {
    return '<'.$value.'>';
  }
}

// Nella action
$this->test_array = array('&', '<', '>');
$this->test_array_of_arrays = array(array('&'));
$this->test_object = new myClass();

// Nel template
<?php foreach($test_array as $value): ?>
  <?php echo $value ?>
<?php endforeach; ?>
 => &amp; &lt; &gt;
<?php echo $test_array_of_arrays[0][0] ?>
 => &amp;
<?php echo $test_object->testSpecialChars('&') ?>
 => &lt;&amp;&gt;

Di fatto, le variabili nel template non sono del tipo che potresti aspettarti. Il sistema di escaping dell'output le "decora" e le trasforma in oggetti speciali:

<?php echo get_class($test_array) ?>
 => sfOutputEscaperArrayDecorator
<?php echo get_class($test_object) ?>
 => sfOutputEscaperObjectDecorator

Questo spiega perché alcune comuni funzioni PHP (come array_shift(), print_r(), ecc.) non funzionano più su array con escaping. Ma possono sempre essere accedute usando [], essere scorse usando foreach, e danno il risultato corretto con count() (count() funziona solo con PHP 5.2 o successivi). Inoltre nei template i dati dovrebbero essere di sola lettura, quindi la maggior parte degli accessi sarà tramite metodi che funzionano.

C'è ancora un modo per recuperare i dati grezzi usando l'oggetto $sf_data. Inoltre, i metodi sugli oggetti con escaping sono alterati per accettare un parametro opzionale: un metodo di escaping. Quindi puoi scegliere un modo alternativo di escaping ogni volta che mostri una variabile in un template, oppure optare per l'helper ESC_RAW per disattivare l'escaping. Si veda il Listato 7-46 per un esempio.

Listato 7-46 - I Metodi degli Oggetti con Escaping accettano Parametri Addizionali

<?php echo $test_object->testSpecialChars('&') ?>
=> &lt;&amp;&gt;
// Le tre righe seguenti restituiscono lo stesso valore
<?php echo $test_object->testSpecialChars('&', ESC_RAW) ?>
<?php echo $sf_data->getRaw('test_object')->testSpecialChars('&') ?>
<?php echo $sf_data->get('test_object', ESC_RAW)->testSpecialChars('&') ?>
 => <&>

Se hai a che fare con oggetti nei tuoi template, userai i parametri addizionali spesso, poiché sono il modo più veloce per ottenere dati grezzi da chiamate a metodi.

CAUTION Le solite variabili di symfony subiscono l'escaping quando si attiva l'escaping. Quindi fai attenzione perché $sf_user, $sf_request, $sf_param, e $sf_context funzioneranno ancora, ma i loro metodo restuitiranno dati con escaping, a meno che tu non aggiunga ESC_RAW come argomento finale alle chiamate ai loro metodi. TIP '''Nuovo in symfony 1.1''': Anche se XSS è uno dei modi più comuni per bucare i siti, non è l'unico. Anche CSRF è molto diffuso e symfony fornisce un semplice modo per proteggere la tua applicazione. Leggi la parte chiamata "Il filtro CSRF" nel Capitolo 6 per maggiori informazioni.

Riepilogo

Ogni genere di strumento è disponibile per manipolare il livello di presentazione. I template si costruiscono in pochi secondi, grazie agli helper. I layout, i partial, i component, e i component slot aggiungono modularità e riusabilità. La configurazione della vista si avvantaggia di ogni modifica alla presentazione che dipenda da dati dinamici, la action ha accesso all'oggetto sfResponse. E la vista è protetta da attacchi XSS, grazie al sistema di escaping dell'output.