PageRenderTime 65ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/gentle-introduction/it/07-Inside-the-View-Layer.markdown

https://gitlab.com/intelij/symfony1-docs
Markdown | 1005 lines | 703 code | 302 blank | 0 comment | 0 complexity | 29713901d2c33667fcbce7fc0c940639 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, CC-BY-SA-4.0
  1. Capitolo 7 - All'interno del livello della vista
  2. ================================================
  3. La vista è la responsabile per la visualizzazione dell'output relativo a una particolare azione. In symfony, la vista è costituita da più parti, di cui ogni singolo elemento è stato progettato per essere facilmente modificato da chi di solito lavora con esso.
  4. * I web designer in genere lavorano sui template (la presentazione dei dati dell'azione corrente) e sul layout (che contiene il codice comune a tutte le pagine). Questi sono scritti in HTML con l'inclusione di piccoli pezzi in PHP, che sono per lo più chiamate a helper.
  5. * Gli sviluppatori, avendo in mente la riusabilità, di solito inseriscono i frammenti di codice in partial o component. Usano gli slot per agire su più di una zona del layout. Anche i web designer possono lavorare su questi frammenti di template.
  6. * L'attenzione degli sviluppatori va anche sul file di configurazione view in YAML (impostando le proprietà della risposta e degli altri elementi dell'interfaccia) e sull'oggetto response. Quando si ha a che fare con variabili nei template, i rischi di cross-site scripting non devono essere ignorati e una buona comprensione delle tecniche di escape dell'output è richiesta per trattare in modo sicuro i dati dell'utente.
  7. Ma qualunque sia il proprio ruolo, si troveranno utili strumenti per accelerare il noioso lavoro della visualizzazione dei risultati dell'azione. Questo capitolo comprende tutti questi strumenti.
  8. I template
  9. ----------
  10. Il listato 7-1 mostra un classico template di symfony. Contiene del codice HTML e un po' di codice PHP semplice, generalmente chiamate a variabili definite nell'azione (attraverso `$this->nome = 'foo';`) ed helper.
  11. Listato 7-1 - Un template di esempio indexSuccess.php
  12. [php]
  13. <h1>Benvenuto</h1>
  14. <p>Bentornato, <?php echo $nome ?>!</p>
  15. <h2>Cosa ti piacerebbe fare?</h2>
  16. <ul>
  17. <li><?php echo link_to('Leggere gli ultimi articoli', 'articolo/read') ?></li>
  18. <li><?php echo link_to('Iniziare a scriverne uno nuovo', 'articolo/write') ?></li>
  19. </ul>
  20. Come spiegato nel capitolo 4, nei template è preferibile la sintassi alternativa di PHP, in modo da renderli più leggibili anche per chi non è uno sviluppatore di PHP. Nei template si consiglia di utilizzare il minimo indispensabile di codice PHP, poiché questi file sono quelli usati per progettare la grafica dell'applicazione e spesso sono creati e mantenuti da un altro gruppo, specializzato nella presentazione, ma non nella logica dell'applicazione. Mantenere la logica all'interno dell'azione rende anche più facile avere template diversi per una singola azione, senza alcuna duplicazione del codice.
  21. ### Gli helper
  22. Gli helper sono funzioni PHP che restituiscono codice HTML e possono essere utilizzate nei template. Nel listato 7-1, la funzione `link_to()` è un helper. A volte gli helper servono solo per risparmiare tempo, impacchettando frammenti di codice utilizzati frequentemente nei template. Per esempio, si può facilmente immaginare la definizione della funzione di questo helper:
  23. [php]
  24. <?php echo image_tag('photo.jpg') ?>
  25. => <img src="/images/photo.jpg" />
  26. Dovrebbe essere simile a quella del listato 7-2.
  27. Listato 7-2 - Esempio di definizione di un helper
  28. [php]
  29. function image_tag($source)
  30. {
  31. return '<img src="/images/'.$source.'" />';
  32. }
  33. In realtà la funzione `image_tag()` presente in symfony è un po' più complicata d iqeusta, dal momento che accetta un secondo parametro per aggiungere altri attributi al tag `<img>`. Si può vedere la sintassi completa e le opzioni nella [Documentazione con le API](http://www.symfony-project.org/api/1_4/) presente online.
  34. Molte volte, gli helper sono intelligenti e fanno risparmiare codice:
  35. [php]
  36. <?php echo auto_link_text('Visitate il nostro sito web www.example.com') ?>
  37. => Visitate il nostro sito web <a href="http://www.example.com">www.example.com</a>
  38. Gli helper facilitano il processo di scrittura dei template e generano il miglior codice HTML possibile in quanto a prestazioni e accessibilità. Si può sempre utilizzare il semplice HTML, ma gli helper in genere sono più veloci da scrivere.
  39. >**TIP**
  40. >Ci si potrebbe chiedere perché gli helper sono nominati utilizzando la convenzione del trattino basso piuttosto che quella camelCase, utilizzata ovunque in symfony. Il motivo è che gli helper sono funzioni e tutte le funzioni native di PHP usano la convenzione del trattino basso.
  41. #### Dichiarare gli helper
  42. I file di symfony contenenti le dichiarazioni degli helper non sono caricati in automatico (questo perché contengono funzioni, non classi). Gli helper sono raggruppati per scopo. Ad esempio, tutte le funzioni degli helper inerenti i testi sono definite in un file chiamato `TextHelper.php` che contiene il gruppo di helper chiamato `Text`. Se si ha bisogno di usare un helper in un template, bisogna prima caricare il relativo gruppo di helper tramite la funzione `use_helper()`. Il listato 7-3 mostra un template usando l'helper `auto_link_text()` che fa parte del gruppo di helper `Text`.
  43. Listato 7-3 - Dichiarare l'utilizzo di un helper
  44. [php]
  45. // Usare uno specifico gruppo di helper in questo template
  46. <?php use_helper('Text') ?>
  47. ...
  48. <h1>Descrizione</h1>
  49. <p><?php echo auto_link_text($description) ?></p>
  50. >**TIP**
  51. >Quando si ha necessità di dichiarare più di un gruppo di helper, aggiungere più parametri alla chiamata `use_helper()`. Ad esempio, per caricare entrambi i gruppi di helper `Text` e `Javascript` in un template, chiamare `<?php use_helper('Text', 'Javascript') ?>`.
  52. Alcuni helper sono disponibili per impostazione predefinita su ogni template, senza la necessità di dichiararli. Sono quelli che appartengono ai seguenti gruppi di helper:
  53. * `Helper`: Richiesto dall'helper di inclusione (infatti la funzione `use_helper()` è a sua volta un helper)
  54. * `Tag`: Helper di base per i tag, usato in quasi tutti gli helper
  55. * `Url`: Helper per i link e la gestione degli URL
  56. * `Asset`: Helper per popolare la sezione HTML `<head>` e fornire facilmente link a risorse esterne (file di immagini, JavaScript e fogli di stile)
  57. * `Partial`: Helper che permettono l'inclusione di frammenti di template
  58. * `Cache`: Manipolazione di frammenti di codice presenti nella cache
  59. L'elenco degli helper standard, caricato in modalità predefinita per ogni template, è configurabile nel file `settings.yml`. Quindi, ad esempio se non si dovessero utilizzare gli helper del gruppo `Cache`, o se si dovessero sempre utilizzare quelli del gruppo `Text`, si può modificare di conseguenza l'impostazione `standard_helpers`. Ciò permetterà di accelerare leggermente l'applicazione. Non è possibile rimuovere i primi quattro gruppi di helper della lista precedente (`Helper`, `Tag`, `Url` e `Asset`), perché essi sono obbligatori affiché il motore di template funzioni correttamente. Di conseguenza, essi non compaiono neppure nella lista degli helper standard.
  60. >**TIP**
  61. >Se si dovesse utilizzare un helper fuori dal template, è possibile caricare un gruppo di helper da qualsiasi parte chiamando `sfProjectConfiguration::getActive()->loadHelpers($helpers)`, dove `$helpers` è il nome del gruppo di helper o un di nomi di gruppi di helper. Ad esempio, se si vuole usare `auto_link_text()` in una azione, bisogna prima chiamare `sfProjectConfiguration::getActive()->loadHelpers('Text')`.
  62. #### Helper utilizzati frequentemente
  63. Nei capitoli successivi verranno mostrati in dettaglio alcuni helper, in relazione con le caratteristiche prese in esame. Il listato 7-4 un breve elenco degli helper predefiniti che vengono utilizzati di frequente, assieme al codice HTML che restituiscono.
  64. Listato 7-4 - Gli helper predefiniti più comuni
  65. [php]
  66. // Gruppo Helper
  67. <?php use_helper('NomeHelper') ?>
  68. <?php use_helper('NomeHelper1', 'NomeHelper2', 'NomeHelper3') ?>
  69. // Gruppo Url
  70. <?php echo link_to('clicca', 'miomodulo/miaazione') ?>
  71. => <a href="/rotta/alla/miaazione">clicca</a> // Dipende dalla configurazione delle rotte
  72. // Gruppo Asset
  73. <?php echo image_tag('miaimmagine', 'alt=foo size=200x100') ?>
  74. => <img src="/images/miaimmagine.png" alt="foo" width="200" height="100"/>
  75. <?php echo javascript_include_tag('mioscript') ?>
  76. => <script language="JavaScript" type="text/javascript" src="/js/mioscript.js"></script>
  77. <?php echo stylesheet_tag('style') ?>
  78. => <link href="/stylesheets/style.css" media="screen" rel="stylesheet"type="text/css" />
  79. Ci sono molti altri helper in symfony, ci vorrebbe un libro intero per descriverli tutti. Il migliore riferimento per gli helper è la [documentazione delle API](http:// www.symfony-project.org/api/1_4/) online, dove sono ben documentati tutti gli helper, con la loro sintassi, le opzioni e gli esempi.
  80. #### Aggiungere i propri helper
  81. Symfony ha molti helper per vari diversi utilizzi, ma se non si trova quello di cui si ha bisogno nella documentazione delle API, probabilmente si vorrà crearne uno nuovo. Questo è un compito molto semplice.
  82. Le funzioni per gli helper (normali funzioni PHP che restituiscono codice HTML) devono essere salvate in un file chiamato `FooBarHelper.php`, dove `FooBar` è il nome del gruppo di helper. Salvare il file nella cartella `apps/frontend/lib/helper/` (o in qualunque cartella `helper/` creata dentro una delle cartelle `lib/` del progetto) in modo che possa essere trovata automaticamente dall'helper per l'inclusione `use_helper('FooBar')`.
  83. >**TIP**
  84. >Questo sistema permette anche di sovrascrivere gli helper esistenti di symfony. Ad esempio, per ridefinire tutti gli helper del gruppo `Text`, basta creare un file `TextHelper.php` nella cartella `apps/frontend/lib/helper/`. Ogni volta che verrà chiamato `use_helper('Text')`, symfony userà il vostro gruppo di helper al posto del suo. Ma attenzione: poiché il file originale non viene caricato, è necessario ridefinire tutte le funzioni di un gruppo di helper per sovrascriverlo; in caso contrario, alcuni degli helper originali non saranno disponibili.
  85. ### Layout della pagina
  86. Il template mostrato nel listato 7-1 non è un documento XHTML valido. Mancano la definizione del `DOCTYPE` e i tag `<html>` e `<body>`. Questo perché vengono memorizzati in un'altra parte dell'applicazione, in un file chiamato `layout.php`, che contiene il layout della pagina. Questo file, chiamato anche template globale, memorizza il codice HTML che è comune a tutte le pagine dell'applicazione per evitare di ripeterlo per ciascun template. Il contenuto del template è integrato nel layout o, cambiando il punto di vista, il layout "decora" il template. Questa è una applicazione del pattern decorator design, mostrata in figura 7-1.
  87. >**TIP**
  88. >Per maggiori informazioni sul decorator e gli altri design pattern, vedere *Patterns of Enterprise Application Architecture* di Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0).
  89. Figura 7-1 - Decorare un template con un layout
  90. ![Decorare un template con un layout](http://www.symfony-project.org/images/book/1_4/F0701.png "Decorare un template con un layout")
  91. Listato 7-5 mostra la pagina predefinita del layout, presente nella cartella dell'applicazione `templates/`.
  92. Listatog 7-5 - Layout predefinito, in `myproject/apps/frontend/templates/layout.php`
  93. [php]
  94. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  95. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  96. <head>
  97. <?php include_javascripts() ?>
  98. <?php include_stylesheets() ?>
  99. <?php include_http_metas() ?>
  100. <?php include_metas() ?>
  101. <?php include_title() ?>
  102. <link rel="shortcut icon" href="/favicon.ico" />
  103. </head>
  104. <body>
  105. <?php echo $sf_content ?>
  106. </body>
  107. </html>
  108. Gli helper chiamati nella sezione `<head>` recuperano le informazioni dall'oggetto response e dalla configurazione view. Il tag `<body>` mostra il risultato del template. Con questo layout, la configurazione predefinita e il template di esempio nel listato 7-1 la vista elaborata è simile a quanto si vede nel listato 7-6.
  109. Listato 7-6 - Il Layout, la configurazione View e il template assemblato.
  110. [php]
  111. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  112. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  113. <head>
  114. <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  115. <meta name="title" content="symfony project" />
  116. <meta name="robots" content="index, follow" />
  117. <meta name="description" content="symfony project" />
  118. <meta name="keywords" content="symfony, project" />
  119. <title>symfony project</title>
  120. <link rel="stylesheet" type="text/css" href="/css/main.css" />
  121. <link rel="shortcut icon" href="/favicon.ico">
  122. </head>
  123. <body>
  124. <h1>Benvenuto</h1>
  125. <p>Bentornato, <?php echo $nome ?>!</p>
  126. <h2>Cosa vuoi fare?</h2>
  127. <ul>
  128. <li><?php echo link_to('Leggere gli ultimi articoli', 'articolo/read') ?></li>
  129. <li><?php echo link_to('Scriverne uno nuovo', 'articolo/write') ?></li>
  130. </ul>
  131. </body>
  132. </html>
  133. Il template globale può essere completamente personalizzato per ciascuna applicazione. Aggiungerlo in ogni codice HTML in cui se ne ha bisogno. Questo layout spesso è usato per mantenere la navigazione del sito, i logo e altro. Si può anche avere più di un layout e decidere quale layout dovrebbe essere usato per ciascuna azione. Per ora non preoccuparsi delle inclusioni di JavaScript e fogli di stile; La sezione "Configurazione della vista" mostrerà più avanti come ottenere ciò.
  134. ### Scorciatoie nei template
  135. Nei template, alcune variabili symfony sono sempre disponibili. Queste scorciatoie forniscono l'accesso alla maggior parte delle informazioni necessarie nei template, attraverso oggetti del nucleo di symfony:
  136. * `$sf_context`: L'intero oggetto context (`istanza di sfContext`)
  137. * `$sf_request`: L'oggetto request(`istanza di sfRequest`)
  138. * `$sf_params` : I parametri dell'oggetto request
  139. * `$sf_user` : L'oggetto corrente di sessione utente (`istanza di sfUser`)
  140. Il capitolo precedente ha mostrato utili metodi per gli oggetti `sfRequest` e `sfUser`. Si possono chiamare questi metodi nei template attraverso le variabili `$sf_request` e `$sf_user`. Ad esempio, se la request include un parametro `total`, il suo valore è disponibile nel template tramite:
  141. [php]
  142. // Versione lunga
  143. <?php echo $sf_request->getParameter('total') ?>
  144. // Versione breve
  145. <?php echo $sf_params->get('total') ?>
  146. // Equivalente al seguente codice dell'azione
  147. echo $request->getParameter('total')
  148. Frammenti di codice
  149. -------------------
  150. Spesso c'è la necessità di includere del codice HTML o PHP su più pagine. Per evitare la ripetizione del codice, la maggior parte delle volte è sufficiente il comando `include()`.
  151. Per esempio, se molti template dell'applicazione hanno bisogno di utilizzare lo stesso frammento di codice, salvarlo in un file chiamato `mioFrammento.php` nella cartella globale dei template (`mioprogetto/apps/frontend/templates/`) e includerlo nei template come segue:
  152. [php]
  153. <?php include(sfConfig::get('sf_app_template_dir').'/mioFrammento.php') ?>
  154. Ma questo non è un modo molto pulito per gestire un frammento, soprattutto perché possono esserci nomi di variabili differenti tra il frammento e i vari template che lo includono. Inoltre, il sistema per la cache di symfony (descritto nel capitolo 12) non ha modo di rilevare un include, quindi il frammento non può essere messo in cache indipendentemente dal template. Symfony fornisce tre tipi alternativi di frammenti di codice per sostituire gli `include`:
  155. * Se la logica è leggera, basterà includere un file template che ha accesso ad alcuni dati che gli vengono passati. Per questo si userà un partial.
  156. * Se la logica è più pesante (ad esempio, se è necessario accedere al modello dei dati e/o modificare il contenuto in base alla sessione), è preferibile separare la presentazione dalla logica. Per questo, si userà un component.
  157. * Se il frammento ha lo scopo di sostituire una parte specifica del layout, per il quale può già esistere un contenuto predefinito, si userà uno slot.
  158. L'inserimento di questi frammenti si ottiene con gli helper del gruppo `Partial`. Questi helper sono disponibili da ogni template symfony, senza aver bisogno della dichiarazione iniziale.
  159. ### Partial
  160. Un partial è un pezzo di codice del template riutilizzabile. Ad esempio, in una applicazione per pubblicare articoli, il codice del template che mostra un articolo è usato nella pagina con l'articolo completo, nell'elenco dei migliori articoli e nell'elenco degli ultimi articoli. Questo codice è un candidato perfetto per un partial, come mostrato nella figura 7-2.
  161. Figura 7-2 - Il riutilizzo dei partial nei template
  162. ![Il riutilizzo dei partial nei template](http://www.symfony-project.org/images/book/1_4/F0702.png "Il riutilizzo dei partial nei template")
  163. Proprio come i template, i partial sono file che si trovano nella cartella `templates/` e contengono codice HTML con del codice PHP. Il nome file di un partial inizia sempre con un carattere di sottolineatura (`_`) e questo aiuta a distinguere i partial dai template, poiché sono situati nelle stesse cartelle `templates/`.
  164. Un template può includere partial se è nello stesso modulo, in un altro modulo, o nella cartella `templates/` globale. Includere un partial usando un helper `include_partial()` e specificare il nome del modulo e del partial come parametro (omettendo la sottolineatura e il `.php` finale), come descritto nel listato 7-7.
  165. Listato 7-7 - Includere un partial in un template del modulo `miomodulo`
  166. [php]
  167. // Include il partial frontend/modules/miomodulo/templates/_miopartial1.php
  168. // Poiché il template e il partial sono nello stesso modulo,
  169. // si può omettere il nome del modulo
  170. <?php include_partial('miopartial1') ?>
  171. // Include il partial frontend/modules/pippo/templates/_mypartial2.php
  172. // In questo caso il nome del modulo è obbligatorio
  173. <?php include_partial('pippo/miopartial2') ?>
  174. // Include il partial frontend/templates/_mypartial3.php
  175. // È considerato parte del modulo 'global'
  176. <?php include_partial('global/miopartial3') ?>
  177. I partial hanno accesso ai normali helper di symfony e alle scorciatoie dei template. Ma poiché i partial possono essere chiamati da qualsiasi punto dell'applicazione, non hanno accesso automatico alle variabili definite nelle azioni che chiamano i template stessi, a meno che non siano esplicitamente passate come parametro. Ad esempio, se si vuole che un partial abbia accesso a una variabile `$totale`, l'azione deve passarla ai template e poi il template all'helper come secondo parametro della chiamata `include_partial()`, come mostrato nei listati 7-8, 7-9 e 7-10.
  178. Listato 7-8 - L'azione definisce una variabile, in `miomodulo/actions/actions.class.php`
  179. [php]
  180. class mymoduleActions extends sfActions
  181. {
  182. public function executeIndex()
  183. {
  184. $this->totale = 100;
  185. }
  186. }
  187. Listato 7-9 - Il template passa la variabile al partial, in `miomodulo/templates/indexSuccess.php`
  188. [php]
  189. <p>Buongiorno, mondo!</p>
  190. <?php include_partial('miopartial', array('miototale' => $totale)) ?>
  191. Listato 7-10 - Il partial ora può usare la variabile, in `miomodulo/templates/_miopartial.php`
  192. [php]
  193. <p>Totale: <?php echo $miototale ?></p>
  194. >**TIP**
  195. >Tutti gli helper fin'ora sono stati chiamati da `<?php echo nomeFunzione() ?>`. L'helper partial, però, è chiamato semplicemente da `<?php include_partial() ?>`, senza `echo`, in modo che abbia un comportamento simile al normale comando PHP `include()`. Se si ha bisogno di una funzione che restituisca il contenuto di un partial senza visualizzarlo, utilizzare `get_partial()`. Tutti gli helper `include_` descritti in questo capitolo hanno una controparte `get_` che può essere chiamata insieme al comando `echo`.
  196. >**TIP**
  197. >Invece di visualizzare un template, una azione può restituire un partial o un component. I metodi `renderPartial()` e `renderComponent()` della classe dell'azione promuovono la riusabilità del codice. Inoltre sfruttano la possibilità dei partial di essere messi in cache (vedere il capitolo 12). Le variabili definite nell'azione verranno passate automaticamente al partial/component, a meno che non si definisca un array associativo di varibili come secondo paramentro del metodo.
  198. >
  199. > [php]
  200. > public function executeFoo()
  201. > {
  202. > // do things
  203. > $this->foo = 1234;
  204. > $this->bar = 4567;
  205. >
  206. > return $this->renderPartial('miomodulo/miopartial');
  207. > }
  208. >
  209. >In questo esempio, il partial avrà accesso a `$foo` e `$bar`. Se l'azione termina con le seguenti righe:
  210. >
  211. > return $this->renderPartial('miomodulo/miopartial', array('foo' => $this->foo));
  212. >
  213. >allora il partial ha solo accesso a `$foo`.
  214. ### Component
  215. Nel capitolo 2, il primo script di esempio è stato spezzato in due parti per separare la logica dalla presentazione. Proprio come il pattern MVC si applica alle azioni e ai template, può essere necessario dividere un partial in una parte di logica e in una parte di presentazione. In tal caso, è necessario utilizzare un componente.
  216. Un componente è come una azione, salvo il fatto che è molto più veloce. La logica di un componente è all'interno di una classe che eredita da `sfComponents`, situata in un file `actions/components.class.php`. La sua presentazione è è messa in un partial. I metodi di una classe `sfComponents` iniziano con la parola `execute`, proprio come le azioni e possono passare variabili ai loro controparti della presentazione nello stesso modo con cui le azioni passano variabili. I partial che vengono utilizzati come presentazione per componenti sono nominati con lo stesso nome del componente (senza l'`execute`, ma con una sottolineatura iniziale). La Tabella 7-1 compara le convenzioni per i nomi per azioni e componenti.
  217. Table 7-1 - Convenzioni per i nomi di azioni e componenti
  218. Convenzione | Azioni | Componenti
  219. ------------------------------------ | ---------------------- | ----------------------
  220. File con la logica | `actions.class.php` | `components.class.php`
  221. La classe con la logica estende | `sfActions` | `sfComponents`
  222. Nomi per i metodi | `executeMiaAzione()` | `executeMioComponente()`
  223. Nomi dei file con la presentazione | `miaAzioneSuccess.php` | `_mioComponente.php`
  224. >**TIP**
  225. >Così come è possibile separare i file delle azioni, la classe `sfComponents` ha una controparte `sfComponent` che permette ai singoli file dei componenti lo stesso tipo di sintassi.
  226. Per esempio, supponiamo di avere una barra laterale che mostra le ultime notizie di un dato soggetto, a seconda del profilo dell'utente, che viene riutilizzato in diverse pagine. Le query necessarie a ottenere le notizie sono troppo complesse per apparire in una semplice partial, quindi hanno bisogno di essere spostate in un qualcosa simile a una azione, un componente. La figura 7-3 mostra questo esempio
  227. Per questo esempio, mostrato nei listati 7-11 e 7-12, il componente verrà tenuto nel proprio modulo (chiamato `novita`), ma si possono mischiare componenti e azioni in un singolo modulo se questo ha senso da un punto di vista funzionale.
  228. Figura 7-3 - Usare i componenti nei template
  229. ![Usare i componenti nei template](http://www.symfony-project.org/images/book/1_4/F0703.png "Usare i componenti nei template")
  230. Listato 7-11 - La classe Components, in `modules/novita/actions/components.class.php`
  231. [php]
  232. <?php
  233. class novitaComponents extends sfComponents
  234. {
  235. public function executeHeadlines()
  236. {
  237. // Propel
  238. $c = new Criteria();
  239. $c->addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
  240. $c->setLimit(5);
  241. $this->novita = NewsPeer::doSelect($c);
  242. // Doctrine
  243. $query = Doctrine::getTable('Novita')
  244. ->createQuery()
  245. ->orderBy('pubblicato_il DESC')
  246. ->limit(5);
  247. $this->novita = $query->execute();
  248. }
  249. }
  250. Listato 7-12 - Il partial, in `modules/novita/templates/_headlines.php`
  251. [php]
  252. <div>
  253. <h1>Latest news</h1>
  254. <ul>
  255. <?php foreach($news as $headline): ?>
  256. <li>
  257. <?php echo $headline->getPublishedAt() ?>
  258. <?php echo link_to($headline->getTitle(),'news/show?id='.$headline->getId()) ?>
  259. </li>
  260. <?php endforeach ?>
  261. </ul>
  262. </div>
  263. Ora ogni volta che si avrà bisogno del componente in un template, basterà chiamare:
  264. [php]
  265. <?php include_component('novita', 'headlines') ?>
  266. Come i partial, i componenti accettano parametri aggiuntivi nella forma di un array associativo. I parametri sono disponibili al partial con il loro nome e nel componente attraverso l'oggetto `$this`. Per un esempio, vedere il listato 7-13.
  267. Listato 7-13 - Passare i parametri a un componente e ai suoi template
  268. [php]
  269. // Chiamare il componente
  270. <?php include_component('novita', 'headlines', array('foo' => 'bar')) ?>
  271. // Nel componente stesso
  272. echo $this->foo;
  273. => 'bar'
  274. // Nel partial _headlines.php
  275. echo $foo;
  276. => 'bar'
  277. Si possono includere componenti in componenti, o nel layout globale, così come in ogni normale template. Come nelle azioni, i metodi `execute` dei componenti possono passare variabili ai relativi partial e avere accesso alle stesse scorciatoie. Ma le similitudini si fermano qui. Un componente non gestisce la sicurezza o la validazione, non può essere chiamato da Internet (solo dall'applicazione stessa) e non ha le varie possibilità di return. Questo è il motivo per cui un componente nell'esecuzione è più veloce di un'azione.
  278. ### Gli slot
  279. I partial e i componenti sono ottimi per la riusabilità. Ma in molti casi i frammenti di codice devono andare a comporre un layout con più di una zona dinamica. Ad esempio, supponiamo che si desideri aggiungere alcuni tag personalizzati nella sezione `<head>` del layout, in base al contenuto di una azione. Oppure supponiamo che il layout abbia una zona dinamica principale, composta dal risultato di una azione, più numerose altre azioni più piccole, con un contenuto che è predefinito nel layout, ma che può essere sovrascritto a livello di template.
  280. Per queste situazioni, la soluzione è uno slot. In sostanza, uno slot è un segnaposto che si può mettere in qualsiasi degli elementi della vista (layout, template o partial). Riempire questo segnaposto è come impostare una variabile. Il codice di riempimento è memorizzato nella risposta a livello globale, in modo da poterlo definire ovunque (nel layout, template o partial). Basta fare in modo di definire uno slot prima di includerlo e ricordare che il layout viene eseguito dopo il template (processo di decorazione) e che i partial vengono eseguiti quando sono chiamati in un template. Il tutto sembra un po' troppo astratto? Vediamo un esempio.
  281. Immaginiamo un layout con una zona per il template e due slot: uno per la barra laterale e l'altro per il piè di pagina. I valori per lo slot sono definiti nel template. Durante il processo di decorazione, il codice del layout avvolge il codice del template e gli slot sono riempiti con i valori definiti precedentemente, così come illustrato in figura 7-4. La barra laterale e il piè di pagina possono essere contestuali all'azione principale. È come avere un layout con più di un "buco".
  282. Figura 7-4 - Slot layout definiti in un template
  283. ![Slot layout definiti in un template](http://www.symfony-project.org/images/book/1_4/F0704.png "Slot layout definiti in un template")
  284. Vedere un po' di codice chiarirà ulteriormente le cose. Per includere uno slot, usare l'helper `include_slot()`. L'helper `has_slot()` restituisce `true` se lo slot è stato definito in precedenza, fornendo come bonus un meccanismo di fallback. Ad esempio, definire un segnaposto per uno slot `'sidebar'` nel layout e i suoi contenuti predefiniti, come mostrato nel listato 7-14.
  285. Listato 7-14 - Inclusione di uno slot `'sidebar'` nel layout
  286. [php]
  287. <div id="sidebar">
  288. <?php if (has_slot('sidebar')): ?>
  289. <?php include_slot('sidebar') ?>
  290. <?php else: ?>
  291. <!-- codice predefinito della barra laterale -->
  292. <h1>Zona contestuale</h1>
  293. <p>Questa zona contiene link e informazioni
  294. relative al contenuto principale della pagina.</p>
  295. <?php endif; ?>
  296. </div>
  297. Capita abbastanza frequentemente di dover mostrare dei contenuti predefiniti se uno slot non è definito e per questo scopo l'helper `include_slot` restituisce un valore booleano, che indica se lo slot è stato definito. Il listato 7-15 mostra come utilizzare questo valore in modo da semplificare il codice.
  298. Listato 7-15 - Inclusione di uno slot `'sidebar'` nel layout
  299. [php]
  300. <div id="sidebar">
  301. <?php if (!include_slot('sidebar')): ?>
  302. <!-- codice predefinito della barra laterale -->
  303. <h1>Zona contestuale</h1>
  304. <p>Questa zona contiene link e informazioni
  305. relative al contenuto principale della pagina.</p>
  306. <?php endif; ?>
  307. </div>
  308. Ciascun template ha la possibilità di definire i contenuti di uno slot (in realtà anche i partial sono in grado di farlo). Essendo gli slot destinati a contenere codice HTML, symfony offre un modo conveniente per definirli: basta scrivere il codice dello slot tra gli helper `slot()` e `end_slot()`, come mostrato nel listato 7-16.
  309. Listato 7-16 - Sovrascrivere il contenuto dello slot `'sidebar'` in un template
  310. [php]
  311. // ...
  312. <?php slot('sidebar') ?>
  313. <!-- codice per la barra laterale personalizzata per il template corrente -->
  314. <h1>Dettagli dell'utente</h1>
  315. <p>nome: <?php echo $user->getNome() ?></p>
  316. <p>email: <?php echo $user->getEmail() ?></p>
  317. <?php end_slot() ?>
  318. Il codice tra gli helper degli slot è eseguito nel contesto del template, quindi ha accesso a tutte le variabili che sono state definite nell'azione. Symfony metterà automaticamente il risultato dell'esecuzione del codice nell'oggetto response. Non verrà visualizzato nel template, ma reso disponibile per future chiamate `include_slot()`, come quella mostrata nel listato 7-14.
  319. Gli slot sono molto utili per definire zone che devono mostrare dei contenuti contestuali. Possono anche essere usati per aggiungere codice HTML al layout solo per certe azioni. Ad esempio, un template che mostra l'elenco delle ultime news potrebbe volere aggiungere un link a un feed RSS nella zona `<head>` del layout. Lo si può fare semplicemente aggiungendo uno slot 'feed'` nel layout e sovrascrivendolo nel template dell'elenco.
  320. Se il contenuto dello slot è breve, per esempio come nel caso di uno slot `titolo`, si può semplicemente passare il contenuto come secondo parametro del metodo `slot()`, come mostrato nel listato 7-17.
  321. Listato 7-17 - Usare lo `slot()` per definire un contenuto breve
  322. [php]
  323. <?php slot('titolo', 'Il contenuto del titolo') ?>
  324. >**SIDEBAR**
  325. >Dove cercare i frammenti dei template
  326. >
  327. >Chi lavora sui template è in genere un web designer, che potrebbe non conoscere symfony molto bene e avere difficoltà a trovare i frammenti dei template, dal momento che possono essere sparsi in tutta l'applicazione. Queste brevi linee guida renderanno più comodo il dover lavorare con il sistema dei template di symfony.
  328. >
  329. >Prima di tutto, anche se un progetto symfony contiene molte cartelle, tutti i file dei layout, dei template e dei frammenti di template risiedono in cartelle chiamate `templates/`. Quindi, per quello che può interessare a un web designer, la struttura di un progetto può essere ridotta a qualcosa di questo tipo:
  330. >
  331. >
  332. > mioprogetto/
  333. > apps/
  334. > applicazione1/
  335. > templates/ # Layout per l'applicazione 1
  336. > modules/
  337. > modulo1/
  338. > templates/ # Template e partial per il modulo 1
  339. > modulo2/
  340. > templates/ # Template e partial per il modulo 2
  341. > modulo3/
  342. > templates/ # Template e partial per il modulo 3
  343. >
  344. >
  345. >Tutte le altre cartelle possono essere ignorate.
  346. >
  347. >Quando si trova un `include_partial()`, i web designer devono sapere che solo il primo parametro è importante. Lo schema di questo parametro è `nome_modulo/nome_partial` e ciò significa che il codice di presentazione si trova in `modules/nome_modulo/templates/_nome_partial.php`.
  348. >
  349. >Per l'helper `include_component()`, il nome del modulo e il nome del partial sono i primi due parametri. Per il resto, un'idea generale su cosa sono gli helper e su quali helper sono più utili nei template dovrebbe essere sufficiente per iniziare a progettare template per le applicazioni symfony.
  350. Configurazione della vista
  351. --------------------------
  352. In symfony, una vista consiste di due parti distinte:
  353. * La presentazione HTML del risultato dell'azione (memorizzata nel template, nel layout e nei frammenti di template)
  354. * Tutto il resto, comprese le seguenti cose:
  355. * Meta dichiarazioni: keywords, description o durata della cache.
  356. * Tag `title` della pagina: non solo aiuta gli utenti con numerose finestre aperte del browser a trovare quella giusta, ma è anche molto importante per l'indicizzazione nei motori di ricerca.
  357. * Inclusione di file: JavaScript e fogli di stile.
  358. * Layout: alcune azioni richiedono un layout personalizzato (popup, banner, ecc.) oppure anche nessun layout (ad esempio le azioni Ajax).
  359. Nella vista, tutto quello che non è HTML è chiamato configurazione della vista e symfony fornisce due modi per gestirla. Il modo principale è attraverso il file di configurazione `view.yml`. Può essere utilizzato ogni volta che i valori non dipendono dal contesto o per le query del database. Quando è necessario impostare valori dinamici, il metodo alternativo è quello di impostare la configurazione della vista attraverso gli attributi dell'oggetto `sfResponse`, direttamente nell'azione.
  360. >**NOTE**
  361. >Se si imposta un certo parametro della configurazione della vista sia attraverso l'oggetto `sfResponse` che attraverso il file `view.yml`, la definizione di `sfResponse` ha la precedenza.
  362. ### Il file `view.yml`
  363. Ogni modulo può avere un file `view.yml`, per definire la configurazione delle sue viste. Questo permette di definire le impostazioni di visualizzazione per un intero modulo e vista per vista in un singolo file. Le chiavi di primo livello del file `view.yml` sono i nomi dei moduli della vista. Il listato 7-18 mostra un esempio della configurazione della vista
  364. Listato 7-18 - Esempio Livello-Modulo `view.yml`
  365. editSuccess:
  366. metas:
  367. title: Modifica il tuo profilo
  368. editError:
  369. metas:
  370. title: Errore nella modifica del profilo
  371. all:
  372. stylesheets: [mio_style]
  373. metas:
  374. title: Il mio sito web
  375. >**CAUTION**
  376. >Bisogna tenere presente che le chiavi principali del file `view.yml` sono nomi di viste, non nomi di azioni. Inoltre bisogna ricordarsi che il nome di una vista è composta dal nome di una azione e dalla terminazione dell'azione. Ad esempio, se l'azione `edit` restituisce `sfView::SUCCESS` (o non restituisce nulla, dal momento che è la terminazione predefinita in una azione), allora il nome della vista è `editSuccess`.
  377. Le impostazioni predefinite per il modulo sono definite sotto la chiave `all:` nel modulo `view.yml`. Le impostazioni predefinite per tutte le viste dell'applicazione sono definite in `view.yml`. Anche qua vale il principio della configurazione a cascata:
  378. * In `apps/frontend/modules/miomodulo/config/view.yml`, le definizioni per vista si applicano solo a una vista e sovrascrivono le definizioni a livello di modulo.
  379. * In `apps/frontend/modules/miomodulo/config/view.yml`, le definizioni `all:` si applicano a tutte le azioni del modulo e sovrascrivono le definizioni a livello di applicazione.
  380. * In `apps/frontend/config/view.yml`, le definizioni `default:` si applicano a tutti i moduli e a tutte le azioni dell'applicazione.
  381. >**TIP**
  382. >I file `view.yml` a livello di modulo non esistono nella modalità predefinita. La prima volta che si ha necessità di modificare un parametro della configurazione della vista per un modulo, bisogna creare un file `view.yml` vuoto nella cartella `config/` del modulo stesso.
  383. Dopo aver visto il template predefinito nel listato 7-5 e un esempio di una response finale nel listato 7-6, ci si potrebbe chiedere da dove provengono i valori dell'header. La risposta è che sono le impostazioni predefinite per la vista, definite nel file `view.yml` dell'applicazione e mostrate nel listato 7-19.
  384. Listato 7-19 - Configurazione predefinita della vista a livello di applicazione, in `apps/frontend/config/view.yml`
  385. default:
  386. http_metas:
  387. content-type: text/html
  388. metas:
  389. #title: symfony project
  390. #description: symfony project
  391. #keywords: symfony, project
  392. #language: en
  393. #robots: index, follow
  394. stylesheets: [main]
  395. javascripts: []
  396. has_layout: true
  397. layout: layout
  398. Ciascuna di queste impostazioni verrà descritta in dettaglio nella sezione "Impostazione della configurazione della vista".
  399. ### L'oggetto response
  400. Sebbene faccia parte del livello vista, l'oggetto response viene spesso modificato dall'azione. Le azioni possono accedere all'oggetto response di symfony, chiamato `sfResponse`, attraverso il metodo `getResponse()`. Il listato 7-20 elenca alcuni dei metodi di `sfResponse` spesso utilizzati all'interno di un'azione.
  401. Listato 7-20 - Le azioni hanno accesso ai metodi dell'oggetto `sfResponse`
  402. [php]
  403. class miomoduloActions extends sfActions
  404. {
  405. public function executeIndex()
  406. {
  407. $response = $this->getResponse();
  408. // Header HTTP
  409. $response->setContentType('text/xml');
  410. $response->setHttpHeader('Content-Language', 'en');
  411. $response->setStatusCode(403);
  412. $response->addVaryHttpHeader('Accept-Language');
  413. $response->addCacheControlHttpHeader('no-cache');
  414. // Cookie
  415. $response->setCookie($name, $content, $expire, $path, $domain);
  416. // Meta e header della pagina
  417. $response->addMeta('robots', 'NONE');
  418. $response->addMeta('keywords', 'foo bar');
  419. $response->setTitle('La mia pagina FooBar');
  420. $response->addStyleSheet('custom_style');
  421. $response->addJavaScript('custom_behavior');
  422. }
  423. }
  424. Oltre ai metodi setter che sono stati mostrati, la classe `sfResponse` ha getter che restituiscono il valore corrente degli attributi della response.
  425. I setter dell'header sono molto potenti in symfony. Gli header sono inviati il più tardi possibile (in `sfRenderingFilter`), in modo che possano venire modificati come si vuole. Forniscono anche utili scorciatoie. Ad esempio, se non si specifica un charset quando si chiama `setContentType()`, symfony aggiunge automaticamente il charset predefinito, definito nel file `settings.yml`.
  426. [php]
  427. $response->setContentType('text/xml');
  428. echo $response->getContentType();
  429. => 'text/xml; charset=utf-8'
  430. Il codice di stato delle risposte di symfony è compatibile con la specifica HTTP. Le eccezioni restituiscono uno stato 500, le pagine non trovate restituiscono 404, le pagine normali restituiscono uno stato 200, le pagine non modificate possono essere ridotte a un semplice header con il codice di stato 304 (vedere il capitolo 12 per maggiori dettagli) e così via. Ma è possibile sovrascrivere questi valori predefiniti impostando il proprio codice di stato nell'azione con il metodo response `setStatusCode()`. È possibile specificare un codice personalizzato e un messaggio personalizzato, o semplicemente un codice personalizzato, nel qual caso, symfony aggiunge un messaggio generico per questo codice.
  431. [php]
  432. $response->setStatusCode(404, 'Questa pagina non esiste');
  433. >**TIP**
  434. >Prima di inviare gli header, symfony normalizza i loro nomi. Quindi non è il caso di preoccuparsi se si scrive `content-language` invece di `Content-Language` in una chiamata a `setHttpHeader()`, perché symfony coomprenderà il primo e l otrasformerà automaticamente nel secondo.
  435. ### Impostare la configurazione per la vista
  436. Si sarà notato che ci sono due tipi di impostazioni per la configurazione della vista:
  437. * Quella che ha un unico valore (il valore è una stringa nel file `view.yml` e la response usa un metodo `set` per qeusta)
  438. * Quella con valori multipli (per le quali `view.yml` usa array e la response usa un metodo `add`)
  439. Ricordarsi che la configurazione a cascata cancella l'impostazione del valore unico ma mantiene le impostazioni con valori multipli. Questo diventerà più evidente proseguendo la lettura del capitolo.
  440. #### Configurare i meta tag
  441. Le informazioni scritte nei tag `<meta>` nella response non sono visualizzate nel browser ma sono utili per i robot e i motori di ricerca. Gestiscono anche le impostazioni della cache di tutte le pagine. Definire questi tag sotto le chiavi `http_metas:` e `metas:`, come mostrato nel listato 7-21, o con i metodi di response `addHttpMeta()` e `addMeta()` nell'azione, come mostrato nel listato 7-22.
  442. Listato 7-21 - Definizione meta come chiave: coppie di valori in `view.yml`
  443. http_metas:
  444. cache-control: public
  445. metas:
  446. description: Finanza in Italia
  447. keywords: finanza, Italia
  448. Listato 7-22 - Definizione meta come impostazioni della Response nell'azione
  449. [php]
  450. $this->getResponse()->addHttpMeta('cache-control', 'public');
  451. $this->getResponse()->addMeta('description', 'Finanza in Italia');
  452. $this->getResponse()->addMeta('keywords', 'finanza, Italia');
  453. L'aggiunta di una chiave esistente sostituirà il contenuto corrente predefinito. Per i meta tag HTTP, si può aggiungere un terzo parametro e impostarlo a `false` per fare si che il metodo `addHttpMeta()` (oltre a `setHttpHeader()`) aggiunga il valore a quello esistente, invece che sostituirlo.
  454. [php]
  455. $this->getResponse()->addHttpMeta('accept-language', 'en');
  456. $this->getResponse()->addHttpMeta('accept-language', 'fr', false);
  457. echo $this->getResponse()->getHttpHeader('accept-language');
  458. => 'en, fr'
  459. Al fine di far si che questi meta tag compaiano nella pagina finale, gli helper `include_http_metas()` e `include_metas()` devono essere chiamati nella sezione `<head>` (questo è il caso del layout predefinito; vedere il listato 7-5). Symfony aggrega automaticamente le impostazioni di tutti i file `view.yml` (compreso quello predefinito mostrato nel listato 7-18) e dell'attributo response per visualizzare i corretti `<meta>` tag. L'esempio del listato 7-21 finisce come mostrato nel listato 7-23.
  460. Listato 7-23 - Visualizzazione dei meta tag nella pagina finale
  461. [php]
  462. <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  463. <meta http-equiv="cache-control" content="public" />
  464. <meta name="robots" content="index, follow" />
  465. <meta name="description" content="Finanza in Italia" />
  466. <meta name="keywords" content="finanza, Italia" />
  467. Come bonus, l'header HTTP della response è anche gestito dall'impostazione `http-metas:` anche se non si hanno helper `include_http_metas()` nel layout, o se non esiste alcun layout. Ad esempio, se bisogna inviare una pagina come testo normale, definire il seguente `view.yml`:
  468. http_metas:
  469. content-type: text/plain
  470. has_layout: false
  471. #### Configurazione del title
  472. Il titolo della pagina è una parte chiave per l'indicizzazione dei motori di ricerca. È anche molto utile con i moderni browser che forniscono la navigazione con i tab. In HTML, il titolo è sia un tag che una informazione meta della pagina, quindi il file `view.yml` vede la chiave `title:` come un figlio della chiave `metas:`. Il listato 7-24 mostra la definizione di title in `view.yml` e il listato 7-25 mostra la definizione nell'azione.
  473. Listato 7-24 - La definizione di title in `view.yml`
  474. indexSuccess:
  475. metas:
  476. title: I tre porcellini
  477. Listato 7-25 - La definizione di title nell'azione (permette la creazione titoli dinamici)
  478. [php]
  479. $this->getResponse()->setTitle(sprintf('%d porcellini', $number));
  480. Nella sezione `<head>` della pagina finale, la definizione del title imposta il tag `<meta name="title">` se l'helper `include_metas()` è presente e il tag `<title>` se l'helper `include_title()` è presente. Se sono presenti entrambi (come nel layout predefinito del listato 7-5), il titolo compare due volte nel sorgente della pagina (vedere il listato 7-6), ma questo non crea nessun problema.
  481. >**TIP**
  482. >Un altro modo per gestire la definizione del title è usare gli slot, come discusso sopra. Questo metodo permette di mantenere una migliore separazione tra i controllori e i template : il titolo appartiene alla vista, non al controllore.
  483. #### Configurazione dell'inclusione dei file
  484. Aggiungere un certo foglio di stile o un file JavaScript alla vista è semplice, come mostrato nel listato 7-26.
  485. Listato 7-26 - Inclusione dei file
  486. [yml]
  487. // In view.yml
  488. indexSuccess:
  489. stylesheets: [mystyle1, mystyle2]
  490. javascripts: [myscript]
  491. -
  492. [php]
  493. // In una azione
  494. $this->getResponse()->addStylesheet('miostile1');
  495. $this->getResponse()->addStylesheet('miostile2');
  496. $this->getResponse()->addJavascript('mioscript');
  497. // In un template
  498. <?php use_stylesheet('miostile1') ?>
  499. <?php use_stylesheet('miostile2') ?>
  500. <?php use_javascript('mioscript') ?>
  501. In ogni caso, il parametro è un nome di un file. Se il file ha una estensione logica ((`.css` per un foglio di stile e `.js` per un file JavaScript), si può ometterla. Se il file ha una collocazione logica (`/css/` per un foglio di stile e `/js/` per un file JavaScript), si può omettere anche quella. Symfony è abbastanza intelligente da aggiungere la corretta estensione o locazione.
  502. Come le definizioni di meta e title, le definizioni per includere i file richiedono l'utilizzo degli helper `include_javascripts()` e `include_stylesheets()` nel template o nel layout dove devono essere inclusi. Questo significa che le precedenti impostazioni visualizzeranno il codice HTML del listato 7-27.
  503. Listato 7-27 - Risultato dell'inclusione dei file
  504. [php]
  505. <head>
  506. ...
  507. <link rel="stylesheet" type="text/css" media="screen" href="/css/miostile1.css" />
  508. <link rel="stylesheet" type="text/css" media="screen" href="/css/miostile2.css" />
  509. <script language="javascript" type="text/javascript" src="/js/mioscript.js">
  510. </script>
  511. </head>
  512. Ricordarsi che vengono applicati i principi di configurazione a cascata, quindi qualunque inclusione di file definita nel `view.yml` dell'applicazione verrà applicata in ogni pagina dell'applicazione. I listati 7-28, 7-29 e 7-30 mostrano questo principio.
  513. Listato 7-28 - Esempio di `view.yml` nell'applicazione
  514. default:
  515. stylesheets: [main]
  516. Listato 7-29 - Sample Module `view.yml`
  517. indexSuccess:
  518. stylesheets: [special]
  519. all:
  520. stylesheets: [additional]
  521. Listato 7-30 - Visualizzazione della vista `indexSuccess`
  522. [php]
  523. <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
  524. <link rel="stylesheet" type="text/css" media="screen" href="/css/additional.css" />
  525. <link rel="stylesheet" type="text/css" media="screen" href="/css/special.css" />
  526. Quando si ha bisogno di rimuovere un file definito a un livello più alto, basta aggiungere un segno meno (`-`) davanti al nome del file nella definizione di livello inferiore, come mostrato nel listato 7-31.
  527. Listato 7-31 - Esempio con `view.yml` nel modulo. Viene rimossa un file definito nel livello applicazione
  528. indexSuccess:
  529. stylesheets: [-main, special]
  530. all:
  531. stylesheets: [additional]
  532. Per rimuovere tutti i fogli di stile o i file JavaScript ,usare `-*` come nome del file, come mostrato nel listato 7-32.
  533. Listato 7-32 - Esempio con `view.yml` nel modulo. Vengono rimossi tutti i file definiti nel livello applicazione
  534. indexSuccess:
  535. stylesheets: [-*]
  536. javascripts: [-*]
  537. Si può lavorare con maggiore precisione e definire un parametro ulteriore 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.
  538. Listato 7-33 - Definire la posizione di un file incluso
  539. [yml]
  540. // In view.yml
  541. indexSuccess:
  542. stylesheets: [special: { position: first }]
  543. -
  544. [php]
  545. // In una azione
  546. $this->getResponse()->addStylesheet('special', 'first');
  547. // In un template
  548. <?php use_stylesheet('special', 'first') ?>
  549. Si può anche decidere di ignorare la trasformazione dei nomi dei file, in modo che i risultanti tag `<link>` o `<script>` vengano riferiti alla esatta locazione specificata, come mostrato nel listato 7-34.
  550. Listato 7-34 - Inclusione di un foglio di stile con Style Sheet con nome inalterato
  551. [yml]
  552. // In view.yml
  553. indexSuccess:
  554. stylesheets: [main, paper: { raw_name: true }]
  555. -
  556. [php]
  557. // In una azione
  558. $this->getResponse()->addStylesheet('main', '', array('raw_name' => true));
  559. // In un template
  560. <?php use_stylesheet('main', '', array('raw_name' => true)) ?>
  561. // La vista risultante
  562. <link rel="stylesheet" type="text/css" href="main" />
  563. Per specificare il media relativo a una inclusione di un foglio di stile, si possono cambiare le opzioni predefinite del tag per i fogli di stile, come mostrato nel listato 7-35.
  564. Listato 7-35 - Inclusione di un foglio di stile specificando il media
  565. [yml]
  566. // In the view.yml
  567. indexSuccess:
  568. stylesheets: [main, paper: { media: print }]
  569. -
  570. [php]
  571. // In una azione
  572. $this->getResponse()->addStylesheet('paper', '', array('media' => 'print'));
  573. // In un template
  574. <?php use_stylesheet('paper', '', array('media' => 'print')) ?>
  575. // La vista risultante
  576. <link rel="stylesheet" type="text/css" media="print" href="/css/paper.css" />
  577. >**SIDEBAR**
  578. >Nota relativa all'inclusione di file usando il file view.yml
  579. >
  580. >La pratica migliore è quella di definire i file predefiniti per i fogli di stile e i JavaScript nel file view.yml del progetto e includere specifici fogli di stile o file JavaScript nei template, utilizzando gli appositi helper. In questo modo, non è necessario rimuovere o sostituire file già inclusi, cosa che in alcuni casi può diventare un problema.
  581. #### Configurazione del layout
  582. In base a come è progettato un sito web, è possibile avere più layout. I siti web classici ne hanno almeno due: il layout predefinito e il layout per il pop-up.
  583. Si è già visto che il layout predefinito è `mioprogetto/apps/frontend/templates/layout.php`. I layout devono essere aggiunti nella stessa cartella globale `templates/`. Se si vuole che una vista utilizzi il file `frontend/templates/mio_layout.php`, usare la sintassi mostrata nel listato 7-36.
  584. Listato 7-36 - Definizione di un layout
  585. [yml]
  586. // In view.yml
  587. indexSuccess:
  588. layout: mio_layout
  589. -
  590. [php]
  591. // In una azione
  592. $this->setLayout('mio_layout');
  593. // In un template
  594. <?php decorate_with('mio_layout') ?>
  595. Alcune viste non necessita di layout (ad esempio, pagine con testo semplice o feed RSS=. In questo caso, impostare `has_layout` a `false`, come mostrato nel listato 7-37.
  596. Listato 7-37 - Rimozione del layout
  597. [yml]
  598. // In `view.yml`
  599. indexSuccess:
  600. has_layout: false
  601. -
  602. [php]
  603. // In una azione
  604. $this->setLayout(false);
  605. // In un template
  606. <?php decorate_with(false) ?>
  607. >**NOTE**
  608. >Le viste per le azioni Ajax non hanno layout per impostazione predefinita.
  609. Escape dell'output
  610. ------------------
  611. Quando vengono inseriti dati dinamici in un template, bisogna essere sicuri dell'integrità dei dati. Per esempio, se i dati provengono da form compilati da utenti anonimi, c'è il rischio che possano includere script maligni che hanno lo scopo di lanciare attacchi di tipo cross-site scripting (XSS). Bisogna essere in grado di fare l'escape dei dati visualizzati, in modo che qualunque tag HTML possa contenere diventi inerte.
  612. Come esempio, supponiamo che un utente compili un campo input con il seguente valore:
  613. [php]
  614. <script>alert(document.cookie)</script>
  615. Se si fa un echo di questo valore senza precauzioni, su ogni browser verrà eseguito il JavaScript e permetterà attacchi ben più pericolosi rispetto alla visualizzazione di un messaggio di alert. Questo è il motivo per il quale è necessario fare l'escape del valore prima della sua visualizzazione, in modo che diventi un qualcosa del genere:
  616. [php]
  617. &lt;script&gt;alert(document.cookie)&lt;/script&gt;
  618. Si può eseguire l'escape dell'output manualmente racchiudendo ogni valore insicuro con la chiamata a `htmlspecialchars()`, ma questo approccio può diventare molto ripetitivo e soggetto a errori. Quindi symfony fornisce un sistema speciale, chiamato escape dell'output, che esegue l'escape automaticamente ogni variabile in output nel template. É attivato in modalità predefinita nel `settings.yml` dell'applicazione.
  619. ### Attivazione dell'escape dell'output
  620. L'escape dell'output è configurata globalmente per una applicazione nel file `settings.yml`. Due parametri controllano il modo funziona l'escape dell'output: la strategia stabilisce come le variabili vengono rese disponibili alla vista e il metodo è la funzione di escape predefinita applicata ai dati.
  621. In sostanza, tutto quelllo che bisogna fare per attivare l'escape dell'output è impostare il parametro `escaping_strategy` a `true` (che è il valore predefinito), come mostrato nel listato 7-38.
  622. Listato 7-38 - Attivazione dell'escape dell'output, in `frontend/config/settings.yml`
  623. all:
  624. .settings:
  625. escaping_strategy: true
  626. escaping_method: ESC_SPECIALCHARS
  627. Questo, per impostazione predefinita, aggiungerà `htmlspecialchars()` nell'output di tutte le variabili. Ad esempio, supponiamo di definire una variabile `test` in una azione:
  628. [php]
  629. $this->test = '<script>alert(document.cookie)</script>';
  630. Con l'escape dell'output attivato, l'echo di questa variabile nel template mostrerà i dati sotto escape:
  631. [php]
  632. echo $test;
  633. => &lt;script&gt;alert(document.cookie)&lt;/script&gt;
  634. Inoltre, ogni template ha accesso a una variabile `$sf_data`, che è un contenitore di oggetti riferito a tutte le variabili sotto escape. Quindi si può anche visualizzare la variabile test in questo modo:
  635. [php]
  636. echo $sf_data->get('test');
  637. => &lt;script&gt;alert(document.cookie)&lt;/script&gt;
  638. >**TIP**
  639. >L'oggetto `$sf_data object` implementa l'interfaccia Array, quindi invece di usare `$sf_data->get('miavariabile')`, si possono recuperare i valori sotto escape chiamando `$sf_data['myvariable']`. Ma non è un array reale, quindi le funzioni tipo `print_r()` non funzioneranno come ci si attende.
  640. `$sf_data` fornisce anche l'accesso ai dati non sotto escape, detti anche "raw". Questo è utile quando una variabile memorizza codice HTML che deve essere interpretato dal browser, a condizione che vi "fidiate" di questa variabile. Richiamare il metodo `getRaw()` quando si vogliono visualizzare dati raw.
  641. [php]
  642. echo $sf_data->getRaw('test');
  643. => <script>alert(document.cookie)</script>
  644. Si vorrà accedere ai dati raw tutte le volte che si ha bisogno che le variabili contenenti HTML siano realmente interpretate come HTML.
  645. Quando `escaping_strategy` è `false`, `$sf_data` è comunque disponibile, ma restituisce sempre dati raw.
  646. ### Helper per l'escape
  647. Gli helper per l'escape sono funzioni che restituiscono una versione sotto escape del loro input. Possono essere forniti come predefiniti `escaping_method` nel file `settings.yml` o per specificare un metodo di escape per uno specifico valore nella vista. Sono disponibili i seguenti helper per l'escape:
  648. * `ESC_RAW`: Non fa l'escape del valore.
  649. * `ESC_SPECIALCHARS`: Applica la funzione PHP `htmlspecialchars()` all'input.
  650. * `ESC_ENTITIES`: Applica la funzione PHP `htmlentities()` all'input con `ENT_QUOTES` come stile per le quote.
  651. * `ESC_JS`: Fa l'escape di un valore che deve essere inserito in una stringa JavaScript che deve essere usata come HTML. É utile per fare l'escape di cose dove l'HTML deve essere cambiato dinamicamente usando il JavaScript.
  652. * `ESC_JS_NO_ENTITIES`: Fa l'escape di un valore che deve essere messo in una stringa JavaScript ma non aggiunge entità. É utile se il valore deve essere visualizzato usando una dialog box (ad esempio, per una variabile `miaStringa` usata in `javascript:alert(miaStringa);`).
  653. ### Fare l'escape di array e oggetti
  654. L'escape dell'output funziona non solo per le stringhe, ma anche per gli array e gli oggetti. Tutti i valori che sono oggetti o array passeranno il loro stato di escape ai loro figli. Assumendo che la strategia sia impostata a `true`, il listato 7-39 mostra l'escape a cascata.
  655. Listato 7-39 - L'escape funziona anche per gli array e gli oggetti
  656. [php]
  657. // Definizione della classe
  658. class miaClasse
  659. {
  660. public function testSpecialChars($value = '')
  661. {
  662. return '<'.$value.'>';
  663. }
  664. }
  665. // In una azione
  666. $this->test_array = array('&', '<', '>');
  667. $this->test_array_of_arrays = array(array('&'));
  668. $this->test_object = new myClass();
  669. // In un template
  670. <?php foreach($test_array as $value): ?>
  671. <?php echo $value ?>
  672. <?php endforeach; ?>
  673. => &amp; &lt; &gt;
  674. <?php echo $test_array_of_arrays[0][0] ?>
  675. => &amp;
  676. <?php echo $test_object->testSpecialChars('&') ?>
  677. => &lt;&amp;&gt;
  678. È un dato di fatto, che le variabili nel template non sono del tipo ci si potrebbe aspettare. Il sistema di escape dell'output le "decora" e le trasforma in oggetti speciali:
  679. [php]
  680. <?php echo get_class($test_array) ?>
  681. => sfOutputEscaperArrayDecorator
  682. <?php echo get_class($test_object) ?>
  683. => sfOutputEscaperObjectDecorator
  684. Questo spiega perché alcune normali funzioni PHP (come `array_shift()`, `print_r()` e altre) non funzionano più con gli array sotto escape. Ma questi possono essere acceduti utilizzando `[]`, essere attraversati usando `foreach` e restituire il corretto risultato con `count()`. E in ogni caso nei template i dati dovrebbero essere a sola lettura, quindi la maggior parte degli accessi verrà fatta utilizzando metodi che funzionano correttamente.
  685. C'è ancora un modo per recuperare i dati raw attraverso l'oggetto `$sf_data`. Inoltre, i metodi di oggetti sotto escape vengono alterati per accettare un parametro aggiuntivo: un metodo di escape. Così si può scegliere un metodo alternativo per fare l'escape ogni volta che si visualizza una variabile in un template, oppure optare per l'helper `ESC_RAW` per disattivare l'escape. Vedere il listato 7-40 come esempio.
  686. Listato 7-40 - I metodi degli oggetti sotto escape accettano un parametro aggiuntivo
  687. [php]
  688. <?php echo $test_object->testSpecialChars('&') ?>
  689. => &lt;&amp;&gt;
  690. // Le seguenti tre righe restituiscono lo stesso valore
  691. <?php echo $test_object->testSpecialChars('&', ESC_RAW) ?>
  692. <?php echo $sf_data->getRaw('test_object')->testSpecialChars('&') ?>
  693. <?php echo $sf_data->get('test_object', ESC_RAW)->testSpecialChars('&') ?>
  694. => <&>
  695. Se si ha a che fare con oggetti nei template, sarà necessario usare spesso il trucco del parametro aggiuntivo, dal momento che è il modo più veloce per ottenere i dati grezzi in una chiamata al metodo.
  696. >**CAUTION**
  697. >Anche le normali variabili di symfony subiscono l'escape quando si imposta a `true` l'escape dell'output. Quindi bisogna tener presente che `$sf_user`, `$sf_request`, `$sf_param` e `$sf_context` funzionano ancora, ma i loro metodi restituiscono dati sotto escape, a meno che non si aggiunga `ESC_RAW` come parametro finale alle loro chiamate di metodi.
  698. -
  699. >**TIP**
  700. >Anche se l'XSS è uno degli exploit più comuni nei siti web, non è l'unico. Anche il CSRF è molto popolare e symfony fornisce una protezione automatica per i form. Si può scoprire come funziona questa protezione di sicurezza nel capitolo 10.
  701. Riepilogo
  702. ---------
  703. Sono disponibili molti tipi di strumenti per manipolare il livello di presentazione. I template vengono generati in pochi secondi, grazie agli helper. I layout, i partial e i componenti sono utili per la modularità e la riusabilità. La configurazione della vista sfrutta la velocità dello YAML per gestire (soprattutto) gli header delle pagine. La configurazione a cascata esime dal definire una impostazione per ciascuna vista. Per ogni modifica della presentazione che dipende da dati dinamici, l'azione ha accesso all'oggetto `sfResponse`. La vista è al sicuro da attacchi XSS, grazie al sistema di escape dell'output.