Giorno 4: Il controllore e la vista
Ieri abbiamo analizzato come symfony possa semplificare la gestione del database astraendo le differenze tra i vari motori di database e convertendo gli elementi dello schema relazionale in classi orientate agli oggetti. Abbiamo inoltre giocato con ##ORM## per descrivere lo schema del database, creare le tabelle e popolare il database con alcuni dati iniziali.
Oggi andremo a personalizzare il modulo job
creato ieri. Il modulo job
contiene già tutto il codice di cui abbiamo bisogno per Jobeet:
- Una pagina per elencare tutte le offerte di lavoro
- Una pagina per creare una nuova offerta
- Una pagina per aggiornare un'offerta esistente
- Una pagina per cancellare un'offerta
Nonostante il codice sia pronto per essere usato com'è, rifattorizzeremo i template per attenerci il più possibile ai mockup di Jobeet.
L'architettura ~MVC~
Se siete abituati a sviluppare siti web con PHP senza utilizzare un framework, probabilmente utilizzate il paradigma del singolo file PHP per singola pagina HTML. Questi file PHP probabilmente contengono lo stesso tipo di struttura: inizializzazione e configurazioni globali, business logic relativa alla pagina richiesta, recupero dei record dal database e infine il codice HTML che costruisce la pagina.
Potreste usare un motore per i template per separare la logica dall'HTML. Forse usate un layer per l'astrazione del database per separare l'interazione tra il modello e la business logic. Purtroppo il più delle volte finite per avere una grande quantità di codice che è un vero e proprio incubo da mantenere. È stato veloce da realizzare, ma con il passare del tempo è sempre più difficile apportare cambiamenti, specialmente perché nessuno eccetto voi capisce come è fatto e come funziona.
Come per tutti i problemi esistono piacevoli soluzioni. Per lo sviluppo web la soluzione più diffusa di questi tempi per organizzare il codice è rappresentata dal pattern architetturale MVC. Brevemente il pattern MVC definisce un modo per organizzare il proprio codice secondo la sua natura. Questo pattern separa il codice in tre strati:
-
Il ~Modello~ è lo strato che definisce la business logic (il database appartiene a questo strato). Probabilmente siete al corrente del fatto che symfony memorizza tutte le classi e i file relativi al Modello nella cartella
lib/model/
. -
La ~Vista~ rappresenta ciò con cui l'utente interagisce (un motore di template è parte di questo strato). In symfony lo strato della Vista è principalmente costituito da template PHP. Queste sono memorizzate in varie cartelle
templates
come vedremo in seguito. -
Il ~Controllore~ è la parte di codice che chiama il Modello per ottenere alcuni dati da passare alla Vista per visualizzarli attraverso il client. Quando abbiamo installato symfony il primo giorno abbiamo visto che tutte le richieste sono gestite dai front controller (
index.php
efrontend_dev.php
). Questi front controller delegano il vero lavoro alle azioni. Come abbiamo visto ieri queste azioni sono raggruppate in moduli.
Oggi utilizzeremo il mockup definito il giorno 2 per personalizzare l'homepage e la pagina delle offerte di lavoro. Inoltre le renderemo dinamiche. Lungo la strada perfezioneremo molte cose in molti file differenti per mostrare la struttura delle cartelle di symfony e come separare il codice tra i vari strati.
Il Layout
Per prima cosa se avete guardato con attenzione i mockup avrete notato che gran parte di ogni pagina sembra sempre la stessa. Sapere già che la duplicazione del codice è una cattiva pratica se stiamo parlando di codice HTML o PHP, perciò abbiamo bisogno di trovare un modo per prevenire il fatto che elementi comuni implichino duplicazione del codice.
Un modo per risolvere questo problema è quello di definire un header e un footer includendoli in ogni template:
Ma qui i file di header e footer non contengono HTML valido. Deve esserci una strada migliore. Invece di reinventate la ruota, utilizzeremo un altro design pattern per risolvere questo problema: il design pattern ~decorator~. Il design pattern decorator risolve il problema agendo al contrario: il template viene decorato dopo che il contenuto è stato reso da un template globale, in symfony questo è definito come un ~layout~:
Il layout di default di un'applicazione è chiamato layout.php
e può essere trovato nella cartella apps/frontend/templates/
. Questa cartella contiene tutti i template globali di un'applicazione.
Rimpiazzate il layout di default di symfony con il seguente codice:
<!-- apps/frontend/templates/layout.php --> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Jobeet - Your best job board</title> <link rel="shortcut icon" href="/favicon.ico" /> <?php include_javascripts() ?> <?php include_stylesheets() ?> </head> <body> <div id="container"> <div id="header"> <div class="content"> <h1><a href="<?php echo url_for('job/index') ?>"> <img src="/images/logo.jpg" alt="Jobeet Job Board" /> </a></h1> <div id="sub_header"> <div class="post"> <h2>Ask for people</h2> <div> <a href="<?php echo url_for('job/index') ?>">Post a Job</a> </div> </div> <div class="search"> <h2>Ask for a job</h2> <form action="" method="get"> <input type="text" name="keywords" id="search_keywords" /> <input type="submit" value="search" /> <div class="help"> Enter some keywords (city, country, position, ...) </div> </form> </div> </div> </div> </div> <div id="content"> <?php if ($sf_user->hasFlash('notice')): ?> <div class="flash_notice"> <?php echo $sf_user->getFlash('notice') ?> </div> <?php endif; ?> <?php if ($sf_user->hasFlash('error')): ?> <div class="flash_notice"> <?php echo $sf_user->getFlash('error') ?> </div> <?php endif; ?> <div class="content"> <?php echo $sf_content ?> </div> </div> <div id="footer"> <div class="content"> <span class="symfony"> <img src="/images/jobeet-mini.png" /> powered by <a href="http://www.symfony-project.org/"> <img src="/images/symfony.gif" alt="symfony framework" /> </a> </span> <ul> <li><a href="">About Jobeet</a></li> <li class="feed"><a href="">Full feed</a></li> <li><a href="">Jobeet API</a></li> <li class="last"><a href="">Affiliates</a></li> </ul> </div> </div> </div> </body> </html>
Un template di symfony è solamente un semplice file PHP. Nel template del layout ci sono chiamate a funzioni PHP e riferimenti a variabili PHP. $sf_content
è la variabile più interessante: è definita dal framework stesso e contiene l'HTML generato dall'azione.
Se navigate il modulo job
(http://www.jobeet.com.localhost/frontend_dev.php/job
) potete vedere che tutte le azioni sono decorate dal layout.
I Fogli di stile, le Immagini e i Javascript
Dato che questo tutorial non riguarda il web design, abbiamo già preparato tutte le risorse necessarie per Jobbet: organizzeremo un concorso per il miglior design il giorno 21, abbiamo scaricate l'archivio delle immagini e mettetele nella cartella web/images
; scarica tel'archivio dei fogli di stile e metteteli nella cartella web/css/
.
Nel layout abbiamo incluso una favicon. Potete scaricare quella di Jobeet e metterla nella cartella
web/
.
Di default, il task
generate:project
ha creato tre cartelle per i file degli elementi grafici:web/images/
per le immagini,web/~css~/
per i ~fogli di stile~web/js/
per i ~Javascript~. Questa è una delle ~convenzioni~ definite da symfony, ma potete salvarli ovunque vogliate all'interno della cartellaweb/
.
Il lettore più attento avrà notato che anche se il file main.css
non è menzionato in nessun posto nel layout, è presente nell'HTML generato. Ma nessun altro file è presente. Com'è possibile?
Il foglio di stile è stato incluso dalla funzione ~include_stylesheets~()
chiamata nel tag <head>
all'interno del layout. La funzione include_stylesheets()
è chiamata helper. Un helper è una funzione definita da symfony, che accetta dei parametri e restituisce codice HTML. La maggior parte delle volte, gli helper fanno risparmiare del tempo e racchiudono degli spezzoni di codice usati di frequente nei template. L'helper include_stylesheets()
genera il tag <link>
per i fogli di stile.
Ma come fa l'helper a sapere quali fogli di stile includere?
Lo strato della ~Vista~ può essere configurato modificando il file di configurazione dell'applicazione ~view.yml
~. Questo è quello generato di default dal comando generate:app
:
---
# apps/frontend/config/view.yml
default:
http_metas:
content-type: text/html
metas:
#title: symfony project
#description: symfony project
#keywords: symfony, project
#language: en
#robots: index, follow
stylesheets: [main.css]
javascripts: []
has_layout: true
layout: layout
Il file view.yml
configura le impostazioni di default
per ogni template dell'applicazione. Per esempio, l'elemento stylesheets
definisce un array di fogli di stile da includere in ogni pagina dell'applicazione (l'inclusione è fatta dall'helper include_stylesheets()
).
Nel file
view.yml
di default, il file referenziato èmain.css
e non/css/main.css
. Comunque, le due definizioni sono equivalente in quanto symfony aggiunge il prefisso/~css~/
ai percorsi relativi.
Se molti file sono definiti, symfony li includerà nello stesso ordine della definizione:
---
stylesheets: [main.css, jobs.css, job.css]
Si può anche cambiare l'attributo media
e omettere il suffisso .css
:
Questo file di configurazione sarà tradotto in:
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/jobs.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/job.css" /> <link rel="stylesheet" type="text/css" media="print" href="/css/print.css" />
Il file di configurazione
view.yml
definisce anche il ~layout~ usato dall'applicazione. Di default, il nome èlayout
, così symfony decora ogni pagina con il filelayout.php
. Si può anche disabilitare il processo di decorazione una volta per tutte, impostando la proprietà ~has_layout
~ afalse
.
Funziona già così com'è, ma il file jobs.css
è necessario solo per l'homepage, e il file job.css
è necessario solo per la pagina del lavoro. Il file view.yml
può essere personalizzato in ogni modulo. Cambiamo la chiave stylesheets
del file view.yml
dell'applicazione in modo che contenga solo il file main.css
:
---
# apps/frontend/config/view.yml
stylesheets: [main.css]
Per personalizzare la vista del modulo job
, creiamo un file view.yml
all'interno della cartella apps/frontend/modules/job/config
:
---
# apps/frontend/modules/job/config/view.yml
indexSuccess:
stylesheets: [jobs.css]
showSuccess:
stylesheets: [job.css]
All'interno delle sezioni indexSuccess
e showSuccess
(sono i nomi dei file dei template associati alle azioni index
e show
, come vedremo in seguito), si può personalizzare ogni elemento all'interno della sezione default
del file view.yml
dell'applicazione. Tutti i nuovi elementi sono sostituiti a quelli definiti nella configurazione dell'applicazione. Si possono inoltre definire alcune configurazioni per tutte le azioni di un modulo con la sezione speciale all
.
Principi di configurazione di symfony
Per molti file di ~configurazione~ di symfony, la stessa impostazione può essere definita in livelli differenti:
- La configurazione di default è all'interno del framework
- La configurazione globale per il progetto (in
config/
)- La configurazione locale per un'applicazione (in
apps/APP/config/
)- La configurazione locale per un modulo (in
apps/APP/modules/MODULE/config/
)Nell'esecuzione, il sistema di configurazione unisce tutti i valori dei differenti file se esistono e crea un copia cache dei risultati per migliorare le performance.
Solitamente, quando qualcosa è configurabile tramite un file di configurazione, lo è anche tramite codice PHP. al posto di create un file view.yml
per il modulo job
per esempio, potete anche usare l'helper ~use_stylesheet~()
per includere un foglio di stile da un template:
<?php use_stylesheet('main.css') ?>
Si può anche usare questo helper nel layout per includere un foglio di stile globale.
Scegliere tra un metodo è l'altro è solo un questione di gusti. Il file view.yml
fornisce un modo per definire impostazioni per tutte le azioni di un modulo, che non è possibile in un template, ma la configurazione è statica. D'altro canto, usare l'~helper~ use_stylehseet()
è più flessibile e, soprattutto, è tutto nello stesso posto: la definizione dello stile e il codice HTMl. Per Jobeet useremo l'helper use_stylesheet()
, per cui potete eliminare il file view.yml
che abbiamo appena creato e modificare i template job
con le chiamate a use_stylesheet()
:
<!-- apps/frontend/modules/job/templates/indexSuccess.php --> <?php use_stylesheet('jobs.css') ?> <!-- apps/frontend/modules/job/templates/showSuccess.php --> <?php use_stylesheet('job.css') ?>
Simmetricamente, la configurazione di Javascript è eseguita dell'elemento
javascripts
del fileview.yml
e l'helper~use_javascript~()
definisce i file JavaScript da includere in un template.
L'Homepage di Jobeet
Come visto nel giorno 3, la pagina dei lavori è generata dall'azione index
del modulo job
. L'azione index
è la parte Controller della pagina e il template associato, indexSuccess.php
, è la View:
apps/
frontend/
modules/
job/
actions/
actions.class.php
templates/
indexSuccess.php
L'azione
Ogni ~azione~ è rappresentata da un metodo di una classe. Per l'homepage di Jobeet, la classe è jobActions
(il nome del modulo seguito dal suffisso Actions
) ed il metodo è executeIndex()
(execute
seguito dal nome dell'azione). L'azione recupera tutti i lavori dal database:
// apps/frontend/modules/job/actions/actions.class.php class jobActions extends sfActions { public function executeFooBar(sfWebRequest $request) {
$this->jobeet_jobs = JobeetJobPeer::doSelect(new Criteria()); $this->jobeet_jobs = Doctrine::getTable('JobeetJob') ->createQuery('a') ->execute(); }
// ...
}
Diamo uno sguardo da vicino al codice: il metodo executeIndex()
(il Controllore) chiama il Modello JobeetJobPeer
per recuperare tutti i lavori (new Criteria()
). Esso restituisce un array di oggetti JobeetJob
che sono assegnati alla proprietà jobeet_jobs
. Diamo uno sguardo da vicino al codice: il metodo executeIndex()
(il Controllore) chiama il Modello JobeetJob
per creare una query per recuperare tutti i lavori. Esso restituisce un Doctrine_Collection
di oggetti JobeetJob
che sono assegnati alla proprietà jobeet_jobs
. Ognuna di queste proprietà è automaticamente passata al template (la Vista). Per passare dati dal Controllore alla Vista, basta creare una nuova proprietà:
public function executeIndex(sfWebRequest $request) { $this->foo = 'bar'; $this->bar = array('bar', 'baz'); }
Questo codice renderà le variabili $foo
e $bar
accessibili dal template.
Il Template
Di default, il ~template~ associato a un'azione è dedotto da symfony grazie a una convenzione (il nome dell'azione seguito dal suffisso Success
).
Il template indexSuccess.php
genera una tabella HTML per tutti i lavori. Ecco il codice attuale del template:
<!-- apps/frontend/modules/job/templates/indexSuccess.php --> <?php use_stylesheet('jobs.css') ?> <h1>Job List</h1> <table> <thead> <tr> <th>Id</th> <th>Category</th> <th>Type</th> <!-- more columns here --> <th>Created at</th> <th>Updated at</th> </tr> </thead> <tbody> <?php foreach ($jobeet_jobs as $jobeet_job): ?> <tr> <td> <a href="<?php echo url_for('job/show?id='.$jobeet_job->getId()) ?>"> <?php echo $jobeet_job->getId() ?> </a> </td> <td><?php echo $jobeet_job->getCategoryId() ?></td> <td><?php echo $jobeet_job->getType() ?></td> <!-- more columns here --> <td><?php echo $jobeet_job->getCreatedAt() ?></td> <td><?php echo $jobeet_job->getUpdatedAt() ?></td> </tr> <?php endforeach; ?> </tbody> </table> <a href="<?php echo url_for('job/new') ?>">New</a>
Nel codice del template, il foreach
scorre attraverso la lista di oggetti Job
($jobeet_jobs
) e, per ognuno di loro, ogni valore delle colonne è visualizzato. Ricordate che accedere al valore di una colonna è semplice come chiamare un metodo getter, il cui nome inizia con get
ed è seguito dal nome della colonna in formato ~camelCase~ (per esempio il metodo getCreatedAt()
per la colonna created_at
).
Ripuliamolo un po', per visualizzare solo un sottoinsieme di colonne disponibili:
<!-- apps/frontend/modules/job/templates/indexSuccess.php --> <?php use_stylesheet('jobs.css') ?> <div id="jobs"> <table class="jobs"> <?php foreach ($jobeet_jobs as $i => $job): ?> <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>"> <td><?php echo $job->getLocation() ?></td> <td> <a href="<?php echo url_for('job/show?id='.$job->getId()) ?>"> <?php echo $job->getPosition() ?> </a> </td> <td><?php echo $job->getCompany() ?></td> </tr> <?php endforeach; ?> </table> </div>
La funzione url_for()
in questo template è un helper che verrà discusso domani.
Il template della pagina del lavoro
Personalizziamo ora il template della pagina del lavoro. Apriamo il file showSuccess.php
e sostituiamo il suo contenuto con il codice seguente:
<!-- apps/frontend/modules/job/templates/showSuccess.php --> <?php use_stylesheet('job.css') ?> <?php use_helper('Text') ?> <div id="job"> <h1><?php echo $job->getCompany() ?></h1> <h2><?php echo $job->getLocation() ?></h2> <h3> <?php echo $job->getPosition() ?> <small> - <?php echo $job->getType() ?></small> </h3> <?php if ($job->getLogo()): ?> <div class="logo"> <a href="<?php echo $job->getUrl() ?>"> <img src="/uploads/jobs/<?php echo $job->getLogo() ?>" alt="<?php echo $job->getCompany() ?> logo" /> </a> </div> <?php endif; ?> <div class="description"> <?php echo simple_format_text($job->getDescription()) ?> </div> <h4>How to apply?</h4> <p class="how_to_apply"><?php echo $job->getHowToApply() ?></p> <div class="meta">
posted on getCreatedAt('m/d/Y') ?> posted on getDateTimeObject('created_at')->format('m/d/Y') ?>
<div style="padding: 20px 0">
<a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">Edit</a>
</div>
</div>
Questo template usa la variabile $job
, passata dall'azione, per mostrare l'informazione sul lavoro. Poiché abbiamo rinominato la variabile passata al template da $jobeet_job
a $job
, dobbiamo riportare questo cambiamento nell'azione show
(attenzione, ci sono due occorrenze della variabile):
// apps/frontend/jobeet/actions/actions.class.php public function executeShow(sfWebRequest $request) {
$this->job = JobeetJobPeer::retrieveByPk($request->getParameter('id')); $this->job = Doctrine::getTable('JobeetJob')->find($request->getParameter('id')); $this->forward404Unless($this->job); }
Notate che alcuni metodi di Propel accettano dei parametri. Siccome abbiamo definito la colonna created_at
come un timestamp, il metodo getCreatedAt()
accetta uno schema di formattazione della data come primo parametro.
$job->getCreatedAt('m/d/Y');
Notate che le colonne di tipo date possono essere convertite a istanze dell'oggetto PHP DateTime. Così come abbiamo definito le colonne created_at
come timestamp, è possibile convertire il valore della colonna a un oggetto DateTime usando il metodo getDateTimeObject()
e dopo chiamando il metodo format()
che prende un modello di formattazione della data come suo primo parametro:
$job->getDateTimeObject('created_at')->format('m/d/Y');
La descrizione del lavoro usa l'helper
simple_format_text()
per formattarsi come HTML, sostituendo ad esempio gli "a capo" con un<br />
. Poiché tale helper appartiene al gruppo di helperText
, che non è caricato di default, l'abbiamo caricato manualmente usando l'helper~use_helper~()
.
Gli ~slot~
Ed ora, il titolo di tutte le pagine è definito nel tag <title>
del layout:
<title>Jobeet - Your best job board</title>
Ma per la pagina del lavoro vogliamo fornire delle informazioni più dettagliate, come il nome della compagnia e la posizione del lavoro.
In symfony, quando una zona del layout dipende dal template che deve essere visualizzato, occorre definire uno slot:
Aggiungiamo uno slot al layout per avere un titolo dinamico:
// apps/frontend/templates/layout.php <title><?php include_slot('title') ?></title>
Ogni slot è definito da un nome (title
) e può essere visualizzato usando l'helper ~include_slot~()
. Ora, all'inizio del template showSuccess.php
, usiamo l'helper slot()
per definire il contenuto dello slot per la pagina del lavoro:
// apps/frontend/modules/job/templates/showSuccess.php <?php slot('title', sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition())) ?>
Se il titolo è complesso da generare, l'helper slot()
può anche essere usato con un blocco di codice:
// apps/frontend/modules/job/templates/showSuccess.php <?php slot('title') ?> <?php echo sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()) ?> <?php end_slot(); ?>
Per alcune pagine, come la homepage, ci serve solo un titolo generico. Invece di ripetere lo stesso titolo più e più volte nei template, possiamo definire un titolo di default nel layout:
// apps/frontend/templates/layout.php <title> <?php include_slot('title', 'Jobeet - Your best job board') ?> </title>
Il secondo parametro del metodo include_slot()
è il valore predefinito per lo slot se non è stato definito. Se il valore predefinito è lungo o ha alcuni tag HTML, si può anche crearlo come mostrato nel seguente codice:
// apps/frontend/templates/layout.php <title> <?php if (!include_slot('title')): ?> Jobeet - Your best job board <?php endif; ?> </title>
L'helper include_slot()
restituisce true
se lo slot è stato definito. Quindi, se abbiamo definito uno slot title
in un template, verrà usato; altrimenti, verrà usato il titolo di default.
Abbiamo già visto alcuni helper che iniziano con
include_
. Questi helper visualizzano l'HTML e in molti casi hanno una controparteget_
, per restituire solamente il contenuto:<?php include_slot('title') ?> <?php echo get_slot('title') ?> <?php include_stylesheets() ?> <?php echo get_stylesheets() ?>
L'azione della pagina del lavoro
La pagina del lavoro è generata dall'azione show
, definita nel metodo executeShow()
del modulo job
:
class jobActions extends sfActions { public function executeShow(sfWebRequest $request) {
$this->job = JobeetJobPeer::retrieveByPk($request->getParameter('id')); $this->job = Doctrine::getTable('JobeetJob')->find($request->getParameter('id')); $this->forward404Unless($this->job); }
// ...
}
Come nell'azione index
, la classe JobeetJobPeer
è usata per recuperare un lavoro, stavolta usando il metodo retrieveByPk()
. Il parametro di questo metodo è l'identificatore univoco di un lavoro, la sua ~chiave primaria~. La prossima sezione spiegherà perché l'istruzione $request->getParameter('id')
restituisce la chiave primaria del lavoro. Come nell'azione index
, la classe JobeetJob
è usata per recuperare un lavoro, stavolta usando il metodo find()
. Il parametro di questo metodo è l'identificatore univoco di un lavoro, la sua ~chiave primaria~. La prossima sezione spiegherà perché l'istruzione $request->getParameter('id')
restituisce la chiave primaria del lavoro.
Le classi del modello generate contengono molti metodi utili per interagire con gli oggetti del progetto. Prendetevi un po' di tempo per analizzare il codice che si trova nella cartella
lib/om/
e per scoprire la potenza nascosta in queste classi.
Se il lavoro non esiste nel database, vogliamo rimandare l'utente a una pagina ~404~, che è esattamente ciò che fa il metodo forward404Unless()
. Questo accetta un booleano come primo parametro e, a meno che non sia vero, ferma il flusso corrente dell'esecuzione. Poiché i metodi "forward" fermano l'esecuzione dell'azione sollevando un'eccezione sfError404Exception
, non si ha bisogno di usare return
successivamente.
Come per le eccezioni, la pagina mostrata all'utente è diversa negli ~ambienti~ prod
e dev
:
Prima di pubblicare il sito Jobeet su un server di produzione, impareremo a personalizzare la pagina 404 di default.
La famiglia dei metodi "~forward~"
Una chiamata a
forward404Unless
in realtà equivale a:$this->forward404If(!$this->job);che equivale anche a:
if (!$this->job) { $this->forward404(); }Il metodo stesso
forward404()
è solo una scorciatoia per:$this->forward('default', '404');Il metodo
forward()
rimanda a un'altra azione della stessa applicazione; negli esempi precedenti, all'azione404
del modulodefault
. Il modulodefault
è distribuito con symfony e fornisce delle azioni di default per le pagine 404, secure e login.
La richiesta e la risposta
Quando si visitano le pagine /job
o /job/show/id/1
nel proprio browser, si dà inizio a un viaggio nel server web. Il browser invia una ~richiesta~ e il server rimanda indietro una ~risposta~.
Abbiamo già visto che symfony incapsula la richiesta in un oggetto sfWebRequest
(si veda il metodo executeShow()
). E siccome symfony è un framework orientato agli oggetti, anche la risposta è un oggetto, della classe sfWebResponse
. Si può accedere all'oggetto risposta in un'azione richiamando $this->getResponse()
.
Questi oggetti forniscono molti metodi utili per accedere alle informazioni dalle funzioni e dalle variabili globali di PHP.
Perché symfony ha un wrap di funzionalità esistenti in PHP? Innanzitutto, perché i metodi di symfony sono più potenti delle controparti PHP. Poi, perché quando si testa un'applicazione, è più facile simulare un oggetto richiesta o risposta piuttosto che trattare variabili globali o usare funzioni come
header()
, che fanno troppe cose di nascosto.
La richiesta
La classe sfWebRequest
è un wrapper per le array globali di PHP ~$_SERVER
~, ~$_COOKIE
~, ~$_GET
~, ~$_POST
~ e ~$_FILES
~
Nome del metodo | Equivalente PHP |
---|---|
getMethod() |
$_SERVER['REQUEST_METHOD'] |
getUri() |
$_SERVER['REQUEST_URI'] |
getReferer() |
$_SERVER['HTTP_REFERER'] |
getHost() |
$_SERVER['HTTP_HOST'] |
getLanguages() |
$_SERVER['HTTP_ACCEPT_LANGUAGE'] |
getCharsets() |
$_SERVER['HTTP_ACCEPT_CHARSET'] |
isXmlHttpRequest() |
$_SERVER['X_REQUESTED_WITH'] == 'XMLHttpRequest' |
getHttpHeader() |
$_SERVER |
getCookie() |
$_COOKIE |
isSecure() |
$_SERVER['HTTPS'] |
getFiles() |
$_FILES |
getGetParameter() |
$_GET |
getPostParameter() |
$_POST |
getUrlParameter() |
$_SERVER['PATH_INFO'] |
getRemoteAddress() |
$_SERVER['REMOTE_ADDR'] |
Abbiamo già avuto accesso ai parametri della richiesta usando il metodo getParameter()
. Esso restituisce un valore dalla variabile globale $_GET
o $_POST
, oppure dalla variabile ~PATH_INFO
~.
Se si vuole essere certi che un parametro della richiesta venga da una particolare di queste variabili, si devono usare rispettivamente i metodi getGetParameter()
, getPostParameter()
e getUrlParameter()
.
Se si vuole limitare un'azione per un ~metodo HTTP~ specifico, ad esempio se si vuole essere sicuri che una form sia inviata come
POST
, si può usare il metodoisMethod()
:$this->forwardUnless($request->isMethod('POST'));
.
La risposta
La classe sfWebResponse
è un wrapper per le funzioni PHP ~header~()
e setraw~cookie~()
:
Nome del metodo | Equivalente PHP |
---|---|
setCookie() |
setrawcookie() |
setStatusCode() |
header() |
setHttpHeader() |
header() |
setContentType() |
header() |
addVaryHttpHeader() |
header() |
addCacheControlHttpHeader() |
header() |
Ovviamente, la classe sfWebResponse
fornisce anche un modo per impostare il contenuto della risposta (setContent()
) e inviare la risposta al browser (send()
).
In questo giorno abbiamo visto come gestire i fogli di stile e i JavaScript sia nel file view.yml
che nei template. Alla fine, entrambe le tecniche usano i metodi dell'oggetto risposta addStylesheet()
e addJavascript()
.
Le classi
sfAction
,sfRequest
esfResponse
forniscono molti altri metodi utili. Non esitate a consultare la documentazione delle API per saperne di più su tutte le classi interne di symfony.
A domani
Oggi abbiamo descritto alcuni design pattern usati da symfony. Speriamo che ora la struttura delle cartelle abbia più senso. Abbiamo giocato coi template, manipolando il layout e i file dei template. Li abbiamo anche resi un po' più dinamici, grazie agli slot e alle azioni.
Domani impareremo di più sull'helper url_for()
che abbiamo usato oggi, e sul sub-framework del routing associato con esso.
ORM
インデックス
Document Index
関連ページリスト
Related Pages
- Giorno 1: Impostare il progetto
- Giorno 2: Il progetto
- Giorno 3: Il ~Modello dei dati~
- Giorno 4: Il controllore e la vista
- Giorno 5: Il routing
- Giorno 6: Di più sul Modello
- Giorno 7: Giocare con la pagina delle categorie
- Giorno 8: I test unitari
- Giorno 9: I test funzionali
- Giorno 10: Form
- Giorno 11: Testare i Form
- Giorno 12: Admin Generator
- Giorno 13: L'utente
- Giorno 14: Feed
- Giorno 15: Web Service
- Giorno 16: Inviare ~email|Email~
- Giorno 17: Ricerca
- Giorno 18: ~AJAX~
- Giorno 19: Internazionalizzazione e Localizzazione
- Giorno 20: I plugin
- Giorno 21: La Cache
- Giorno 22: Il rilascio
- Giorno 23: Un altro sguardo a symfony
- Appendice B - Licenza
- Riconoscimenti
日本語ドキュメント
Japanese Documents
- 2011/01/18 Chapter 17 - Extending Symfony
- 2011/01/18 The generator.yml Configuration File
- 2011/01/18 Les tâches
- 2011/01/18 Emails
- 2010/11/26 blogチュートリアル(8) ビューの作成