PageRenderTime 108ms CodeModel.GetById 49ms app.highlight 51ms RepoModel.GetById 2ms app.codeStats 0ms

/jobeet/it/04.markdown

https://github.com/rafaelgou/symfony1-docs
Markdown | 860 lines | 700 code | 160 blank | 0 comment | 0 complexity | 0e874c0c6d88949dbcc508c8a6d93ac9 MD5 | raw file
  1Giorno 4: Il controllore e la vista
  2===================================
  3
  4Ieri abbiamo analizzato come symfony possa semplificare la gestione
  5del database astraendo le differenze tra i vari motori di database e convertendo
  6gli elementi dello schema relazionale in classi orientate agli oggetti. Abbiamo inoltre
  7giocato con ##ORM## per descrivere lo schema del database, creare le tabelle e 
  8popolare il database con alcuni dati iniziali.
  9
 10Oggi andremo a personalizzare il modulo `job` creato ieri. Il modulo `job`
 11contiene già tutto il codice di cui abbiamo bisogno per Jobeet:
 12
 13 * Una pagina per elencare tutte le offerte di lavoro
 14 * Una pagina per creare una nuova offerta
 15 * Una pagina per aggiornare un'offerta esistente
 16 * Una pagina per cancellare un'offerta
 17
 18Nonostante il codice sia pronto per essere usato com'è, rifattorizzeremo
 19i template per attenerci il più possibile ai mockup di Jobeet.
 20
 21L'architettura ~MVC~
 22--------------------
 23
 24Se siete abituati a sviluppare siti web con PHP senza utilizzare un framework,
 25probabilmente utilizzate il paradigma del singolo file PHP per singola pagina
 26HTML. Questi file PHP probabilmente contengono lo stesso tipo di struttura:
 27inizializzazione e configurazioni globali, business logic relativa alla pagina
 28richiesta, recupero dei record dal database e infine il codice HTML che 
 29costruisce la pagina.
 30
 31Potreste usare un motore per i template per separare la logica dall'HTML.
 32Forse usate un layer per l'astrazione del database per separare l'interazione
 33tra il modello e la business logic. Purtroppo il più delle volte finite per 
 34avere una grande quantità di codice che è un vero e proprio incubo da mantenere.
 35È stato veloce da realizzare, ma con il passare del tempo è sempre più difficile
 36apportare cambiamenti, specialmente perché nessuno eccetto voi capisce
 37come è fatto e come funziona.
 38
 39Come per tutti i problemi esistono piacevoli soluzioni. Per lo sviluppo web la
 40soluzione più diffusa di questi tempi per organizzare il codice è
 41rappresentata dal [**pattern architetturale MVC**](http://it.wikipedia.org/wiki/Model-View-Controller).
 42Brevemente il pattern MVC definisce un modo per organizzare il proprio
 43codice secondo la sua natura. Questo pattern separa il codice in **tre strati**:
 44
 45  * Il **~Modello~** è lo strato che definisce la business logic (il database
 46    appartiene a questo strato). Probabilmente siete al corrente del fatto che symfony
 47    memorizza tutte le classi e i file relativi al Modello nella cartella `lib/model/`.
 48
 49  * La **~Vista~** rappresenta ciò con cui l'utente interagisce (un motore di template è
 50    parte di questo strato). In symfony lo strato della Vista è principalmente costituito
 51    da template PHP. Queste sono memorizzate in varie cartelle `templates` come 
 52    vedremo in seguito.
 53
 54  * Il **~Controllore~** è la parte di codice che chiama il Modello per ottenere 
 55    alcuni dati da passare alla Vista per visualizzarli attraverso il client.
 56    Quando abbiamo installato symfony il primo giorno abbiamo visto che tutte
 57    le richieste sono gestite dai front controller (`index.php` e `frontend_dev.php`).
 58    Questi front controller delegano il vero lavoro alle **azioni**. Come
 59    abbiamo visto ieri queste azioni sono raggruppate in **moduli**.
 60
 61![MVC](http://www.symfony-project.org/images/jobeet/1_4/04/mvc.png)
 62
 63Oggi utilizzeremo il mockup definito il giorno 2 per personalizzare l'homepage
 64e la pagina delle offerte di lavoro. Inoltre le renderemo dinamiche. Lungo la strada 
 65perfezioneremo molte cose in molti file differenti per mostrare la struttura
 66delle cartelle di symfony e come separare il codice tra i vari strati.
 67
 68Il Layout
 69---------
 70
 71Per prima cosa se avete guardato con attenzione i mockup avrete notato che
 72gran parte di ogni pagina sembra sempre la stessa. Sapere già che la duplicazione
 73del codice è una cattiva pratica se stiamo parlando di codice HTML o PHP, perciò
 74abbiamo bisogno di trovare un modo per prevenire il fatto che elementi comuni
 75implichino duplicazione del codice.
 76
 77Un modo per risolvere questo problema è quello di definire un header e un footer
 78includendoli in ogni template:
 79
 80![Header e footer](http://www.symfony-project.org/images/jobeet/1_4/04/header_footer.png)
 81
 82Ma qui i file di header e footer non contengono HTML valido. Deve esserci una
 83strada migliore. Invece di reinventate la ruota, utilizzeremo un altro design
 84pattern per risolvere questo problema: il
 85[design pattern ~decorator~](http://it.wikipedia.org/wiki/Decorator_pattern).
 86Il design pattern decorator risolve il problema agendo al contrario: il template
 87viene decorato dopo che il contenuto è stato reso da un template globale,
 88in symfony questo è definito come un **~layout~**:
 89
 90![Layout](http://www.symfony-project.org/images/jobeet/1_4/04/layout.png)
 91
 92Il layout di default di un'applicazione è chiamato `layout.php` e può
 93essere trovato nella cartella `apps/frontend/templates/`. Questa cartella
 94contiene tutti i template globali di un'applicazione.
 95
 96Rimpiazzate il layout di default di symfony con il seguente codice:
 97
 98    [php]
 99    <!-- apps/frontend/templates/layout.php -->
100    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
101     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
102    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
103      <head>
104        <title>Jobeet - Your best job board</title>
105        <link rel="shortcut icon" href="/favicon.ico" />
106        <?php include_javascripts() ?>
107        <?php include_stylesheets() ?>
108      </head>
109      <body>
110        <div id="container">
111          <div id="header">
112            <div class="content">
113              <h1><a href="<?php echo url_for('job/index') ?>">
114                <img src="/images/logo.jpg" alt="Jobeet Job Board" />
115              </a></h1>
116
117              <div id="sub_header">
118                <div class="post">
119                  <h2>Ask for people</h2>
120                  <div>
121                    <a href="<?php echo url_for('job/index') ?>">Post a Job</a>
122                  </div>
123                </div>
124
125                <div class="search">
126                  <h2>Ask for a job</h2>
127                  <form action="" method="get">
128                    <input type="text" name="keywords"
129                      id="search_keywords" />
130                    <input type="submit" value="search" />
131                    <div class="help">
132                      Enter some keywords (city, country, position, ...)
133                    </div>
134                  </form>
135                </div>
136              </div>
137            </div>
138          </div>
139
140          <div id="content">
141            <?php if ($sf_user->hasFlash('notice')): ?>
142              <div class="flash_notice">
143                <?php echo $sf_user->getFlash('notice') ?>
144              </div>
145            <?php endif; ?>
146
147            <?php if ($sf_user->hasFlash('error')): ?>
148              <div class="flash_notice">
149                <?php echo $sf_user->getFlash('error') ?>
150              </div>
151            <?php endif; ?>
152
153            <div class="content">
154              <?php echo $sf_content ?>
155            </div>
156          </div>
157
158          <div id="footer">
159            <div class="content">
160              <span class="symfony">
161                <img src="/images/jobeet-mini.png" />
162                powered by <a href="http://www.symfony-project.org/">
163                <img src="/images/symfony.gif" alt="symfony framework" />
164                </a>
165              </span>
166              <ul>
167                <li><a href="">About Jobeet</a></li>
168                <li class="feed"><a href="">Full feed</a></li>
169                <li><a href="">Jobeet API</a></li>
170                <li class="last"><a href="">Affiliates</a></li>
171              </ul>
172            </div>
173          </div>
174        </div>
175      </body>
176    </html>
177
178Un template di symfony è solamente un semplice file PHP. Nel template del 
179layout ci sono chiamate a funzioni PHP e riferimenti a variabili PHP. 
180`$sf_content` è la variabile più interessante: è definita dal framework stesso
181e contiene l'HTML generato dall'azione.
182
183Se navigate il modulo `job` (`http://jobeet.localhost/frontend_dev.php/job`)
184potete vedere che tutte le azioni sono decorate dal layout.
185
186
187I Fogli di stile, le Immagini e i Javascript
188--------------------------------------------
189
190Dato che questo tutorial non riguarda il web design, abbiamo già preparato
191tutte le risorse necessarie per Jobbet:
192organizzeremo un concorso per il miglior design il giorno 21, abbiamo
193[scaricate l'archivio delle immagini](http://www.symfony-project.org/get/jobeet/images.zip)
194e mettetele nella cartella `web/images`;
195[scarica tel'archivio dei fogli di stile](http://www.symfony-project.org/get/jobeet/css.zip)
196e metteteli nella cartella `web/css/`.
197
198>**NOTE**
199>Nel layout abbiamo incluso una *favicon*. Potete
200>[scaricare quella di Jobeet](http://www.symfony-project.org/images/jobeet/favicon.ico)
201>e metterla nella cartella `web/`.
202
203![Il modulo job con layout ed elementi grafici](http://www.symfony-project.org/images/jobeet/1_4/04/job_layout_assets.png)
204
205>**TIP**
206>Di default, il task `generate:project` ha creato tre cartelle per i file degli
207>elementi grafici: `web/images/` per le immagini, `web/~css~/` per i ~fogli di stile~
208>`web/js/` per i ~Javascript~. Questa è una delle ~convenzioni~ definite da symfony,
209>ma potete salvarli ovunque vogliate all'interno della cartella `web/`.
210
211Il lettore più attento avrà notato che anche se il file `main.css` non è
212menzionato in nessun posto nel layout, è presente nell'HTML generato. Ma nessun
213altro file è presente. Com'è possibile?
214
215Il foglio di stile è stato incluso dalla funzione `~include_stylesheets~()`
216chiamata nel tag `<head>` all'interno del layout. La funzione `include_stylesheets()`
217è chiamata **helper**. Un helper è una funzione definita da symfony, che accetta
218dei parametri e restituisce codice HTML. La maggior parte delle volte, gli helper
219fanno risparmiare del tempo e racchiudono degli spezzoni di codice usati di
220frequente nei template. L'helper `include_stylesheets()` genera il tag `<link>`
221per i fogli di stile.
222
223Ma come fa l'helper a sapere quali fogli di stile includere?
224
225Lo strato della ~Vista~ può essere configurato modificando il file di configurazione
226dell'applicazione `~view.yml~`. Questo è quello generato di default dal comando
227`generate:app`:
228
229    [yml]
230    # apps/frontend/config/view.yml
231    default:
232      http_metas:
233        content-type: text/html
234
235      metas:
236        #title:        symfony project
237        #description:  symfony project
238        #keywords:     symfony, project
239        #language:     en
240        #robots:       index, follow
241
242      stylesheets:    [main.css]
243
244      javascripts:    []
245
246      has_layout:     true
247      layout:         layout
248
249Il file `view.yml` configura le impostazioni di `default` per ogni template
250dell'applicazione. Per esempio, l'elemento `stylesheets` definisce un array di
251fogli di stile da includere in ogni pagina dell'applicazione (l'inclusione è
252fatta dall'helper `include_stylesheets()`).
253
254>**NOTE**
255>Nel file `view.yml` di default, il file referenziato è `main.css` e non
256>`/css/main.css`. Comunque, le due definizioni sono equivalente in quanto
257>symfony aggiunge il prefisso `/~css~/` ai percorsi relativi.
258
259Se molti file sono definiti, symfony li includerà nello stesso ordine della
260definizione:
261
262    [yml]
263    stylesheets:    [main.css, jobs.css, job.css]
264
265Si può anche cambiare l'attributo `media` e omettere il suffisso `.css`:
266
267    [yml]
268    stylesheets:    [main.css, jobs.css, job.css, print: { media: print }]
269
270Questo file di configurazione sarà tradotto in:
271
272    [php]
273    <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
274    <link rel="stylesheet" type="text/css" media="screen" href="/css/jobs.css" />
275    <link rel="stylesheet" type="text/css" media="screen" href="/css/job.css" />
276    <link rel="stylesheet" type="text/css" media="print" href="/css/print.css" />
277
278>**TIP**
279>Il file di configurazione `view.yml` definisce anche il ~layout~ usato dall'applicazione.
280>Di default, il nome è `layout`, così symfony decora ogni pagina con il file
281>`layout.php`. Si può anche disabilitare il processo di decorazione una volta per
282>tutte, impostando la proprietà `~has_layout~` a `false`.
283
284Funziona già così com'è, ma il file `jobs.css` è necessario solo per l'homepage,
285e il file `job.css` è necessario solo per la pagina del lavoro. Il file `view.yml`
286può essere personalizzato in ogni modulo. Cambiamo la chiave `stylesheets`
287del file `view.yml` dell'applicazione in modo che contenga solo il file `main.css`:
288
289    [yml]
290    # apps/frontend/config/view.yml
291    stylesheets:    [main.css]
292
293Per personalizzare la vista del modulo `job`, creiamo un file `view.yml`
294all'interno della cartella `apps/frontend/modules/job/config`:
295
296    [yml]
297    # apps/frontend/modules/job/config/view.yml
298    indexSuccess:
299      stylesheets: [jobs.css]
300
301    showSuccess:
302      stylesheets: [job.css]
303
304All'interno delle sezioni `indexSuccess` e `showSuccess` (sono i nomi dei file dei
305template associati alle azioni `index` e `show`, come vedremo in seguito), si può
306personalizzare ogni elemento all'interno della sezione `default` del file
307`view.yml` dell'applicazione. Tutti i nuovi elementi sono sostituiti a quelli
308definiti nella configurazione dell'applicazione. Si possono inoltre definire alcune
309configurazioni per tutte le azioni di un modulo con la sezione speciale `all`.
310
311>**SIDEBAR**
312>Principi di configurazione di symfony
313>
314>Per molti file di ~configurazione~ di symfony, la stessa impostazione può
315>essere definita in livelli differenti:
316>
317>  * La configurazione di default è all'interno del framework
318>  * La configurazione globale per il progetto (in `config/`)
319>  * La configurazione locale per un'applicazione (in `apps/APP/config/`)
320>  * La configurazione locale per un modulo (in `apps/APP/modules/MODULE/config/`)
321>
322>Nell'esecuzione, il sistema di configurazione unisce tutti i valori dei
323>differenti file se esistono e crea un copia cache dei risultati per migliorare
324>le performance.
325
326Solitamente, quando qualcosa è configurabile tramite un file di configurazione,
327lo è anche tramite codice PHP. al posto di create un file `view.yml` per il modulo
328`job` per esempio, potete anche usare l'helper `~use_stylesheet~()` per includere
329un foglio di stile da un template:
330
331    [php]
332    <?php use_stylesheet('main.css') ?>
333
334Si può anche usare questo helper nel layout per includere un foglio di stile globale.
335
336Scegliere tra un metodo è l'altro è solo un questione di gusti. Il file `view.yml`
337fornisce un modo per definire impostazioni per tutte le azioni di un modulo,
338che non è possibile in un template, ma la configurazione è statica. D'altro canto,
339usare l'~helper~ `use_stylehseet()` è più flessibile e, soprattutto, è tutto nello
340stesso posto: la definizione dello stile e il codice HTMl. Per Jobeet useremo
341l'helper `use_stylesheet()`, per cui potete eliminare il file `view.yml` che
342abbiamo appena creato e modificare i template `job` con le chiamate a `use_stylesheet()`:
343
344    [php]
345    <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
346    <?php use_stylesheet('jobs.css') ?>
347
348    <!-- apps/frontend/modules/job/templates/showSuccess.php -->
349    <?php use_stylesheet('job.css') ?>
350
351>**NOTE**
352>Simmetricamente, la configurazione di Javascript è eseguita dell'elemento `javascripts`
353>del file `view.yml` e l'helper `~use_javascript~()` definisce i file JavaScript
354>da includere in un template.
355
356L'Homepage di Jobeet
357--------------------
358
359Come visto nel giorno 3, la pagina dei lavori è generata dall'azione `index` del
360modulo `job`. L'azione `index` è la parte Controller della pagina e il template
361associato, `indexSuccess.php`, è la View:
362
363    apps/
364      frontend/
365        modules/
366          job/
367            actions/
368              actions.class.php
369            templates/
370              indexSuccess.php
371
372### L'azione
373
374Ogni ~azione~ è rappresentata da un metodo di una classe. Per l'homepage di Jobeet,
375la classe è `jobActions` (il nome del modulo seguito dal suffisso `Actions`) ed
376il metodo è `executeIndex()` (`execute` seguito dal nome dell'azione).
377L'azione recupera tutti i lavori dal database:
378
379    [php]
380    // apps/frontend/modules/job/actions/actions.class.php
381    class jobActions extends sfActions
382    {
383      public function executeFooBar(sfWebRequest $request)
384      {
385<propel>
386        $this->jobeet_jobs = JobeetJobPeer::doSelect(new Criteria());
387</propel>
388<doctrine>
389        $this->jobeet_jobs = Doctrine::getTable('JobeetJob')
390          ->createQuery('a')
391          ->execute();
392</doctrine>
393      }
394
395      // ...
396    }
397
398<propel>
399Diamo uno sguardo da vicino al codice: il metodo `executeIndex()` (il Controllore)
400chiama il Modello `JobeetJobPeer` per recuperare tutti i lavori (`new Criteria()`).
401Esso restituisce un array di oggetti `JobeetJob` che sono assegnati alla
402proprietà `jobeet_jobs`.
403</propel>
404<doctrine>
405Diamo uno sguardo da vicino al codice: il metodo `executeIndex()` (il Controllore)
406chiama il Modello `JobeetJob` per creare una query per recuperare tutti i
407lavori. Esso restituisce un `Doctrine_Collection` di oggetti `JobeetJob` che
408sono assegnati alla proprietà `jobeet_jobs`.
409</doctrine>
410Ognuna di queste proprietà è automaticamente passata al template (la Vista). Per
411passare dati dal Controllore alla Vista, basta creare una nuova proprietà:
412
413    [php]
414    public function executeIndex(sfWebRequest $request)
415    {
416      $this->foo = 'bar';
417      $this->bar = array('bar', 'baz');
418    }
419
420Questo codice renderà le variabili `$foo` e `$bar` accessibili dal template.
421
422### Il Template
423
424Di default, il ~template~ associato a un'azione è dedotto da symfony grazie a
425una convenzione (il nome dell'azione seguito dal suffisso `Success`).
426
427Il template `indexSuccess.php` genera una tabella HTML per tutti i lavori.
428Ecco il codice attuale del template:
429
430    [php]
431    <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
432    <?php use_stylesheet('jobs.css') ?>
433    
434    <h1>Job List</h1>
435
436    <table>
437      <thead>
438        <tr>
439          <th>Id</th>
440          <th>Category</th>
441          <th>Type</th>
442    <!-- more columns here -->
443          <th>Created at</th>
444          <th>Updated at</th>
445        </tr>
446      </thead>
447      <tbody>
448        <?php foreach ($jobeet_jobs as $jobeet_job): ?>
449        <tr>
450          <td>
451            <a href="<?php echo url_for('job/show?id='.$jobeet_job->getId()) ?>">
452              <?php echo $jobeet_job->getId() ?>
453            </a>
454          </td>
455          <td><?php echo $jobeet_job->getCategoryId() ?></td>
456          <td><?php echo $jobeet_job->getType() ?></td>
457    <!-- more columns here -->
458          <td><?php echo $jobeet_job->getCreatedAt() ?></td>
459          <td><?php echo $jobeet_job->getUpdatedAt() ?></td>
460        </tr>
461        <?php endforeach; ?>
462      </tbody>
463    </table>
464
465    <a href="<?php echo url_for('job/new') ?>">New</a>
466
467Nel codice del template, il `foreach` scorre attraverso la lista di oggetti `Job`
468(`$jobeet_jobs`) e, per ognuno di loro, ogni valore delle colonne è visualizzato.
469Ricordate che accedere al valore di una colonna è semplice come chiamare un metodo getter,
470il cui nome inizia con `get` ed è seguito dal nome della colonna in formato
471~camelCase~ (per esempio il metodo `getCreatedAt()` per la colonna `created_at`).
472
473Ripuliamolo un po', per visualizzare solo un sottoinsieme di colonne disponibili:
474
475    [php]
476    <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
477    <?php use_stylesheet('jobs.css') ?>
478
479    <div id="jobs">
480      <table class="jobs">
481        <?php foreach ($jobeet_jobs as $i => $job): ?>
482          <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>">
483            <td><?php echo $job->getLocation() ?></td>
484            <td>
485              <a href="<?php echo url_for('job/show?id='.$job->getId()) ?>">
486                <?php echo $job->getPosition() ?>
487              </a>
488            </td>
489            <td><?php echo $job->getCompany() ?></td>
490          </tr>
491        <?php endforeach; ?>
492      </table>
493    </div>
494
495![Homepage](http://www.symfony-project.org/images/jobeet/1_4/04/homepage.png)
496
497La funzione `url_for()` in questo template è un helper che verrà discusso domani.
498
499Il template della pagina del lavoro
500-----------------------------------
501
502Personalizziamo ora il template della pagina del lavoro. Apriamo il file `showSuccess.php`
503e sostituiamo il suo contenuto con il codice seguente:
504
505    [php]
506    <!-- apps/frontend/modules/job/templates/showSuccess.php -->
507    <?php use_stylesheet('job.css') ?>
508    <?php use_helper('Text') ?>
509
510    <div id="job">
511      <h1><?php echo $job->getCompany() ?></h1>
512      <h2><?php echo $job->getLocation() ?></h2>
513      <h3>
514        <?php echo $job->getPosition() ?>
515        <small> - <?php echo $job->getType() ?></small>
516      </h3>
517
518      <?php if ($job->getLogo()): ?>
519        <div class="logo">
520          <a href="<?php echo $job->getUrl() ?>">
521            <img src="/uploads/jobs/<?php echo $job->getLogo() ?>"
522              alt="<?php echo $job->getCompany() ?> logo" />
523          </a>
524        </div>
525      <?php endif; ?>
526
527      <div class="description">
528        <?php echo simple_format_text($job->getDescription()) ?>
529      </div>
530
531      <h4>How to apply?</h4>
532
533      <p class="how_to_apply"><?php echo $job->getHowToApply() ?></p>
534
535      <div class="meta">
536<propel>
537        <small>posted on <?php echo $job->getCreatedAt('m/d/Y') ?></small>
538</propel>
539<doctrine>
540        <small>posted on <?php echo $job->getDateTimeObject('created_at')->format('m/d/Y') ?></small>
541</doctrine>
542      </div>
543
544      <div style="padding: 20px 0">
545        <a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">Edit</a>
546      </div>
547    </div>
548
549Questo template usa la variabile `$job`, passata dall'azione, per mostrare l'informazione
550sul lavoro. Poiché abbiamo rinominato la variabile passata al template da `$jobeet_job` a
551`$job`, dobbiamo riportare questo cambiamento nell'azione `show` (attenzione, ci
552sono due occorrenze della variabile):
553
554    [php]
555    // apps/frontend/jobeet/actions/actions.class.php
556    public function executeShow(sfWebRequest $request)
557    {
558<propel>
559      $this->job = JobeetJobPeer::retrieveByPk($request->getParameter('id'));
560</propel>
561<doctrine>
562      $this->job = Doctrine::getTable('JobeetJob')->find($request->getParameter('id'));
563</doctrine>
564      $this->forward404Unless($this->job);
565    }
566
567<propel>
568Notate che alcuni metodi di Propel accettano dei parametri. Siccome abbiamo definito la
569colonna `created_at` come un timestamp, il metodo `getCreatedAt()` accetta uno schema di
570formattazione della data come primo parametro.
571
572    [php]
573    $job->getCreatedAt('m/d/Y');
574</propel>
575<doctrine>
576Notate che le colonne di tipo date possono essere convertite a istanze dell'oggetto PHP DateTime.
577Così come abbiamo definito le colonne `created_at` come timestamp, è possibile convertire il
578valore della colonna a un oggetto DateTime usando il metodo `getDateTimeObject()`
579e dopo chiamando il metodo `format()` che prende un modello di formattazione della data come
580suo primo parametro:
581
582    [php]
583    $job->getDateTimeObject('created_at')->format('m/d/Y');
584</doctrine>
585
586>**NOTE**
587>La descrizione del lavoro usa l'helper `simple_format_text()` per formattarsi come HTML,
588>sostituendo ad esempio gli "a capo" con un `<br />`. Poiché tale helper appartiene al
589>gruppo di helper `Text`, che non è caricato di default, l'abbiamo caricato manualmente
590>usando l'helper `~use_helper~()`.
591
592![Pagina del lavoro](http://www.symfony-project.org/images/jobeet/1_4/04/job.png)
593
594Gli ~slot~
595----------
596
597Ed ora, il titolo di tutte le pagine è definito nel tag `<title>` del layout:
598
599    [php]
600    <title>Jobeet - Your best job board</title>
601
602Ma per la pagina del lavoro vogliamo fornire delle informazioni più dettagliate, come il
603nome della compagnia e la posizione del lavoro.
604
605In symfony, quando una zona del layout dipende dal template che deve essere
606visualizzato, occorre definire uno slot:
607
608![Slot](http://www.symfony-project.org/images/jobeet/1_4/04/layout_slots.png)
609
610Aggiungiamo uno slot al layout per avere un titolo dinamico:
611
612    [php]
613    // apps/frontend/templates/layout.php
614    <title><?php include_slot('title') ?></title>
615
616Ogni slot è definito da un nome (`title`) e può essere visualizzato usando l'helper
617`~include_slot~()`. Ora, all'inizio del template `showSuccess.php`, usiamo l'helper
618`slot()` per definire il contenuto dello slot per la pagina del lavoro:
619
620    [php]
621    // apps/frontend/modules/job/templates/showSuccess.php
622    <?php slot('title', sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition())) ?>
623
624Se il titolo è complesso da generare, l'helper `slot()` può anche essere usato con un
625blocco di codice:
626
627    [php]
628    // apps/frontend/modules/job/templates/showSuccess.php
629    <?php slot('title') ?>
630      <?php echo sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()) ?>
631    <?php end_slot(); ?>
632
633Per alcune pagine, come la homepage, ci serve solo un titolo generico. Invece di
634ripetere lo stesso titolo più e più volte nei template, possiamo definire un titolo di
635default nel layout:
636
637    [php]
638    // apps/frontend/templates/layout.php
639    <title>
640      <?php include_slot('title', 'Jobeet - Your best job board') ?>
641    </title>
642
643Il secondo parametro del metodo `include_slot()` è il valore predefinito per
644lo slot se non è stato definito. Se il valore predefinito è lungo o ha
645alcuni tag HTML, si può anche crearlo come mostrato nel seguente codice:
646
647    [php]
648    // apps/frontend/templates/layout.php
649    <title>
650      <?php if (!include_slot('title')): ?>
651        Jobeet - Your best job board
652      <?php endif; ?>
653    </title>
654
655L'helper `include_slot()` restituisce `true` se lo slot è stato definito. Quindi, se
656abbiamo definito uno slot `title` in un template, verrà usato; altrimenti, verrà usato
657il titolo di default.
658
659>**TIP**
660>Abbiamo già visto alcuni helper che iniziano con `include_`. Questi helper
661>visualizzano l'HTML e in molti casi hanno una controparte `get_`, per restituire
662>solamente il contenuto:
663>
664>     [php]
665>     <?php include_slot('title') ?>
666>     <?php echo get_slot('title') ?>
667>
668>     <?php include_stylesheets() ?>
669>     <?php echo get_stylesheets() ?>
670
671L'azione della pagina del lavoro
672--------------------------------
673
674La pagina del lavoro è generata dall'azione `show`, definita nel metodo `executeShow()`
675del modulo `job`:
676
677    [php]
678    class jobActions extends sfActions
679    {
680      public function executeShow(sfWebRequest $request)
681      {
682<propel>
683      $this->job = JobeetJobPeer::retrieveByPk($request->getParameter('id'));
684</propel>
685<doctrine>
686      $this->job = Doctrine::getTable('JobeetJob')->find($request->getParameter('id'));
687</doctrine>
688        $this->forward404Unless($this->job);
689      }
690
691      // ...
692    }
693
694<propel>
695Come nell'azione `index`, la classe `JobeetJobPeer` è usata per recuperare un
696lavoro, stavolta usando il metodo `retrieveByPk()`. Il parametro di questo metodo
697è l'identificatore univoco di un lavoro, la sua ~chiave primaria~. La prossima sezione
698spiegherà perché l'istruzione `$request->getParameter('id')` restituisce la chiave
699primaria del lavoro.
700</propel>
701<doctrine>
702Come nell'azione `index`, la classe `JobeetJob` è usata per recuperare un
703lavoro, stavolta usando il metodo `find()`. Il parametro di questo metodo
704è l'identificatore univoco di un lavoro, la sua ~chiave primaria~. La prossima sezione
705spiegherà perché l'istruzione `$request->getParameter('id')` restituisce la chiave
706primaria del lavoro.
707</doctrine>
708
709<propel>
710>**TIP**
711>Le classi del modello generate contengono molti metodi utili per interagire
712>con gli oggetti del progetto. Prendetevi un po' di tempo per analizzare il codice
713> che si trova nella cartella `lib/om/` e per scoprire la potenza nascosta
714>in queste classi.
715</propel>
716
717Se il lavoro non esiste nel database, vogliamo rimandare l'utente a una pagina ~404~,
718che è esattamente ciò che fa il metodo `forward404Unless()`. Questo accetta un
719booleano come primo parametro e, a meno che non sia vero, ferma il flusso corrente
720dell'esecuzione. Poiché i metodi "forward" fermano l'esecuzione dell'azione
721sollevando un'eccezione `sfError404Exception`, non si ha bisogno di usare `return`
722successivamente.
723
724Come per le eccezioni, la pagina mostrata all'utente è diversa negli ~ambienti~
725`prod` e `dev`:
726
727![404 errore in ambiente dev](http://www.symfony-project.org/images/jobeet/1_4/05/404_dev.png)
728
729![404 errore in ambiente prod](http://www.symfony-project.org/images/jobeet/1_4/05/404_prod.png)
730
731>**NOTE**
732>Prima di pubblicare il sito Jobeet su un server di produzione, impareremo a
733>personalizzare la pagina 404 di default.
734
735-
736
737>**SIDEBAR**
738>La famiglia dei metodi "~forward~"
739>
740>Una chiamata a `forward404Unless` in realtà equivale a:
741>
742>     [php]
743>     $this->forward404If(!$this->job);
744>
745>che equivale anche a:
746>
747>     [php]
748>     if (!$this->job)
749>     {
750>       $this->forward404();
751>     }
752>
753>Il metodo stesso `forward404()` è solo una scorciatoia per:
754>
755>     [php]
756>     $this->forward('default', '404');
757>
758>Il metodo `forward()` rimanda a un'altra azione della stessa applicazione;
759>negli esempi precedenti, all'azione `404` del modulo `default`. Il modulo
760>`default` è distribuito con symfony e fornisce delle azioni di default per le
761> pagine 404, secure e login.
762
763La richiesta e la risposta
764--------------------------
765
766Quando si visitano le pagine `/job` o `/job/show/id/1` nel proprio browser, si
767dà inizio a un viaggio nel server web. Il browser invia una **~richiesta~** e il
768server rimanda indietro una **~risposta~**.
769
770Abbiamo già visto che symfony incapsula la richiesta in un oggetto `sfWebRequest`
771(si veda il metodo `executeShow()`). E siccome symfony è un framework orientato
772agli oggetti, anche la risposta è un oggetto, della classe `sfWebResponse`.
773Si può accedere all'oggetto risposta in un'azione richiamando `$this->getResponse()`.
774
775Questi oggetti forniscono molti metodi utili per accedere alle informazioni dalle
776funzioni e dalle variabili globali di PHP.
777
778>**NOTE**
779>Perché symfony ha un wrap di funzionalità esistenti in PHP? Innanzitutto,
780>perché i metodi di symfony sono più potenti delle controparti PHP. Poi, perché
781>quando si testa un'applicazione, è più facile simulare un oggetto richiesta o
782>risposta piuttosto che trattare variabili globali o usare funzioni come
783>`header()`, che fanno troppe cose di nascosto.
784
785### La richiesta
786
787La classe `sfWebRequest` è un wrapper per le array globali di PHP `~$_SERVER~`,
788`~$_COOKIE~`, `~$_GET~`, `~$_POST~` e `~$_FILES~`
789
790 Nome del metodo      | Equivalente PHP
791 -------------------- | --------------------------------------------------
792 `getMethod()`        | `$_SERVER['REQUEST_METHOD']`
793 `getUri()`           | `$_SERVER['REQUEST_URI']`
794 `getReferer()`       | `$_SERVER['HTTP_REFERER']`
795 `getHost()`          | `$_SERVER['HTTP_HOST']`
796 `getLanguages()`     | `$_SERVER['HTTP_ACCEPT_LANGUAGE']`
797 `getCharsets()`      | `$_SERVER['HTTP_ACCEPT_CHARSET']`
798 `isXmlHttpRequest()` | `$_SERVER['X_REQUESTED_WITH'] == 'XMLHttpRequest'`
799 `getHttpHeader()`    | `$_SERVER`
800 `getCookie()`        | `$_COOKIE`
801 `isSecure()`         | `$_SERVER['HTTPS']`
802 `getFiles()`         | `$_FILES`
803 `getGetParameter()`  | `$_GET`
804 `getPostParameter()` | `$_POST`
805 `getUrlParameter()`  | `$_SERVER['PATH_INFO']`
806 `getRemoteAddress()` | `$_SERVER['REMOTE_ADDR']`
807
808Abbiamo già avuto accesso ai parametri della richiesta usando il metodo
809`getParameter()`. Esso restituisce un valore dalla variabile globale `$_GET`
810o `$_POST`, oppure dalla variabile `~PATH_INFO~`.
811
812Se si vuole essere certi che un parametro della richiesta venga da una
813particolare di queste variabili, si devono usare rispettivamente i metodi
814`getGetParameter()`, `getPostParameter()` e `getUrlParameter()`.
815
816>**NOTE**
817>Se si vuole limitare un'azione per un ~metodo HTTP~ specifico, ad esempio se si
818>vuole essere sicuri che una form sia inviata come `POST`, si può usare
819>il metodo `isMethod()`: `$this->forwardUnless($request->isMethod('POST'));`.
820
821### La risposta
822
823La classe `sfWebResponse` è un wrapper per le funzioni PHP `~header~()`
824e `setraw~cookie~()`:
825
826 Nome del metodo               | Equivalente PHP
827 ----------------------------- | ----------------
828 `setCookie()`                 | `setrawcookie()`
829 `setStatusCode()`             | `header()`
830 `setHttpHeader()`             | `header()`
831 `setContentType()`            | `header()`
832 `addVaryHttpHeader()`         | `header()`
833 `addCacheControlHttpHeader()` | `header()`
834
835Ovviamente, la classe `sfWebResponse` fornisce anche un modo per impostare il
836contenuto della risposta (`setContent()`) e inviare la  risposta al browser
837(`send()`).
838
839In questo giorno abbiamo visto come gestire i fogli di stile e i JavaScript
840sia nel file `view.yml` che nei template. Alla fine, entrambe le tecniche
841usano i metodi dell'oggetto risposta `addStylesheet()` e `addJavascript()`.
842
843>**TIP**
844>Le classi `sfAction`, `sfRequest` e `sfResponse` forniscono molti altri
845>metodi utili. Non esitate a consultare la
846[documentazione delle API](http://www.symfony-project.org/api/1_4/) per saperne
847>di più su tutte le classi interne di symfony.
848
849A domani
850--------
851
852Oggi abbiamo descritto alcuni design pattern usati da symfony. Speriamo che ora
853la struttura delle cartelle abbia più senso. Abbiamo giocato coi template,
854manipolando il layout e i file dei template. Li abbiamo anche resi un po' più
855dinamici, grazie agli slot e alle azioni.
856
857Domani impareremo di più sull'helper `url_for()` che abbiamo usato oggi,
858e sul sub-framework del routing associato con esso.
859
860__ORM__