PageRenderTime 60ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/gentle-introduction/it/09-Links-and-the-Routing-System.markdown

https://gitlab.com/intelij/symfony1-docs
Markdown | 701 lines | 497 code | 204 blank | 0 comment | 0 complexity | a370e26c723d5d7e7070b6047302cd0b MD5 | raw file
Possible License(s): CC-BY-SA-3.0, CC-BY-SA-4.0
  1. Capitolo 9 - Link e il sistema di routing
  2. =========================================
  3. Link e URL meritano particolare attenzione in un framework per applicazioni web. Questo avviene perché
  4. l'unico entry point dell'applicazione (il front controller) e l'utilizzo degli helper all'interno dei
  5. template permettono una completa separazione tra il modo in cui gli URL funzionano e la loro rappresentazione.
  6. Questo è chiamato routing. Più di un semplice gadget il routing è un utile strumento per rendere le
  7. applicazioni web ancora più user-friendly e sicure. Questo capitolo mostrerà quello che è necessario
  8. sapere per gestire gli URL in una applicazione symfony:
  9. * Cos'è il sistema di routing e come funziona
  10. * Come utilizzare gli helper dei link nei template e abilitare il routing di URL uscenti
  11. * Come configurare le regole di routing per modificare la rappresentazione degli url
  12. Come tocco finale verranno mostrati alcuni trucchi per gestire le prestazioni del sistema di routing.
  13. Che cos'è il routing?
  14. ---------------------
  15. Il routing è un meccanismo che riscrive gli URL per renderli più user-friendly. Per capire perché questa cosa è importante è necessario riflettere qualche minuto su cosa sia in effetti un URL
  16. ### URL come comandi per il Server
  17. Gli URL portano informazioni dal browser al server richiesto affinché questo svolga una azione come desiderato dall'utente.
  18. Per esempio, un URL tradizionale contiene il percorso a uno script e alcuni parametri necessari a completare la richiesta, come in questo esempio:
  19. http://www.example.com/web/controller/article.php?id=123456&format_code=6532
  20. Questo URL espone informazioni sull'architettura dell'applicazione e il database. Gli sviluppatori
  21. di solito nascondono l'infrastuttura dell'applicazione nell'interfaccia (es esempio, scegliendo come
  22. titoli di pagina "Profilo" piuttosto che "QZ7.65"). Rivelare i dettagli del funzionamento interno dell'
  23. applicazione nel URL va contro questo sforzo e ha diversi svantaggi:
  24. * I dati contenuti nel URL creano potenziali falle di sicurezza. Nell'esempio precedente, cosa accade se
  25. un utente malintenzionato cambia il valore del parametro `id`? Significa che l'applicazione offre un interfaccia
  26. direttamente verso il database? Cosa accade se l'utente prova a utilizzare un diverso nome per lo script, come
  27. `admin.php` per divertimento? In conclusione, gli URL "raw" offrono un modo facile per hackerare una applicazione
  28. e gestire la sicurezza con quest'ultimi è quasi impossibile.
  29. * L'incomprensibilità degli URL li rendono ingombranti ovunque appaiono e smorzano l'impatto del contenuto che li
  30. circonda. Oggi gli URL non compaiono solo nella barra degli indirizzi. Sono visibili quando un utente
  31. passa il mouse sopra un link così come nei risultati di una ricerca. Quando un utente cerca informazioni bisogna cercare
  32. di fornirgli indizi facilmente comprensibili riguardo quello che ha trovato invece di un URL confuso come quello
  33. mostrato in figura 9-1.
  34. Figura 9-1 - Gli URL compaiono in molti posti, come nei risultati della ricerca
  35. ![Gli URL compaiono in molti posti, come nei risultati della ricerca](http://www.symfony-project.org/images/book/1_4/F0901.png "Gli URL compaiono in molti posti, come nei risultati della ricerca")
  36. * Se è necessario modificare un URL (ad esempio perché il nome dello script o uno dei suoi parameteri viene modificato),
  37. ogni link a questo URL deve essere cambiato allo stesso modo. Questo significa che le modifiche alla struttura del controller
  38. sono pesanti e costose, che non è l'ideale nello sviluppo agile.
  39. Potrebbe essere molto peggio se symfony non usasse il pattern front controller, ovvero se l'applicazione contenesse molti script
  40. accessibili da Internet, in diverse cartelle, come questi:
  41. http://www.example.com/web/gallery/album.php?name=my%20holidays
  42. http://www.example.com/web/weblog/public/post/list.php
  43. http://www.example.com/web/general/content/page.php?name=about%20us
  44. In questo caso gli sviluppatori sarebbero obbligati a vincolare la struttura degli URL con quella del filesystem,
  45. rendendo il mantenimento un incubo al cambiare della struttura.
  46. ### URL come parte dell'interfaccia
  47. L'idea dietro il routing è considerare gli URL come parte dell'interfaccia. L'applicazione può formattare un URL
  48. per dare carte informazioni all'utente e l'utente può utilizzare l'URL per accedere alle risorse dell'applicazione.
  49. Questo è possibile nelle applicazioni symfony, perché gli URL presentati all'utente non sono correlati alle
  50. istruzioni necessarie al server per eseguire la richiesta. Al contrario sono correlati alla risorsa richiesta
  51. e possono essere formattati liberamente. Symfony ad esempio è in grado di comprendere il seguente URL
  52. e mostrare la stessa pagina del primo URL mostrato in questo capitolo:
  53. http://www.example.com/articles/finance/2010/activity-breakdown.html
  54. I benefici sono immensi:
  55. * Le URL assumono effettivamente un significato ben preciso e possono aiutare l'utente a comprendere se la pagina ottenuta da un determinato link contiene ciò che ci si aspetta.
  56. Un link può contenere maggiori dettagli riguardanti la risorsa alla quale esso è legato. Questo è particolarmente utile per i risultati forniti dai motori di ricerca.
  57. Inoltre, può capitare che gli URL appaiano senza alcun riferimento al titolo della pagina (si pensi a quando si copia un link da spedire via e-mail)
  58. e, in tale caso, devono assumere un significato ben preciso. La figura 9-2 mostra un esempio di URL user-friendly.
  59. * L'implementazione tecnica è nascosta all'utente: non viene reso noto quale script venga utilizzato, non è possibile cercare di indovinare un id o parametri simili: l'applicazione è meno vulnerabile a potenziale attacchi.
  60. Inoltre è possibile modificare quello che avviene "dietro le quinte", senza che gli utenti ne risentano (non ci sarà un 404 o un redirect permanente)
  61. Figura 9-2 - Gli URL possono contenere informazioni aggiuntive alla pagina, ad esempio la data di pubblicazione
  62. ![Gli URL possono contenere informazioni aggiuntive alla pagina, ad esempio la data di pubblicazione](http://www.symfony-project.org/images/book/1_1/F0902.png "Gli URL possono contenere informazioni aggiuntive alla pagina, ad esempio la data di pubblicazione")
  63. * Gli URL scritti su documenti di carta sono più facili da scrivere e ricordare. Se una azienda compare su biglietti da visita come `http://www.example.com/controller/web/index.jsp?id=ERD4`, probabilmente non riceverà molte visite.
  64. * Gli URL possono diventare un modo di eseguire comandi, per recuperare informazioni in modo intuitivo. Le applicazioni che offrono tale possibilità sono più veloci da utilizzare per utenti avanzati.
  65. // Lista di risultati: aggiungi un nuovo tag per raffinare il risultato
  66. http://del.icio.us/tag/symfony+ajax
  67. // Pagina profilo utente: cambia il nome per vedere il profilo di un altro utente
  68. http://www.askeet.com/user/francois
  69. * È possibile cambiare la formattazione degli URL e il nome/parametri dell'azione in modo indipendente, con una singola modifica. Significa che è possibile prima sviluppare e alla fine formattare gli URL senza creare confusione.
  70. * Anche quando si riorganizza l'applicazione, gli URL possono rimanere le stesse per il mondo esterno. Ciò rende gli URL persistenti, che è vitale in quanto rende possibile i bookmark di pagine dinamiche.
  71. * I motori di ricerca tendono a evitare pagine dinamiche (che terminano con `.php`, `.asp` e così via) quando indicizzano i siti. È così possibile formattare gli URL in modo che i motori di ricerca pensino di stare navigando su un sito statico,
  72. anche quando incontrano pagine dinamiche, in modo da migliorare il ranking delle pagine stesse.
  73. * È più sicuro. Un URL non riconosciuto verrà redirezionato a una pagina specificata dallo sviluppatore e gli utenti non possono navigare la struttura radice provando indirizzi a caso. Il nome effettivo dello script, così come i suoi parametri, sono nascosti.
  74. La corrispondenza tra l'URL presentato all'utente e il nome effettivo dello script e i suoi parametri viene conseguita dal sistema di routing, basato su schemi che possono essere modificati tramite configurazione.
  75. >**NOTE**
  76. >E le risorse? Fortunatamente, gli URL delle risorse (immagini, fogli di stile e script JavaScript) non compaiono frequentemente durante la navigazione, per cui non c'è un grande bisogno del motore di routing.
  77. >In symfony, tutte le risorse sono situate all'interno della cartella `web/` e il loro URL coincidono con le loro posizioni nel filesystem. Comunque, è possibile gestire risorse dinamiche (dalle azioni) utilizzando URL generati nei relativi helper. Ad esempio, per visualizzare un'immagine generata dinamicamente, è sufficiente utilizzare `image_tag(url_for('captcha/image?key='.$key))`.
  78. ### Come funziona
  79. Symfony scollega gli URL esterni dai propri URI interni. La corrispondenza tra le due viene eseguita dal sistema di routing. Per facilitare le cose, symfony utilizza per le URI interne una sintassi molto simile a quella degli URL normali. Il listato 9-1 ne mostra un esempio.
  80. Listato 9-1 - URL esterni e URI interni
  81. // Sintassi URI interne
  82. <module>/<action>[?param1=value1][&param2=value2][&param3=value3]...
  83. // Esempio di URI interna, non compare mai all'utente finale
  84. article/permalink?year=2006&subject=finance&title=activity-breakdown
  85. // Esempio di URL interna, che compare all'utente finale
  86. http://www.example.com/articles/finance/2006/activity-breakdown.html
  87. Il sistema di routing utilizza un file di configurazione speciale, chiamato `routing.yml`, nel quale è possibile definire le regole. Si consideri la regola mostrata nel listato 9-2. Definisce uno schema come `articles/*/*/*` e un nome alle parti di codice che coincidono con i caratteri jolly.
  88. Listato 9-2 - Esempio di regola di routing
  89. article_by_title:
  90. url: articles/:subject/:year/:title.html
  91. param: { module: article, action: permalink }
  92. Ogni richiesta per un'applicazione symfony viene prima di tutto analizzata dal sistema di routing (la qual cosa risulta piuttosto semplice, in quanto ogni richiesta viene gestita da un unico front controller). Il sistema di routing cerca una corrispondenza tra l'URL della richiesta e gli schemi definiti nelle regole di routing. Se una viene trovata, i caratteri jolly diventano parametri di richiesta e vengono uniti a quelli definiti nella chiave `param:`. Il listato 9-3 ne mostra il funzionamento.
  93. Listato 9-3 - Il sistema di routing interpreta gli URL della richiesta
  94. // L'utente scrive (o clicca su) questo URL esterno
  95. http://www.example.com/articles/finance/2006/activity-breakdown.html
  96. // Il front controller trova una corrispondenza con la regola article_by_title
  97. // Il sistema di routing crea i parametri seguenti
  98. 'module' => 'article'
  99. 'action' => 'permalink'
  100. 'subject' => 'finance'
  101. 'year' => '2006'
  102. 'title' => 'activity-breakdown'
  103. La richiesta viene quindi passata all'azione `permalink` del modulo `article`, il quale in questo modo ha tutti i parametri necessari a mostrare l'articolo richiesto.
  104. Ma questo meccanismo deve anche funzionare in senso opposto. Dato che l'applicazione deve mostrare gli URL esterni nei propri link, devi fornire al sistema di routing abbastanza informazioni affinché esso possa comprendere quale regola applicare.
  105. Inoltre non si deve assolutamente scrivere nei template i link con i tag `<a>`, direttamente, perché in tal modo non verrebbe ignorato completamente il sistema di routing; si deve invece utilizzare un helper speciale, come mostrato nel listato 9-4.
  106. Listato 9-4 - Il sistema di routing formatta gli URL nei template
  107. [php]
  108. // L'helper url_for() trasforma un URI interno in un URL esterno
  109. <a href="<?php echo url_for('article/permalink?subject=finance&year=2006&title=activity-breakdown') ?>">click here</a>
  110. // L'helper riconosce che l'URI soddisfa la regola article_by_title
  111. // Per cui il sistema di routing crea l'URL
  112. => <a href="http://www.example.com/articles/finance/2006/activity-breakdown.html">click here</a>
  113. // L'helper link_to() restituisce un link, evitando di mischiare PHP con HTML
  114. <?php echo link_to(
  115. 'click here',
  116. 'article/permalink?subject=finance&year=2006&title=activity-breakdown'
  117. ) ?>
  118. // Internamente, link_to() chiamerà url_for() in modo che il risultato sia lo stesso
  119. => <a href="http://www.example.com/articles/finance/2006/activity-breakdown.html">click here</a>
  120. Quindi il routing è un meccanismo che funziona in due direzioni e funziona solo se si utilizza l'helper `link_to()` per formattare i link.
  121. URL Rewrite
  122. -----------
  123. Prima di approfondire il sistema di routing, c'è un'altra questione che va chiarita.
  124. Negli esempi precedenti non è stata fatta menzione del front controller (`index.php` o `frontend_dev.php`) nelle URI interne.
  125. Il front controller decide l'ambiente, non gli elementi dell'applicazione. Per cui tutti i link devono essere indipendenti dall'ambiente e il nome del front controller non deve mai apparire negli URI interni.
  126. Negli esempi non c'è traccia del nome dello script nemmeno negli URL. Questo perché per default nell'ambiente di produzione gli URL generati non contengono il nome dello script. Il parametro `no_script_name` nel file `settings.yml` controlla esattamente questo comportamento; impostandolo su `false`, come nel listato 9-5, ogni URL stampata tramite gli helper dei link conterrà il nome dello script del front controller.
  127. Listato 9-5 - Mostrare il nome del front controller negli URL, in `apps/frontend/settings.yml`
  128. prod:
  129. .settings:
  130. no_script_name: false
  131. Così facendo gli URL generati appariranno nel modo seguente:
  132. http://www.example.com/index.php/articles/finance/2006/activity-breakdown.html
  133. In tutti gli ambienti diversi da quello di produzione, il parametro `no_script_name` è impostato su `false` come impostazione predefinita. Per cui quando si naviga l'applicazione nell'ambiente di sviluppo, il nome del front controller risulta sempre negli URL.
  134. http://www.example.com/frontend_dev.php/articles/finance/2006/activity-breakdown.html
  135. In produzione, `no_script_name` è impostato su `on`, così gli URL mostreranno solo le informazioni di routing e risulteranno più user-friendly. Non apparirà alcuna informazione tecnica.
  136. http://www.example.com/articles/finance/2006/activity-breakdown.html
  137. Ma come fa l'applicazione a sapere quale front controller chiamare? Qui è dove entra in gioco l'URL rewrite. Il web server può essere configurato per invocare un determinato script qualora non venga specificato nell'URL.
  138. In Apache, ciò è possibile solo dopo aver attivato l'estensione `mod_rewrite`. Ogni progetto symfony è dotato di un file `.htaccess`, che aggiunge alcune impostazioni `mod_rewrite` per la cartella `web/` alla configurazione del server. Il contenuto predefinito di tale file è mostrato nel listato 9-6.
  139. Listato 9-6 - Regole di rewrite predefinite per Apache, in `myproject/web/.htaccess`
  140. <IfModule mod_rewrite.c>
  141. RewriteEngine On
  142. # salta tutti i file che inizia con un punto
  143. RewriteCond %{REQUEST_URI} \..+$
  144. RewriteCond %{REQUEST_URI} !\.html$
  145. RewriteRule .* - [L]
  146. # controllo se esiste la versione .html (caching)
  147. RewriteRule ^$ index.html [QSA]
  148. RewriteRule ^([^.]+)$ $1.html [QSA]
  149. RewriteCond %{REQUEST_FILENAME} !-f
  150. # se no, redirige al nostro front web controller
  151. RewriteRule ^(.*)$ index.php [QSA,L]
  152. </IfModule>
  153. Il web server controlla gli URL che riceve. Se l'URL non contiene un suffisso e se non c'è già una versione disponibile in cache della pagina (consultare il capitolo 12 riguardante la cache), allora la richiesta viene gestita dallo script `index.php`.
  154. Comunque, la cartella `web/` di un progetto symfony è condivisa da tutte le applicazioni e da tutti gli ambienti del progetto. Ciò significa che spesso esisterà più di un front controller in tale cartella. Ad esempio, un progetto che abbia un'applicazione di `frontend` e una di `backend`,
  155. un ambiente `dev` e uno `prod` conterrà quattro script per i front controller nella cartella `web/`:
  156. index.php // frontend in prod
  157. frontend_dev.php // frontend in dev
  158. backend.php // backend in prod
  159. backend_dev.php // backend in dev
  160. Le impostazioni mod_rewrite possono specificare il nome di un solo front controller di default. Se si imposta `no_script_name` a `true` per tutte le applicazioni e tutti gli ambienti, tutti gli URL saranno interpretati come richieste per l'applicazione `frontend` nell'ambiente `prod`. Ecco perché, per ogni progetto, si può avere solo una applicazione e un ambiente che sfruttino il vantaggio dell'URL rewrite.
  161. >**TIP**
  162. >In effetti c'è un modo per avere più di un'applicazione senza script name. È sufficiente creare sottocartelle nella cartella web e collocarci i vari front controller. È necessario cambiare il percorso del file `ProjectConfiguration` di conseguenza e creare le configurazioni `.htaccess` necessarie a ogni applicazione.
  163. Helper Link
  164. -----------
  165. Per trarre maggior vantaggio dal sistema di routing, si dovrebbe utilizzare gli helper dei link invece dei tag `<a>` nei template. Non bisogna pensare a ciò come uno svantaggio, bensì come a un modo per mantenere l'applicazione pulita e facile da manutenere. Inoltre, questi helper offrono qualche scorciatoia molto utile.
  166. ### Link, pulsanti e form
  167. È stato già precedentemente mostrato l'helper `link_to()`. Esso restituisce un link che rispetta la sintassi XHTML e si aspetta due parametri: l'elemento che deve essere cliccato e l'URI interno. Se invece di un link si volesse un pulsante, è sufficiente utilizzare l'helper `button_to()`.
  168. Anche i form sono provvisti di un helper per gestire il valore dell'attributo `action`. Maggiori informazioni sui form nel prossimo capitolo. Il listato 9-7 mostra alcuni esempi di helper per i link.
  169. Listato 9-7 - Alcuni esempi di helper per i tag `<a>, <input>, e <form>`
  170. [php]
  171. // Link su una stringa
  172. <?php echo link_to('my article', 'article/read?title=Finance_in_France') ?>
  173. => <a href="/routed/url/to/Finance_in_France">my article</a>
  174. // Link su un'immagine
  175. <?php echo link_to(image_tag('read.gif'), 'article/read?title=Finance_in_France') ?>
  176. => <a href="/routed/url/to/Finance_in_France"><img src="/images/read.gif" /></a>
  177. // Pulsante
  178. <?php echo button_to('my article', 'article/read?title=Finance_in_France') ?>
  179. => <input value="my article" type="button"onclick="document.location.href='/routed/url/to/Finance_in_France';" />
  180. // Form
  181. <?php echo form_tag('article/read?title=Finance_in_France') ?>
  182. => <form method="post" action="/routed/url/to/Finance_in_France" />
  183. Tali helper accettano sia URI interni che URL assoluti (che cominciano con `http://`, e vengono ignorate dal sistema di routing) e ancore. Da notare che in applicazioni reali, gli URI interni vengono costruiti con parametri dinamici. Il listato 9-8 mostra un esempio di tali casi.
  184. Listato 9-8 - URL accettate dagli helper dei link
  185. [php]
  186. // URI interne
  187. <?php echo link_to('my article', 'article/read?title=Finance_in_France') ?>
  188. => <a href="/routed/url/to/Finance_in_France">my article</a>
  189. // URI interne con parametri dinamici
  190. <?php echo link_to('my article', 'article/read?title='.$article->getTitle()) ?>
  191. // URI interne con anchor
  192. <?php echo link_to('my article', 'article/read?title=Finance_in_France#foo') ?>
  193. => <a href="/routed/url/to/Finance_in_France#foo">my article</a>
  194. // URL assolute
  195. <?php echo link_to('my article', 'http://www.example.com/foobar.html') ?>
  196. => <a href="http://www.example.com/foobar.html">my article</a>
  197. ### Opzioni degli helper dei link
  198. Come spiegato precedentemente nel capitolo 7, gli helper accettano un ulteriore parametro opzionale, che può essere un array associativo o una stringa. Questo è vero anche per gli helper dei link, come mostrato nel listato 9-9.
  199. Listato 9-9 - Gli helper dei link accettano un parametro addizionale
  200. [php]
  201. // Opzione aggiuntiva come array associativo
  202. <?php echo link_to('my article', 'article/read?title=Finance_in_France', array(
  203. 'class' => 'foobar',
  204. 'target' => '_blank'
  205. )) ?>
  206. // Opzione aggiuntiva come stringa (stesso risultato)
  207. <?php echo link_to('my article', 'article/read?title=Finance_in_France','class=foobar target=_blank') ?>
  208. => <a href="/routed/url/to/Finance_in_France" class="foobar" target="_blank">my article</a>
  209. È possibile anche aggiungere una delle opzioni specifiche di symfony, per gli helper dei link: `confirm` e `popup`. La prima mostra una finestra di dialogo di conferma JavaScript che appare quando si clicca sul link, mentre la seconda apre il link in una nuova finestra, come mostrato nel listato 9-10.
  210. Listato 9-10 - Le opzioni '`confirm`' e '`popup`' per gli helper dei link
  211. [php]
  212. <?php echo link_to('delete item', 'item/delete?id=123', 'confirm=Are you sure?') ?>
  213. => <a onclick="return confirm('Are you sure?');"
  214. href="/routed/url/to/delete/123.html">add to cart</a>
  215. <?php echo link_to('add to cart', 'shoppingCart/add?id=100', 'popup=true') ?>
  216. => <a onclick="window.open(this.href);return false;"
  217. href="/fo_dev.php/shoppingCart/add/id/100.html">add to cart</a>
  218. <?php echo link_to('add to cart', 'shoppingCart/add?id=100', array(
  219. 'popup' => array('Window title', 'width=310,height=400,left=320,top=0')
  220. )) ?>
  221. => <a onclick="window.open(this.href,'Window title','width=310,height=400,left=320,top=0');return false;"
  222. href="/fo_dev.php/shoppingCart/add/id/100.html">add to cart</a>
  223. Tali opzioni possono essere anche usate insieme.
  224. ### Opzioni GET e POST non reali
  225. Può capitare a volte che gli sviluppatori web utilizzino richieste GET per utilizzarle in POST. Ad esempio, si consideri la seguente URL:
  226. http://www.example.com/index.php/shopping_cart/add/id/100
  227. Questa richiesta cambierà i dati contenuti nell'applicazione, aggiungendo un oggetto al carrello, memorizzato in sessione o nel database. Questo URL potrebbe essere messo nei bookmark, in cache e indicizzato dai motori di ricerca.
  228. Si immagini tutti gli effetti poco puliti o chiari che potrebbero accadere al database o alla metrica di un sito, utilizzando questa tecnica. In effetti, questa richiesta dovrebbe essere considerata come POST, in quanto i motori di ricerca non indicizzano tali richieste.
  229. Symfony fornisce un modo per trasformare effettivamente una chiamata agli helper `link_to()` o `button_to()` in una POST. Aggiungendo semplicemente un'opzione `post=true`, come mostrato nel listato 9-11.
  230. Listato 9-11 - Trasformare un link in una richiesta in POST
  231. [php]
  232. <?php echo link_to('go to shopping cart', 'shoppingCart/add?id=100', 'post=true') ?>
  233. => <a onclick="f = document.createElement('form'); document.body.appendChild(f);
  234. f.method = 'POST'; f.action = this.href; f.submit();return false;"
  235. href="/shoppingCart/add/id/100.html">go to shopping cart</a>
  236. Il tag `<a>` generato dal codice appena mostrato possiede un attributo `href` e i browser senza supporto a JavaScript, come i robot dei motori di ricerca, seguiranno il link come una chiamata in GET.
  237. È quindi necessario che l'azione risponda solo a comandi in POST, ad esempio aggiungendo qualcosa come:
  238. [php]
  239. $this->forward404Unless($request->getRequest()->isMethod('post'));
  240. all'inizio dell'azione. È sufficiente essere sicuri di non utilizzare questi link all'interno di form, in quanto essi generano il proprio tag `<form>`.
  241. È buona abitudine trasformare in POST tutte le chiamate che in effetti spediscono dati.
  242. ### Forzare parametri di richiesta come variabili GET
  243. A seconda delle regole di routing impostate, le variabili passate come parametri a `link_to()` sono trasformate in schemi. Se nessuna regola del file `routing.yml` coincide con l'URI interno, la regola predefinita trasforma `module/action?key=value` in `/module/action/key/value`, come mostrato nel listato 9-12.
  244. Listato 9-12 - Regola di routing predefinita
  245. [php]
  246. <?php echo link_to('my article', 'article/read?title=Finance_in_France') ?>
  247. => <a href="/article/read/title/Finance_in_France">my article</a>
  248. Se si avesse bisogno effettivamente di mantenere la sintassi GET, per avere parametri di richiesta nella forma `?key=value`, si devono forzare tali variabili fuori dall'URL, nell'opzione `query_string`.
  249. Dato che ciò andrebbe in conflitto con un'ancora nell'URL, lo si devi collocare nell'opzione dell'ancora invece che prependerlo all'URI interno. Tutti gli helper dei link accettano questa opzione, come dimostrato nel listato 9-13.
  250. Listato 9-13 - Forzare parametri in GET con l'opzione `query_string`
  251. [php]
  252. <?php echo link_to('my article', 'article/read', array(
  253. 'query_string' => 'title=Finanza_in_Francia',
  254. 'anchor' => 'pippo',
  255. )) ?>
  256. => <a href="/article/read?title=Finanza_in_Francia#pippo">il mio articolo</a>
  257. Un URL con parametri di richiesta in GET può essere interpretata da uno script lato client e dalle variabili $_GET e $_POST lato server.
  258. >**SIDEBAR**
  259. >Helper per le risorse
  260. >
  261. >Il capitolo 7 ha introdotto gli helper `image_tag()`, `stylesheet_tag()` e `javascript_include_ tag()`, che permettono di includere un'immagine, un foglio di stile o uno script JavaScript nella risposta.
  262. >I percorsi di tali asset non vengono processati dal sistema di routing, in quanto puntano a risorse situate nella cartella web pubblica.
  263. >
  264. >Non c'è bisogno di specificare l'estensione per un asset. Symfony aggiungerà automaticamente `.png`, `.js` o `.css` a un'immagine, JavaScript o foglio di stile.
  265. >Inoltre, symfony cercherà automaticamente le risorse nelle cartelle `web/images/`, `web/js/` e `web/css/`. Ovviamente, se si volesse inserire un file situato in una particolare cartella, è possibile utilizzare come parametro il percorso completo.
  266. >Per fissare la dimensione di un'immagine, è sufficiente utilizzare l'attributo `size`. Esso si aspetta un'altezza e una larghezza in pixel, separate da una `x`.
  267. >
  268. > [php]
  269. > <?php echo image_tag('test', 'size=100x20')) ?>
  270. > => <img href="/images/test.png" alt="Test" width="100" height="20"/>
  271. >
  272. >Se si volesse che l'inclusione della risorsa avvenga all'interno della sezione `</head>` (per fogli di stile e JavaScript) basta utilizzare nei template gli helper `use_stylesheet()` e `use_javascript()`, invece delle rispettive versioni `*_tag()` del layout.
  273. >Essi aggiungono le risorse alla risposta e tali risorse vengono incluse prima che la chiusura della sezione `</head>` sia generata e inviata al browser.
  274. ### Utilizzare path assoluti
  275. Gli helper dei link e delle risorse, per impostazione predefinita, generano link relativi. Per forzare link assoluti, si deve utilizzare l'opzione `absolute` impostandola a `true`, come mostrato nel listato 9-14.
  276. Listato 9-14 - Link assoluti invece di relativi
  277. [php]
  278. <?php echo url_for('article/read?title=Finance_in_France') ?>
  279. => '/routed/url/to/Finance_in_France'
  280. <?php echo url_for('article/read?title=Finance_in_France', true) ?>
  281. => 'http://www.example.com/routed/url/to/Finance_in_France'
  282. <?php echo link_to('finance', 'article/read?title=Finance_in_France') ?>
  283. => <a href="/routed/url/to/Finance_in_France">finance</a>
  284. <?php echo link_to('finance', 'article/read?title=Finance_in_France','absolute=true') ?>
  285. => <a href=" http://www.example.com/routed/url/to/Finance_in_France">finance</a>
  286. // Lo stesso funziona per gli asset
  287. <?php echo image_tag('test', 'absolute=true') ?>
  288. <?php echo javascript_include_tag('myscript', 'absolute=true') ?>
  289. >**SIDEBAR**
  290. >L'helper Mail
  291. >
  292. >Quotidianamente, i robot raccolgono indirizzi e-mail e invadono la rete e non si può lasciare tranquillamente l'indirizzo della propria applicazione web senza diventare vittima dello spam entro pochi giorni. Per tale motivo symfony mette a disposizione un helper `mail_to()`.
  293. >
  294. >L'helper `mail_to()` accetta due parametri: l'indirizzo e-mail effettivo e la stringa che deve essere visualizzata. Opzioni aggiuntive accettano un parametro `encode` per stampare codice non leggibile dai robot ma comprensibile dal browser.
  295. >
  296. > [php]
  297. > <?php echo mail_to('myaddress@mydomain.com', 'contact') ?>
  298. > => <a href="mailto:myaddress@mydomain.com'>contact</a>
  299. > <?php echo mail_to('myaddress@mydomain.com', 'contact', 'encode=true') ?>
  300. > => <a href="&#109;&#x61;... &#111;&#x6d;">&#x63;&#x74;... e&#115;&#x73;</a>
  301. >
  302. >Tali messaggi e-mail sono composti da caratteri trasformati da un encoder decimale/esadecimale casuale. Questo trucco ferma la maggior parte degli spambot attuali, ma bisogna comunque porre attenzione al fatto che essi evolvono rapidamente.
  303. Configurazione del routing
  304. --------------------------
  305. Il sistema di routing si preoccupa di eseguire due cose:
  306. * Interpreta l'URL esterno di una richiesta e lo trasforma in un URI interno per capire quale modulo/azione chiamare e i suoi parametri.
  307. * Formatta gli URI interni usati nei link in URL esterni (se si usano gli helper).
  308. La conversione avviene sulla base di una serie di regole di routing. Tali regole sono memorizzate nel file di configurazione `routing.yml` dentro la cartella `config/` dell'applicazione. Il listato 9-15 mostra le regole di routing di default, incluse in ogni progetto symfony.
  309. Listato 9-15 - Regole di routing di default, in `frontend/config/routing.yml`
  310. # regole di default
  311. homepage:
  312. url: /
  313. param: { module: default, action: index }
  314. default_symfony:
  315. url: /symfony/:action/*
  316. param: { module: default }
  317. default_index:
  318. url: /:module
  319. param: { action: index }
  320. default:
  321. url: /:module/:action/*
  322. ### Regole e schemi
  323. Le regole di routing sono associazioni biiettive tra URI interni e URL esterni. Una regola tipica è composta da:
  324. * Una label unica, presente per questioni di velocità e leggibilità, e può essere usata dagli helper dei link
  325. * Uno schema a cui corrispondere (chiave `url`)
  326. * Un array di parametri di richiesta (chiave `param`)
  327. Gli schemi possono contenere caratteri jolly (rappresentati da un asterisco, `*`), anche con nomi (che cominciano con i due punti, `:`). Una corrispondenza con un carattere jolly con nome diventa il valore di un parametro di richiesta. Ad esempio, la regola `default` definita nel listato 9-15 corrisponde a ogni URL del tipo `/pippo/pluto` e imposta il parametro `module` a `pippo` e il parametro `action` a `pluto`.
  328. >**NOTE**
  329. > I caratteri jolly possono essere separati da una barra o da un punto, quindi è possibile scrivere uno schema come questo:
  330. >
  331. > mia_regola:
  332. > url: /pippo/:pluto.:format
  333. > param: { module: miomodulo, action: miaazione }
  334. >
  335. >In questo modo, un URL esterno come 'pippo/12.xml' corrisponderà a `mia_regola` ed eseguirà `miomodulo/miaazione` con due parametri: `$pluto=12` e `$format=xml`. Si possono aggiungere più separatori, cambiando il parametro `segment_separators` nella configurazione del factory `sfPatternRouting` (si veda il capitolo 19).
  336. Il sistema di routing analizza il file `routing.yml` dall'inizio alla fine e si ferma alla prima corrispondenza trovata. Per tale motivo si dovrebbero aggiungere le proprie regole all'inizio, prima di quelle predefinite. Ad esempio, l'URL `/pippo/123` corrisponde a entrambe le regole definite nel listato 9-16, ma symfony testa prima `mia_regola:` e, dato che questa corrisponde, non prova nemmeno ad andare avanti. La richiesta viene gestita dall'azione `miomodulo/miaazione` con il parametro `pluto` impostato a `123` (e non dall'azione `pippo/123`).
  337. Listato 9-16 - Analisi delle regole procede dall'inizio alla fine
  338. mia_regola:
  339. url: /pippo/:pluto
  340. param: { module: miomodulo, action: miaazione }
  341. # default rules
  342. default:
  343. url: /:module/:action/*
  344. >**NOTE**
  345. >La creazione di una nuova azione non implica solamente che si debba creare anche una corrispondente regola di routing. Lo schema predefinito modulo/azione funziona, per cui si può evitare di pensare al file `routing.yml`.
  346. >Comunque, qualora si volesse personalizzare gli URL esterni delle azioni, è necessario aggiungere le nuove regole prima di quelle predefinite.
  347. Il listato 9-17 mostra il processo di modifica del formato dell'URL esterno per un'azione `article/read`.
  348. Listato 9-17 - Cambiare il formato dell'URL esterna per un'azione `article/read`
  349. [php]
  350. <?php echo url_for('my article', 'article/read?id=123) ?>
  351. => /article/read/id/123 // Formato predefinito
  352. // Per cambiare in /article/123 aggiungere una nuova regola all'inizio
  353. // del file routing.yml
  354. article_by_id:
  355. url: /article/:id
  356. param: { module: article, action: read }
  357. Il problema è che la regola `article_by_id` del listato 9-17 rompe il routing di default per tutte le altre azioni del modulo `article`.
  358. Infatti, un URL tipo `article/delete` corrisponderà anch'essa a questa regola, invece che a quella predefinita, e chiamerà l'azione `read` con il parametro `id` con valore `delete`, invece dell'azione `delete`. Per evitare ciò, si deve aggiungere un vincolo in modo che la regola `article_by_id` coincida solo con URL in cui il carattere jolly `id` sia un intero.
  359. ### Vincoli di schema
  360. Quando un URL può corrispondere a più regole, si devono raffinare le regole, aggiungendo vincoli o requisiti allo schema. Un requisito è un insieme di espressioni regolari, a cui i caratteri jolly devono corrispondere perché tutta la regola coincida.
  361. Ad esempio, per modificare la regola `article_by_id` in modo che coincida solo con URL che abbiano il parametro `id` intero, bisogna aggiungere una linea come mostrato nel listato 9-18.
  362. Listato 9-18 - Aggiungere un requisito a una regola di routing
  363. article_by_id:
  364. url: /article/:id
  365. param: { module: article, action: read }
  366. requirements: { id: \d+ }
  367. In questo modo un URL `article/delete` non può più corrispondere alla regola `article_by_id`, perché la stringa `delete` non soddisfa il requisito. Perciò il sistema di routing continuerà a cercare una regola adatta e troverà così la `default`.
  368. >**SIDEBAR**
  369. >Permalink
  370. >
  371. >Una buona linea guida per la sicurezza del routing è quella di nascondere le chiavi primarie il più possibile e sostituirle con stringhe significative.
  372. >E se si volesse mostrare gli articoli tramite il titolo invece che l'ID? Implicherebbe URL esterni nel seguente formato:
  373. >
  374. > http://www.example.com/article/Finance_in_France
  375. >
  376. >Per fare ciò, bisogna creare una nuova azione `permalink`, che utilizzerà un parametro `slug` invece di un `id`, e aggiungere una nuova regola di routing:
  377. >
  378. > article_by_id:
  379. > url: /article/:id
  380. > param: { module: article, action: read }
  381. > requirements: { id: \d+ }
  382. >
  383. > article_by_slug:
  384. > url: /article/:slug
  385. > param: { module: article, action: permalink }
  386. >
  387. >L'azione `permalink` ha bisogno di determinare l'articolo richiesto dal titolo, per cui il modello associato deve fornire un metodo appropriato:
  388. >
  389. > [php]
  390. > public function executePermalink($reques)
  391. > {
  392. > $article = ArticlePeer::retrieveBySlug($request->getParameter('slug');
  393. > $this->forward404Unless($article); // Mostra un 404 se non trova alcun articolo corrispondente a slug
  394. > $this->article = $article; // Passa l'oggetto al template
  395. > }
  396. >
  397. >Si deve anche sostituire i link all'azione `read` nei template con quelli alla `permalink`, per abilitare la formattazione degli URI interni.
  398. >
  399. > [php]
  400. > // Sostituire
  401. > <?php echo link_to('my article', 'article/read?id='.$article->getId()) ?>
  402. >
  403. > // con
  404. > <?php echo link_to('my article', 'article/permalink?slug='.$article->getSlug()) ?>
  405. >Grazie alla linea `requirements`, un URL esterno come `/article/Finance_in_France` corrisponderà alla regola `article_by_slug`, anche se la regola `article_by_id` appare prima.
  406. >
  407. >Da notare che dato che gli articoli verranno recuperati tramite slug, si dovrà aggiungere un indice alla colonna `slug` della tabella `Article` per ottimizzare le prestazioni del database.
  408. ### Impostare valori predefiniti
  409. È possibile assegnare ai parametri dei valori predefiniti, in modo che la regola funzioni anche se il parametro non è definito, impostando tali valori nell'array `param:`.
  410. Ad esempio, la regola `article_by_id` non coincide se il parametro `id` non è definito. Lo si può forzare come mostrato nel listato 9-19.
  411. Listato 9-19 - Impostare valori di default per wildcard
  412. article_by_id:
  413. url: /article/:id
  414. param: { module: article, action: read, id: 1 }
  415. Nel listato 9-20, il parametro `display` assume il valore `true` anche se non è presente nell'URL.
  416. Listato 9-20 - Impostare un valore di default per un parametro di rihiesta
  417. article_by_id:
  418. url: /article/:id
  419. param: { module: article, action: read, id: 1, display: true }
  420. Se si guarda attentamente, si può notare che anche `article` e `read` sono valori di default per le variabili `module` e `action` non presenti nello schema.
  421. >**TIP**
  422. >Si Può definire un parametro predefinito per tutte le regole di routing chiamando il metodo `sfRouting::setDefaultParameter()`. Ad esempio, se si volesse che tutti gli URL abbiano per default un parametro `theme` impostato a `default` come parametro predefinito, basta aggiungere la linea `$this->context->getRouting()->setDefaultParameter('theme', 'default');` a uno dei filtri globali.
  423. ### Velocizzare il routing utilizzando i nomi delle regole
  424. Gli helper dei link accettano un'etichetta invece di una coppia modulo/azione se tale etichetta è preceduta dalla chiocciola (@), come mostrato nel listato 9-21.
  425. Listato 9-21 - Utilizzare label invece di modulo/azione
  426. [php]
  427. <?php echo link_to('my article', 'article/read?id='.$article->getId()) ?>
  428. // può anche essere scritto
  429. <?php echo link_to('my article', '@article_by_id?id='.$article->getId()) ?>
  430. Ci sono pro e contro nella scelta di tal metodo. I vantaggi sono i seguenti:
  431. * La formattazione di URI interne avviene più velocemente, in quanto symfony non dovrà cercare tutte le regole per trovare quella che corrisponde al link. In pagine in cui il numero di link è elevato, sarà possibile notare la differenza di velocità utilizzando label al posto di coppie modulo/azione.
  432. * Usare le label aiuta ad astrarre la logica dietro un'azione. Se si decidesse di cambiare il nome dell'azione ma non l'URL associata, infatti, sarà sufficiente una piccola modifica del file `routing.yml`. Tutte le chiamate `link_to()` continueranno a funzionare senza ulteriori cambiamenti.
  433. * La logica della chiamata è più evidente se si utilizzasse una label. Anche se i moduli e azioni possiedono nomi espliciti, spesso è più evidente richiamare `@display_article_by_slug` di `article/display`.
  434. * Si sa esattamente quali azioni sono abilitate, leggendo il file routing.yml
  435. D'altra parte, uno svantaggio è che aggiungendo nuovi link è meno intuitivo, dato che si deve sempre controllare il file `routing.yml` per controllare il nome della label.
  436. In un progetto di grandi dimensioni si avranno sicuramente un gran numero di regole di routing e risulterà leggermente complesso mantenerle.
  437. In quest'ultimo caso si dovrebbe pacchettizzare l'applicazione in diversi plugin, ognuno con limitato e preciso set di funzionalità.
  438. La scelta di quale metodo utilizzare dipende dal progetto, ma è stato constatato che alla lunga la miglior scelta è quella di utilizzare delle etichette.
  439. >**TIP**
  440. >Se si volesse controllare nel browser quale regola di routing è stata utilizzata per una data richiesta, è sufficiente controllare la sezione "logs" della web debug toolbar e cercare la riga "matched route XXX".
  441. >Maggiori informazioni riguardanti la modalità web debug si possono trovare nel capitolo 16.
  442. ### Creare regole senza routing.yml
  443. Come per la maggior parte dei file di configurazione, `routing.yml` è la soluzione per definire regole di routing, ma non l'unica. È possibile definire regole scritte in PHP, sia nel file `config.php` dell'applicazione che nello script del front controller, ma prima della chiamata a `dispatch()`, perché tale metodo determinerà l'azione da eseguire secondo le regole di routing correnti. Definire regole in PHP permette di creare regole dinamiche, dipendenti dalla configurazione o dai parametri.
  444. L'oggetto che gestisce le regole di routing è il factory `sfPatternRouting`. Essa è disponibile in qualsiasi punto dell'applicazione tramite `sfRouting::getInstance()`. Il suo metodo `prependRoute()` aggiunge una nuova regola prima di tutte quelle definite in `routing.yml`. Si aspetta quattro parametri, che sono gli stessi per le regole di routing: etichette, schemi, array associativo di valori di default e array associativo per i requisiti. Ad esempio, la definizione delle regole del listato 9-18 è equivalente al codice PHP del listato 9-24.
  445. >**NOTE**
  446. >La classe di routing è configurabile nel file di configurazione `factories.yml` (per cambiare la classe di routing di default si consulti il capitolo 17). Questo capitolo illustra la classe `sfPatternRouting`, che è la classe di routing predefinita.
  447. Listato 9-24 - Definire una regola in PHP
  448. [php]
  449. sfContext::getInstance()->getRouting()->prependRoute(
  450. 'article_by_id', // Nome rotta
  451. '/article/:id', // Schema rotta
  452. array('module' => 'article', 'action' => 'read'), // Valori di default
  453. array('id' => '\d+'), // Requisiti
  454. );
  455. Il costruttore della classe `sfRoute` richiede tre parametri: uno schema, un array associativo di valori predefiniti e un altro array associativo per i requisiti.
  456. La classe `sfPatternRouting` possiede altri metodi per la gestione manuale del routing: `clearRoutes()`, `hasRoutes()` e così via.
  457. Per maggiori informazioni in merito puoi consultare le [API](http://www.symfony-project.org/api/1_4/).
  458. >**TIP**
  459. >Una volta compresi a fondo i concetti presentati in questa guida, si può aumentare la comprensione del framework consultando le API o, ancora meglio, i sorgenti di symfony. Non tutti i parametri e i trucchi di symfony possono essere spiegati in questa guida. In ogni caso la documentazione online è illimitata.
  460. -
  461. >**NOTE**
  462. >La classe di routing è configurabile attraverso il file di configurazione `factories.yml` (per modificare la classe di routing predefinita si consulti il capitolo 17). Questo capitolo mostra la classe `sfPatternRouting`, che è la classe predefinita.
  463. Gestire le rotte nelle azioni
  464. -----------------------------
  465. Se si avesse bisogno di avere informazioni sulla rotta corrente, ad esempio in preparazione di un futuro link "Torna alla pagina XXX", si dovrebbe usare i metodi dell'oggetto `sfPatternRouting`.
  466. Gli URI restituiti dal metodo `getCurrentInternalUri()` possono essere utilizzati in una chiamata `link_to()`, come mostrato nel listato 9-25.
  467. Listato 9-25 - Utilizzare `sfRouting` per avere informazioni sulla route corrente
  468. [php]
  469. // Se serve un URL come
  470. http://myapp.example.com/article/21
  471. $routing = sfContext::getInstance()->getRouting();
  472. // Usare il codice seguente nell'azione in article/read
  473. $uri = $routing->getCurrentInternalUri();
  474. => article/read?id=21
  475. $uri = $routing->getCurrentInternalUri(true);
  476. => @article_by_id?id=21
  477. $rule = $routing->getCurrentRouteName();
  478. => article_by_id
  479. // Se servono semplicemente i nomi del modulo/azione corrente,
  480. // si ricordi che essi sono parametri di richiesta effettivi
  481. $module = $request->getParameter('module');
  482. $action = $request->getParameter('action');
  483. Se si avesse bisogno di trasformare un URI in un URL esterno in un'azione, come avviene con `url_for()` nei template, bisogna usare il metodo `genUrl()` dell'oggetto sfController, come mostrato nel listato 9-26.
  484. Listato 9-26 - Utilizzare `sfController` per trasformare un URI interno
  485. [php]
  486. $uri = 'article/read?id=21';
  487. $url = $this->getController()->genUrl($uri);
  488. => /article/21
  489. $url = $this->getController()->genUrl($uri, true);
  490. => http://myapp.example.com/article/21
  491. Riepilogo
  492. ---------
  493. Il routing è un meccanismo bidirezionale pensato per permettere la formattazione di URL esterni in modo che siano più comprensibili e intuitive.
  494. La riscrittura degli URL è necessaria per permettere l'omissione del nome del front controller nell'URL di una delle applicazioni di ogni progetto.
  495. Si deve utilizzare gli helper dei link ogni qualvolta si avesse bisogno di stampare un URL in un template, se si vuole che il sistema di routing funzioni in entrambe le direzioni.
  496. Il file `routing.yml` configura le regole del sistema di routing e utilizza un ordine di precedenza e requisiti.
  497. Il file `settings.yml` contiene impostazioni addizionali riguardanti la presenza del nome del front controller e possibili suffissi in URL esterni.