Development

Documentation/it_IT/book/1.1/06-Inside-the-Controller-Layer

You must first sign up to be able to contribute.

Version 4 (modified by giosan, 9 years ago)
correzione errore ortografico

Capitolo 6 - All'interno del Layer Controller

In symfony il controller, che contiene il codice che collega la business logic alla presentazione, è diviso in alcune componenti che puoi usare per finalità diverse:

  • Il front controller è l'unico punto di accesso all'applicazione. Carica la configurazione e determina l'azione da eseguire.
  • Le azioni contengono la logica applicativa. Controllano l'integrità della richiesta e preparano i dati di cui ha bisogno il layer di presentazione.
  • La richiesta, la risposta e gli oggetti di sessione forniscono accesso ai parametri di richiesta, agli header di risposta ed ai dati utente persistenti. Essi sono usati molto di frequente a livello controller.
  • I filtri sono porzioni di codice eseguiti per ogni richiesta, prima o dopo l'azione. Ad esempio, i filtri di sicurezza e validazione sono usati molto spesso in applicazioni web. Puoi estendere il framework creando i tuoi filtri.

Questo capitolo descrive queste componenti, ma non ti fare intimidire dal loro numero. Per una pagina di base non avrai bisogno d'altro se non scrivere qualche linea in un'azione. Gli altri componenti del controller verranno utilizzati in situazioni specifiche.

Il Front Controller

Tutte le richieste sono gestite da un unico front controller, che è anche l'unico punto di accesso all'intera applicazione in un dato ambiente.

Quando il front controller riceve una richiesta, usa il sistema di routing per trovare la corrispondenza della URL che l'utente ha inserito (o clikkato) con una coppia azione/modulo. Ad esempio, l'URL seguente richiama lo script index.php (che è il front controller) e sara' recepita come chiamata per l'azione myAction del modulo mymodule:

http://localhost/index.php/mymodule/myAction

Se non sei interessato alle fondamenta di symfony, questo è tutto ciò che hai bisogno di sapere a proposito del front controller. è una componente fondamentale dell'implementazione MVC in symfony, ma raramente avrai bisogno di modificarlo. Puoi saltare direttamente alla prossima sezione se non sei veramente interessato ai punti di forza del front controller.

Il lavoro del front controller in dettaglio

Il fatto che il front controller gestisca le richieste ha un significato un pò più profondo di capire solo quale azione va eseguita. Infatti, esso esegue il codice comune a tutte le azioni, incluso:

  1. Carica la classe di configurazione del progetto e le librerie di symfony.
  2. Crea una configurazione di applicazione ed un contesto symfony.
  3. Carica e instanzia le classi del core del framework.
  4. Carica la configurazione.
  5. Decodifica la URL di richiesta ed i parametri relativi per capire quale azione eseguire.
  6. Se l'azione non esiste, rediriziona all'azione di errore 404.
  7. Attiva i filtri (ad esempio se la richiesta necessita autenticazione).
  8. Esegue i filtri, primo passaggio.
  9. Esegue l'azione e renderizza la vista.
  10. Esegue i filtri, secondo passaggio.
  11. Output della risposta.

Il front controller di default

Il front controller di default, chiamato index.php e situato nella cartella web/ del progetto, è un semplice file PHP come mostrato nel Listato 6-1.

Listato 6-1 - Front controller di default (ambiente di produzione)

[php]
<?php
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');

$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
sfContext::createInstance($configuration)->dispatch();

Il front controller include la configurazione dell'applicazione, che si occupa dei punti da 2 a 4. La chiamata al metodo dispatch() dell'oggetto sfController (che è il controller di core dell'architettura MVC di symfony) distribuisce le richieste, occupandosi dei punti da 5 a 7. Gli ultimi step sono gestiti dalla catena dei filtri, come descritto più avanti in questo capitolo.

Chiamare un altro front controller per cambiare ambiente

Esiste un front controller per ambiente. In effetti è proprio l'esistenza di un front controller che definisce un ambiente. L'ambiente è definito nel secondo argomento che viene passato al metodo ProjectConfiguration::getApplicationConfiguration().

Per poter cambiare l'ambiente nel quale stai navigando l'applicazione, scegli semplicemente un altro front controller. I front controller di default quando crei una nuova applicazione con il comando symfony generate:app si chiamano index.php per l'ambiente di produzione e frontend_dev.php per l'ambiente di test (quando la tua applicazione ovviamente si chiama frontend. La configurazione di default del mod_rewrite utilizzerà index.php quando non troverà un riferimento ad un front controller nella URL. Così, entrambe le URL seguenti mostreranno la stessa pagina (mymodule/index) nell'ambiente di produzione:

http://localhost/index.php/mymodule/index
http://localhost/mymodule/index

e l'URL seguente mostra la stessa pagina nell'ambiente di sviluppo:

http://localhost/frontend_dev.php/mymodule/index

Creare un nuovo ambiente è facile quanto creare un nuovo front controller. Ad esempio, potresti aver bisogno di un ambiente di test dove il tuo cliente può provare l'applicazione prima che vada in produzione. Per creare tale ambiente, copia semplicemente web/frontend_dev.php in web/frontend_staging.php, e cambia il valore del secondo argomento di ProjectConfiguration::getApplicationConfiguration() in staging. Ora, in tutti i file di configurazione, puoi aggiungere una nuova sezione {{{staging:}}} che contiene valori specifici per questo ambiente, come mostrato nel Listato 6-2.

Listato 6-2 - Esempio di app.yml con valori specifici per l'ambiente staging

staging:
  mail:
    webmaster:    dummy@mysite.com
    contact:      dummy@mysite.com
all:
  mail:
    webmaster:    webmaster@mysite.com
    contact:      contact@mysite.com

Se vuoi vedere come reagisce l'applicazione in questo ambiente, chiama il front controller relativo:

http://localhost/frontend_staging.php/mymodule/index

Actions

Le azioni sono il cuore di un'applicazione, perchè esse ne contengono tutta la logica. Usano il modello e definiscono variabili per le viste. Quando esegui una richiesta in un'applicazione symfony, la URL definisce l'azione ed i parametri.

La classe Action

Le azioni sono metodi chiamati executeActionName della classe moduleNameActions che eredita da sfActions, e raggruppate per moduli. Le azioni di una classe sono memorizzate in un file chiamato actions.class.php dentro la cartella actions/ del modulo.

Il Listato 6-3 mostra un esempio di actions.class.php con la sola azione index per l'intero modulo mymodule.

Listato 6-3 - Esempio di azione, in apps/frontend/modules/mymodule/actions/actions.class.php

[php]
class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    // ...
  }
}

CAUTION Anche se i nomi dei metodi non sono case-sensitive in PHP, lo sono in symfony. Per cui non dimenticare che il nome di un'azione deve cominciare con execute minuscolo, seguito dal nome esatto dell'azione con la prima lettera maiuscola.

Per poter richiedere un'azione, devi chiamare il front controller con il nome del modulo e dell'azione come parametri. Per default, questo si fa aggiungendo la coppia module_name/action_name allo script. Ciò significa che l'azione definita nel Listato 6-4 si può chiamare tramite:

http://localhost/index.php/mymodule/index

Aggiungere più azioni significa semplicemente aggiungere più metodi execute all'oggetto sfActions, come mostrato nel Listato 6-4.

Listato 6-4 - Classe con due azioni, in frontend/modules/mymodule/actions/actions.class.php

[php]
class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    // ...
  }

  public function executeList()
  {
    // ...
  }
}

Se la dimensione dell'azione cresce troppo, probabilmente avrai bisogno di fare un pò d'ordine nel codice (refactoring), spostandone qualche parte nel modello. Le azioni dovrebbero essere corte (non più di qualche riga), e la business logic di solito dovrebbe essere contenuta nel modello.

Inoltre, il numero di azioni in un modello potrebbe influenzare la decisione di dividere il modello stesso in due.

SIDEBAR Standard di codifica di symfony

Avrai notato negli esempi di questo libro che le parentesi graffe di apertura e chiusura ({ e }) occupano ognuna sempre una riga. Questa convenzione rende il codice più facile da leggere.

Fra gli altri standard di codifica del framework, l'indentazione è sempre costituita da due spazi bianchi, non vengono utilizzate tabulazioni. Questo è dovuto al fatto che le tabulazioni hanno valori diversi in termini di spazi bianchi a seconda dell'editor utilizzato, inoltre codice composto sia da spazi bianchi che da tabulazioni risulta impossibile da leggere.

Il core e i file generati da symfony non finiscono con il classico tag di chiusura ?>. Questo perché ciò non è realmente necessario, inoltre possono insorgere problemi nell'ouput se ad esempio ci fossero spazi bianchi dopo questo tag.

E se presti bene attenzione, noterai che nessuna linea finisce con uno spazio bianco in symfony. Questo per una ragione più banale questa volta: linee che finiscono con spazi bianchi si vedono molto male nell'editor di Fabien.

Sintassi alternativa per le classi Action

Si può utilizzare anche una sintassi alternativa per poter dividere le azioni in file separati, un'azione per ogni file. In questo caso, ogni azione estende sfAction (invece di sfActions) e prende il nome actionNameAction. Il nome effettivo del metodo è semplicemente execute. Il nome del file è lo stesso di quello della classe. Ciò significa che il Listato 6-4 può essere scritto come nei Listati 6-5 e 6-6.

Listato 6-5 - File con azione singola, in frontend/modules/mymodule/actions/indexAction.class.php

[php]
class indexAction extends sfAction
{
  public function execute()
  {
    // ...
  }
}

Listato 6-6 - File con azione singola, in frontend/modules/mymodule/actions/listAction.class.php

[php]
class listAction extends sfAction
{
  public function execute($request)
  {
    // ...
  }
}

Recuperare informazioni dalla Action

La classe action offre un modo di accedere ad informazioni relative al controller ed al core di symfony. Il Listato 6-8 mostra come usarle.

Listato 6-7 - Metodi comuni di sfActions

[php]
class mymoduleActions extends sfActions
{
  public function executeIndex($request)
  {
    // Recupera i parametri request
    $password    = $request->getParameter('password');

    // Recupera le informazioni per il controller
    $moduleName  = $this->getModuleName();
    $actionName  = $this->getActionName();

    // Recupera gli oggetti chiave del framework
    $userSession = $this->getUser();
    $response    = $this->getResponse();
    $controller  = $this->getController();
    $context     = $this->getContext();

    // Imposta le variabili dell'azione per passare informazioni al template
    $this->setVar('foo', 'bar');
    $this->foo = 'bar';            // Versione breve

  }
}

SIDEBAR Il contesto singleton

Hai già visto nel front controller, una chiamata a sfContext::getInstance(). In una azione, il metodo getContext() restituisce lo stesso singleton. è un oggetto molto utile che contiene un riferimento a tutti gli oggetti di core di symfony relativi ad una data richiesta ed offre un modo per accedervi:

sfController: il controller (->getController()) sfRequest: la richiesta (->getRequest()) sfResponse: la risposta (->getResponse()) sfUser: la sessione utente (->getUser()) sfDatabaseConnection: la connessione al database (->getDatabaseConnection()) sfLogger: l'oggetto logger (->getLogger()) sfI18N: l'oggetto per l'internazionalizzazione (->getI18N())

Puoi richiamare il singleton sfContext::getInstance() da qualsiasi punto del codice.

Termine di una Action

Sono possibili diversi comportamenti alla conclusione dell'esecuzione di un'azione. Il valore di ritorno dal metodo dell'azione determina come la vista deve essere renderizzata. Le costanti della classe sfView sono utilizzate per specificare quale template deve essere utilizzato per visualizzare il risultato dell'azione.

Se c'è una vista di default (come nella maggior parte dei casi), l'azione dovrebbe terminare nel modo seguente:

[php]
return sfView::SUCCESS;

In tale maniera symfony cercherà un file chiamato actionNameSuccess.php. Questo è anche il default, per cui se ometti il return nell'azione symfony cercherà di nuovo il template actionNameSuccess.php. Anche azioni vuote provocano questo comportamento. Il Listato 6-8 mostra esempi di azioni che terminano con successo.

Listato 6-8 - Azioni che richiamano i template indexSuccess.php e listSuccess.php

[php]
public function executeIndex()
{
  return sfView::SUCCESS;
}

public function executeList()
{
}

Se ci fosse un errore da richiamare, l'azione dovrebbe terminare così:

[php]
return sfView::ERROR;

Symfony cercherà un template chiamato actionNameError.php.

Per una vista personalizzata, utilizza questa terminazione:

[php]
return 'MyResult';

Symfony cercherà un template chiamato actionNameMyResult.php.

Se non c'è una vista da chiamare, come ad esempio un'azione chiamata in un file batch, l'azione dovrebbe terminare con:

[php]
return sfView::NONE;

In questo caso non sarà eseguito alcun template. Ciò significa che puoi aggirare totalmente il layer vista, mostrando in output codice direttamente dall'azione. Come mostrato nel Listato 6-9, symfony fornisce un metodo specifico renderText() per questa eventualita'. Può tornare utile quando hai bisogno di molta velocità in un'azione, come ad esempio con interazioni Ajax, come discusso nel Capitolo 11.

Listato 6-9 - Aggira la vista e stampa il risultato con sfView::NONE

[php]
public function executeIndex()
{
  echo '<html><body>Ciao, mondo!</body></html>';

  return sfView::NONE;
}

// equivalente a
public function executeIndex()
{
  return $this->renderText('<html><body>Ciao, mondo!</body></html>');
}

In qualche caso potresti aver bisogno di spedire una risposta vuota ma contenente qualche header (specialmente nel caso di header X-JSON). In tal caso definisci gli header tramite l'oggetto sfResponse, discusso nel prossimo capitolo, e ritorna la costante sfView::HEADER_ONLY, come mostrato nel Listato 6-10.

Listato 6-10 - Evitare il rendering della vista e spedire solo header

[php]
public function executeRefresh()
{
  $output = '<"title","My basic letter"],["name","Mr Brown">';
  $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')');

  return sfView::HEADER_ONLY;
}

Nel caso in cui l'azione dovesse essere renderizzata da un template specifico, puoi ignorare il metodo return ed utilizzare invece il metodo setTemplate():

[php]
$this->setTemplate('myCustomTemplate');

Passare ad un'altra Action

In qualche caso l'esecuzione di un'azione finisce richiedendo l'esecuzione di un'altra azione. Ad esempio, un'azione che gestisce i parametri di una richiesta in POST da una form, di solito redireziona ad un'altra azione dopo aver aggiornato il database. Un altro esempio è l'alias di un'azione: index è spesso un modo di visualizzare una lista, ed in effetti redireziona ad un'azione list.

La classe action fornisce due metodi per eseguirne un'altra:

  • Se l'azione redireziona ad un'altra azione:

    [php]
    $this->forward('otherModule', 'index');
    
  • Se l'azione come risultato deve fare una redirezione web:

    [php]
    $this->redirect('otherModule/index');
    $this->redirect('http://www.google.com/');
    

NOTE Il codice seguente ad una redirezione o ad un forward non viene mai eseguito. Puoi considerare queste chiamate equivalenti ad un return. Esse generano una chiamata sfStopException per fermare l'esecuzione dell'azione; tale eccezione è catturata in seguito da symfony e semplicemente ignorata.

La scelta tra un forward ed un redirect qualche volta è delicata. Per scegliere la soluzione migliore, tieni a mente che il forward è interno all'applicazione e trasparente all'utente. Per quel che riguarda l'utente, l'URL visualizzata è la stessa della richiesta. Per contro, un redirect è un messaggio al browser dell'utente, che comprende una richiesta da quest'ultimo ed un cambiamento nell'URL che ne risulta alla fine.

Se l'azione è chiamata dal submit di una form con method="post", dovresti sempre fare un redirect. Il vantaggio sta nel fatto che se l'utente aggiorna la pagina risultante, la richiesta non è inoltrata di nuovo; inoltre, il pulsante "indietro" del browser si comporterà come l'utente si aspetta, ovvero mostrando la form e non chiedendo all'utente se vuole spedire di nuovo i dati in POST.

C'è un tipo speciale di forward usato molto di frequente. Il metodo forward404() redireziona ad un'azione "pagina non trovata". Questo metodo è chiamato ogni qualvolta un parametro necessario all'azione non è presente nella richiesta (in questo modo viene rilevata una URL sbagliata). Il Listato 6-11 mostra un'esempio di azione show che si aspetta un parametro id.

Listato 6-11 - Utilizzo del metodo forward404()

[php]
public function executeShow($request)
{
  $article = ArticlePeer::retrieveByPK($request->getParameter('id'));
  if (!$article)
  {
    $this->forward404();
  }
}

TIP Se stai cercando l'azione e il template dell'errore 404, le troverai nella cartella $sf_symfony_lib_dir/controller/default/. Puoi personalizzare questa pagina aggiungendo un nuovo modulo default nella tua applicazione, sovrascrivendo così quello del framework, e definendo una nuova azione error404 ed un nuovo template error404Success. Alternativamente, puoi impostare le costanti error_404_module e error_404_action del file settings.yml per utilizzare un'azione esistente.

L'esperienza mostra che il più delle volte un'azione esegue un redirect od un forward dopo aver testato qualcosa, come nel Listato 6-11. Questo è il motivo per cui la classe sfActions ha qualche metodo in più, come forwardIf(), forwardUnless(), forward404If(), forward404Unless(), redirectIf(), e redirectUnless(). Tali metodi accettano semplicemente un parametro che rappresenta una condizione, che porta all'esecuzione se vera (per i metodi xxxIf()) o falsa (per i metodi xxxUnless()), come mostrato nel Listato 6-12.

Listato 6-12 - Utilizzo del metodo forward404If()

[php]
// This action is equivalent to the one shown in Listing 6-12
public function executeShow($request)
{
  $article = ArticlePeer::retrieveByPK($request->getParameter('id'));
  $this->forward404If(!$article);
}

// So is this one
public function executeShow($request)
{
  $article = ArticlePeer::retrieveByPK($request->getParameter('id'));
  $this->forward404Unless($article);
}

Utilizzare questi metodi non solo manterrà il tuo codice più corto, ma anche più leggibile.

TIP Quando l'azione chiama il metodo forward404() od uno dei suoi "compagni", symfony genera una sfError404Exception che gestisce la risposta 404. Questo significa che se tu avessi bisogno di visualizzare un messaggio 404 da una parte dalla quale non vuoi accedere al controller, puoi generare semplicemente una eccezione simile.

Ripetere codice per diverse Action di un modulo

La convenzione di chiamare le azioni executeActionName() (nel caso della classe sfActions, oppure execute() (nel caso della classe sfAction) garantisce il fatto che symfony trovi il metodo dell'azione. Ti da' la possibilita' di aggiungere altri metodi che non saranno considerati come azioni, se il loro nome non comincia con execute.

C'è un'altra convenzione molto utile nel caso in cui hai bisogno di ripetere diversi statement in ogni azione prima della sua effettiva esecuzione. Puoi inserirli nel metodo preExecute() della tua azione. Puoi indovinare da solo come fare la stessa cosa ma dopo che l'esecuzione è avvenuta: inseriscili nel metodo postExecute(). La sintassi di questi metodi è mostrata nel Listato 6-13.

Listato 6-13 - Utilizzare preExecute(), postExecute() e metodi personalizzati in un'azione

[php]
class mymoduleActions extends sfActions
{
  public function preExecute()
  {
    // Il codice inserito qui viene eseguito all'inizio della chiamata di ogni azione
    // ...
  }

  public function executeIndex($request)
  {
    // ...
  }

  public function executeList($request)
  {
    // ...
    $this->myCustomMethod();  // I metodo della classe action sono accessibili
  }

  public function postExecute()
  {
    // Il codice inserito qui viene eseguito alla fine della chiamata di ogni azione
    // ...
  }

  protected function myCustomMethod()
  {
    // Puoi anche aggiungere i tuoi metodo, purché non inizino con "execute"
    // In questo caso, meglio dichiararli come protected oppure private
    ...
  }
}

Accedere alla richiesta (Request)

Il primo argomento del meteodo di una azione è l'oggetto request, chiamato sfWebRequest in symfony. Hai già familiarità con il metodo getParameter('myparam'), utilizzato per ricevere un parametro di richiesta tramite il suo nome. La Tabella 6-1 mostra i metodi sfWebRequest più utili.

Tabella 6-1 - Metodi dell'oggetto sfWebRequest

Nome | Funzione | Output d'esempio -------------------------------- | -------------------------------------- | ----------------------------------------------------------------------- Request Information | | isMethod($method) | È un post o un get? | Restituisce true oppure false getMethod() | Metodo della richiesta | Restituisce le constanti di sfRequest::GET o sfRequest::POST getMethodName() | Nome del metodo della richiesta | 'POST' getHttpHeader('Server') | Valore di un dato header HTTP | 'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6' getCookie('foo') | Valore di un dato cookie | 'bar' isXmlHttpRequest()* | è una richiesta Ajax? | true isSecure() | è una richiesta SSL? | true Request Parameters | | hasParameter('foo') | Parametro presente nella richiesta? | true getParameter('foo') | Valore di un dato parametro | 'bar' getParameterHolder()->getAll() | Array di tutti i parametri richiesta | URI-Related Information | | getUri() | URI completo | 'http://localhost/frontend_dev.php/mymodule/myaction' getPathInfo() | Path info | '/mymodule/myaction' getReferer() | Referrer | 'http://localhost/frontend_dev.php/' getHost() | Host name | 'localhost' getScriptName() | Path e nome del Front controller | 'frontend_dev.php' Client Browser Information | | getLanguages() | Array delle lingue accettate | Array( [0] => fr [1] => fr_FR [2] => en_US [3] => en ) getCharsets() | Array dei charset accettati | Array( [0] => ISO-8859-1 [1] => UTF-8 [2] => * ) getAcceptableContentTypes() | Array dei content types accettati | Array( [0] => text/xml [1] => text/html

  • Funziona solo con Prototype

Alcune volte bloccato dai proxy

Per richieste multipart alle quali gli utenti hanno allegato dei file, l'oggetto sfWebRequest fornisce dei mezzi per accedere e spostare tali file, come mostrato nel Listato 6-14. Questi metodi sono deprecati in symfony 1.1 (vedi il capitolo 10 per maggiori informazioni).

Listato 6-14 - L'oggetto sfWebRequest sa come gestire i files allegati

[php]
class mymoduleActions extends sfActions
{
  public function executeUpload($request)
  {
    if ($request->hasFiles())
    {
      foreach ($request->getFileNames() as $fileName)
      {
        $fileSize  = $request->getFileSize($fileName);
        $fileType  = $request->getFileType($fileName);
        $fileError = $request->hasFileError($fileName);
        $uploadDir = sfConfig::get('sf_upload_dir');
        $request->moveFile('file', $uploadDir.'/'.$fileName);
      }
    }
  }
}

Non ti devi preoccupare di sapere se il tuo server supporta le variabili $_SERVER o $_ENV, o dei valori di default o di problemi di compatibilità; i metodi sfWebRequest si occupano di tutto al posto tuo. Inoltre i loro nomi sono talmente evidenti da non rendere necessario andare a controllare la documentazione di PHP per capire come gestire la richiesta.

Sessione utente

Symfony gestisce automaticamente le sessioni utente, ed è capace di mantenere dati persistenti fra le richieste dell'utente. Esso utilizza i meccanismi di gestione delle sessioni di PHP, e li migliora rendendoli più configurabili e più facili da usare.

Accedere alla sessione utente

La sessione dell'utente corrente è disponibile nelle azioni tramite il metodo getUser(), che è un'istanza della classe sfUser. Tale classe possiede una gestione di parametri che ti permette di memorizzare attributi dell'utente. Questi dati saranno disponibili anche alle altre richieste, fino alla scadenza della sessione utente, come mostrato nel Listato 6-15. Gli attributi utente possono contenere qualsiasi tipo di dati (stringhe, array, array associativi). Possono essere impostati per ogni singolo utente, anche se l'utente stesso non è identificato.

Listato 6-15 - L'oggetto sfUser può contenere attributi dell'utente

[php]
class mymoduleActions extends sfActions
{
  public function executeFirstPage($request)
  {
    $nickname = $request->getParameter('nickname');

    // Store data in the user session
    $this->getUser()->setAttribute('nickname', $nickname);
  }

  public function executeSecondPage()
  {
    // Retrieve data from the user session with a default value
    $nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward');
  }
}

CAUTION Potresti memorizzare oggetti nelle sessioni, ma è fortemente sconsigliato. Questo perché la sessione viene serializzata durante le richieste. Quando la sessione viene de-serializzata, la classe che contiene l'oggetto deve essere già caricata, e questo non succede sempre. Inoltre, se memorizzi oggetti Propel, essi potrebbero andare in "stallo".

Come diversi altri getter in symfony, il metodo getAttribute() supporta un secondo parametro come valore di default, da utilizzare quando l'attributo non è presente. Per controllare se un attrbuto è stato definito per un utente, utilizza il metodo hasAttribute(). Gli attributi sono memorizzati in un gestore di parametri che può essere acceduto tramite il metodo getAttributeHolder(). Permette una facile pulizia degli attributi con i soliti metodi per la gestione di parametri, come mostrato nel Listato 6-16.

Listato 6-16 - Eliminare dati dalla sessione utente

[php]
class mymoduleActions extends sfActions
{
  public function executeRemoveNickname()
  {
    $this->getUser()->getAttributeHolder()->remove('nickname');
  }

  public function executeCleanup()
  {
    $this->getUser()->getAttributeHolder()->clear();
  }
}

Gli attributi della sessione utente sono anche disponibili nei template tramite la variabile $sf_user, che contiene l'oggetto $sfUser corrente, come mostrato nel Listato 6-17.

Listato 6-17 - Anche i template hanno accesso agli attributi utente in sessione

[php]
<p>
  Hello, <?php echo $sf_user->getAttribute('nickname') ?>
</p>

NOTE Se hai bisogno di memorizzare informazioni solo per la durata della richiesta corrente (ad esempio, per passare informazioni attraverso una catena di chiamate ad azioni), potresti preferire la classe sfRequest la quale possiede anch'essa i metodi getAttribute() e setAttribute(). Solo gli attributi dell'oggetto sfUser sono persistenti fra le richieste.

Attributi Flash

Un problema ricorrente con gli attributi utente è la pulizia della sessione quando l'attributo non serve piu'. Ad esempio, potresti voler mostrare una conferma dopo che una form è stata riempita e spedita. Dato che la gestione delle form nell'azione fa un redirect, l'unico modo per poter passare informazioni da tale azione a quella cui redireziona è usare la sessione utente. Ma dopo che il messaggio è stato visualizzato, bisognerebbe eliminare l'attributo; altrimenti rimarrebbe in sessione fino alla sua scadenza.

L'attributo flash è un attributo effimero che puoi definire e poi dimenticare, certo che scomparirà alla richiesta successiva, lasciando la sessione pulita. Nell'azione lo puoi definire così:

[php]
$this->getUser()->setFlash('notice', $value);

Il template sarà renderizzato e spedito all'utente, che farà una richiesta per un'altra azione. In tale seconda azione, puoi recuperare il valore dell'attributo flash nella seguente maniera:

[php]
$value = $this->getUser()->getFlash('notice');

Dopodiché potrai dimenticarlo, in quanto dopo aver spedito la seconda pagina, l'attributo flash notice sarà eliminato. Lo sarà anche se non ne hai bisogno nella seconda azione.

Se avessi bisogno di accedervi in un template, puoi usare l'oggetto $sf_user:

[php]
<?php if ($sf_user->has('notice')): ?>
  <?php echo $sf_user->get('notice') ?>
<?php endif; ?>

oppure:

[php]
<?php echo $sf_user->get('notice') ?>

Gli attributi flash sono un metodo molto pulito di passare informazioni alla richiesta successiva.

Gestione della Sessione

Le funzionalita' della gestione delle sessioni di symfony nasconde completamente allo sviluppatore la memorizzazione degli ID di sessione server e client. Però se tu volessi modificare il comportamento di default di tale meccanismo lo potresti fare. è per utenti avanzati.

Lato client, le sessioni sono gestite dai cookie. Il cookie di sessione di symfony si chiama symfony, ma ne potresti cambiare il nome nel file di configurazione factories.yml, come mostrato nel Listato 6-18.

Listato 6-18- Cambiare il nome del cookie di sessione, in apps/frontend/config/factories.yml

all:
  storage:
    class: sfSessionStorage
    param:
      session_name: my_cookie_name

TIP La sessione parte (tramite la funzione PHP session_start()) solo se il parametro auto_start è impostato a true nel file factories.yml (come di default). Se vuoi avviare la sessione manualmente, disabilita tale parametro.

La gestione delle sessioni di symfony è basata sulla gestione delle sessioni di PHP. Questo significa che se tu volessi le sessioni lato client gestite da URL invece di cookie, dovresti semplicemente cambiare il parametro use_trans_sid di php.ini. Questo è però sconsigliato.

session.use_trans_sid = 1

Lato server, symfony gestisce le sessioni per default su file. Le puoi memorizzare su database cambiando il valore del parametro class nel file di configurazione factories.yml, come mostrato nel Listato 6-19.

Listato 6-19 - Modifica della memorizzazione delle sessioni, in apps/myapp/config/factories.yml

all:
  storage:
    class: sfMySQLSessionStorage
    param:
      db_table: session      # Nome della tabella che memorizza le sessioni
      database: propel       # Nome della connessione al database
      # Parametri opzionali
      db_id_col:   sess_id   # Nome della colonna che contiene l'id di sessione
      db_data_col: sess_data # Nome della colonna che contiene i dati di sessione
      db_time_col: sess_time # Nome della colonna che contiene il timestamp di sessione

Le classi disponibili per la memorizzazione delle sessioni sono sfMySQLSessionStorage, sfMySQLiSessionStorage,sfPostgreSQLSessionStorage, e sfPDOSessionStorage; l'ultima è la preferita. Il settaggio opzionale database definisce la connessione da usare; quindi symfony utilizzera' il file di configurazione databases.yml (vedi Capitolo 8) per determinare le impostazioni (host, nome database, utente e pssword) da utilizzare. Per disabilitare completamente le sessioni, puoi usare la classe sfNoStorage.

Le sessioni scadono automaticamente dopo 30 minuti. Questa impostazione di default può essere modificata per ogni ambiente nello stesso file di configurazione factories.yml, ma stavolta nel factory user.yml, come mostrato nel Listato 6-20.

Listato 6-20 - Cambiare la durata delle sessioni, in apps/frontend/config/factories.yml

all:
  user:
    class:       myUser
    param:
      timeout:   1800           # Session lifetime in seconds

Per saperne di più sui factories, vedi il Capitolo 19.

Sicurezza nelle Action

La possibilita' di eseguire un'azione può essere ristretta ad un certo insieme di utenti con particolari privilegi. Le funzionalita' fornite da symfony a tale scopo permettono di realizzare applicazioni sicure, dove gli utenti devono essere autenticati per poter accedere ad alcune parti dell'applicazione. Mettere un'applicazione in sicurezza richiede due step: dichiarare i requisiti di sicurezza per ogni azione e far accedere gli utenti con privilegi tali da poter utilizzare tali azioni.

Restrizioni d'accesso

Prima di essere eseguita, ogni azione passa da un filtro per verificare che l'utente corrente abbia i permessi di eseguirla. In symfony, i privilegi sono composti di due parti:

  • Azioni sicure necessitano che l'utente sia utenticato.
  • Le credenziali sono privilegi di sicurezza che permettono di organizzarla in gruppi.

Restringere l'accesso ad un'azione significa semplicemente creare e modificare un file chiamato security.yml dentro la cartella config/ del modulo. In questo file, puoi specificare i requisiti di sicurezza che gli utenti devono possedere per ogni azione o per tutte. Il Listato 6-21 ne mostra un esempio.

Listato 6-21 - Impostare restrizioni di accesso, in apps/frontend/modules/mymodule/config/security.yml

read:
  is_secure:   off       # All users can request the read action

update:
  is_secure:   on        # The update action is only for authenticated users

delete:
  is_secure:   on        # Only for authenticated users
  credentials: admin     # With the admin credential

all:
  is_secure:  off        # off is the default value anyway

Le azioni non sono sicure per default, quindi se non c'è un file security.yml oppure tale file esiste ma un'azione non vi è menzionata, tale azione è accessibile a chiunque. Se il file security.yml esiste, symfony cerca il nome dell'azione richiesta, e se la trova, ne controlla il soddisfacimento dei requisiti di sicurezza. Quello che succede quando un utente cerca di accedere ad una pagina protetta dipende dalla sue credenziali:

  • Se l'utente è autenticato e possiede le giuste credenziali, l'azione è eseguita.
  • Se l'utente non è identificato, sara' rediretto all'azione di login di default.
  • Se l'utente è autenticato ma non possiede le giuste credenziali, sara' rediretto all'azione di default mostrata in Figura 6-1.

Le pagine di login e protezione di default sono abbastanza semplici, e probabilmente vorrai modificarle. Puoi configurare quale azione deve essere chiamata in caso di privilegi insufficienti nel file settings.yml cambiando il valore della proprieta' mostrata nel Listato 6-22.

Figura 6-1 - Pagina di default per azioni protette Figure 6-1 - The default secure action page

Pagina di default per azioni protette

Listato 6-22 - Le azioni di sicurezza di default sono definite in apps/frontend/config/settings.yml

all:
  .actions:
    login_module:           default
    login_action:           login

    secure_module:          default
    secure_action:          secure

Garantire l'accesso

Per poter accedere a certe azioni, gli utenti devono essere autenticati e/o possedere determinati privilegi. Puoi estendere i privilegi di un utente utilizzando i metodi dell'oggetto sfUser. Lo stato di autenticazione è impostato tramite il metodo setAuthenticated(). Il Listato 6-23 mostra un semplice esempio di autenticazione.

Listato 6-23 - Impostare lo stato di autenticazione di un utente

[php]
class myAccountActions extends sfActions
{
  public function executeLogin($request)
  {
    if ($request->getParameter('login') == 'foobar')
    {
      $this->getUser()->setAuthenticated(true);
    }
  }

  public function executeLogout()
  {
    $this->getUser()->setAuthenticated(false);
  }
}

Le credenziali sono leggermente più complicate da gestire, in quanto possono essere assegnate, rimosse od eliminate. Il Listato 6-24 descrive i metodi delle credenziali per la classe sfUser.

Listato 6-24 - Gestire le credenziali utente nell'Action

[php]
class myAccountActions extends sfActions
{
  public function executeDoThingsWithCredentials()
  {
    $user = $this->getUser();

    // Add one or more credentials
    $user->addCredential('foo');
    $user->addCredentials('foo', 'bar');

    // Check if the user has a credential
    echo $user->hasCredential('foo');                      =>   true

    // Check if the user has both credentials
    echo $user->hasCredential(array('foo', 'bar'));        =>   true

    // Check if the user has one of the credentials
    echo $user->hasCredential(array('foo', 'bar'), false); =>   true

    // Remove a credential
    $user->removeCredential('foo');
    echo $user->hasCredential('foo');                      =>   false

    // Remove all credentials (useful in the logout process)
    $user->clearCredentials();
    echo $user->hasCredential('bar');                      =>   false
  }
}

Se l'utente possiede la credenziale 'foo', potra' accedere alle azioni per le quali il file security.yml richiede tale credenziale. Le credenziali possono essere utilizzate anche per mostrare contenuto protetto in una template, come mostrato nel Listato 6-25.

Listato 6-25 - Credenziali utente in un template

[php]
<ul>
  <li><?php echo link_to('section1', 'content/section1') ?></li>
  <li><?php echo link_to('section2', 'content/section2') ?></li>
  <?php if ($sf_user->hasCredential('section3')): ?>
  <li><?php echo link_to('section3', 'content/section3') ?></li>
  <?php endif; ?>
</ul>

Come per l'autenticazione, anche le credenziali sono apesso assegnate ad un utente durante il processo di login. Questo è il motivo per cui l'oggetto sfUser viene spesso esteso con i metodi login e logout, così da poter impostare lo stato di sicurezza di un utente in un unico punto centrale.

TIP Tra i vari plugin di symfony, sfGuardPlugin estende la classe di sessione per facilitare login e logout. Controlla il Capitolo 17 per maggiori informazioni.

Credenziali complesse

La sintassi YAML utilizzata nel file security.yml ti permette di restringere l'accesso ad utenti che possiedono una combinazione di credenziali, utilizzando associazioni AND oppure OR. Tramite combinazioni del genere, puoi creare un workflow complesso ed un sistema di gestione dei privilegi; ad esempio, il back-office di un content management system (CMS) accessibile solo ad utenti con privilegi di amministrazione, mentre gli articoli possono essere modificati solo da utenti con credenziali editor e pubblicati solo da quelli con credenziali publisher. Il Listato 6-26 mostra questo esempio.

Listato 6-26 - Sintassi di combinazione di credenziali

editArticle:
  credentials: [ admin, editor ]              # admin AND editor

publishArticle:
  credentials: [ admin, publisher ]           # admin AND publisher

userManagement:
  credentials: [[ admin, superuser ]]         # admin OR superuser

Ogni volta che aggiungi un livello di parentesi quadre, la logica alterna tra AND e OR. In tal maniera puoi creare combinazioni di credenziali veramente complesse, come:

credentials: [[root, [supplier, [owner, quasiowner]], accounts]]
             # root OR (supplier AND (owner OR quasiowner)) OR accounts

Metodi per la validazione e per la gestione dell'errore

Le caratteristiche descritte in questa sezione sono deprecate in symfony 1.1 e funzionano solo se si abilita il plugin sfCompat10.

La validazione dell'input (per lo più parametri di richiesta) è un lavoro noioso e ripetitivo. Symfony dispone di un sistema per la validazione incorporato, utilizzando metodi delle azioni.

Cominciamo con un esempio. Quando un utente esegue una richiesta per myAction, per prima cosa symfony cerca sempre se esiste un metodo validateMyAction(). Se lo trova, lo esegue. Il valore di ritorno di tale metodo determina la prossima azione da eseguire: se true, viene eseguita executeMyAction(), altrimenti viene eseguita handleErrorMyAction(). Se anche quest'ultimo metodo non esiste, symfony cerca un metodo generico handleError(). Se infine anche questo non viene trovato, symfony restituisce semplicemente sfView::ERROR per eseguire il template myActionError. La Figura 6-2 mostra questo processo.

Figura 6-2 - Il processo di validazione

Il processo di validazione

Quindi la chiave per la validazione è rispettare la convenzione dei nomi:

  • validateActionName è il metodo per la validazione, che resitituisce {{{true}}} o {{{false}}}. è il primo metodo cercato quando viene richiesta l'azione {{{ActionName}}}. Se non esiste, l'azione viene chiamata direttamente.
  • handleErrorActionName è il metodo chiamato quando la validazione fallisce. Se non esiste, viene visualizzato il template {{{Error}}}.
  • executeActionName è il metodo dell'azione. Deve esistere per ogni azione.

Il Listato 6-27 mostra un esempio di azione con la validazione. Sia che la validazione abbia successo o fallisca, in questo esempio viene sempre visualizzato il template myActionSuccess.php, ma con parametri diversi.

Listato 6-27 - Esempio di metodo per la validazione

[php]
class mymoduleActions extends sfActions
{
  public function validateMyAction($request)
  {
    return ($request->getParameter('id') > 0);
  }

  public function handleErrorMyAction()
  {
    $this->message = "Invalid parameters";

    return sfView::SUCCESS;
  }

  public function executeMyAction()
  {
    $this->message = "The parameters are correct";
  }
}

Puoi inserire il codice che preferisci nel metodo validate(). Devi essere semplicemente sicuro che esso restituisca o true o false. Come i metodi della classe sfActions, ha accesso agli oggetti sfRequest e sfUser, che possono essere molto utili per la validazione dell'input e del contesto.

Potresti utilizzare questo meccanismo per la validazione delle form (che altro non è se non il controllo dei dati inseriti prima di processarli), ma quello è proprio un task ripetitivo e noiso per il quale symfony ha meccanismi automatici, come mostrato nel Capitolo 10.

Filtri

Il processo di sicurezza può essere interpretato come un filtro tramite il quale tutte le richieste devono passare prima di poter eseguire un'azione. A seconda di alcuni test eseguiti nel filtro, il processo della richiesta viene modificato: ad esempio, l'azione da eseguire (nel caso di filtro di sicurezza default/secure invece dell'azione richiesta). Symfony estende quest'idea per filtrare le classi. Puoi specificare un qualsiasi numero di (classi) filtri da eseguire prima dell'esecuzione di un'azione o prima del rendering di una risposta, e farlo per ogni richiesta. Puoi vedere i filtri come un metodo per pacchettizzare codice, simile a preExecute() e postExecute(), ma ad un livello più alto (per l'intera applicazione invece che per l'intero modulo).

La catena dei filtri

Effettivamente symfony vede il processo di una richiesta come una catena di filtri. Quando il framework riceve una richiesta, esegue il primo filtro (che è sempre sfRenderingFilter). Ad un certo punto, esegue il secondo filtro, poi il seguente e così via. Quando viene eseguito l'ultimo filtro (che è sempre sfExecutionFilter), il filtro precedente può terminare, e così via fino al primo. La Figura 6-3 illustra tale concetto con un diagramma di sequenza, utilizzando una catena di filtri artificiale (quella reale ne contiene di più).

Figura 6-3 - Esempio di catena di filtri

Esempio di catena di filtri

Questo processo giustifica la struttura delle classi di filtri. Esse estendono tutte la classe sfFilter, e contengono un metodo execute(), che si aspetta come parametro un oggetto $filterChain. Da qualche parte in questo metodo, il filtro passa a quello successivo chiamando $filterChain->execute(). Il Listato 6-28 ne mostra un esempio. Quindi, fondamentalmente, i filtri sono divisi in due parti:
* Il codice prima della chiamata $filterChain->execute() viene eseguito prima dell'azione. * Il codice dopo la chiamata $filterChain->execute() viene eseguito dopo l'azione ma prima del rendering.

Listato 6-28- Struttura di una classe filtro

[php]
class myFilter extends sfFilter
{
  public function execute ($filterChain)
  {
    // Code to execute before the action execution
    ...

    // Execute next filter in the chain
    $filterChain->execute();

    // Code to execute after the action execution, before the rendering
    ...
  }
}

La catena di filtri di default è definita in un file di configurazione dell'applicazione chiamato filters.yml, ed è mostrato nel Listato 6-29. Questo file contiene un elenco dei filtri che devono essere eseguiti per ogni richiesta.

Listato 6-29 - Catena di filtri di default, in frontend/config/filters.yml

rendering: ~
security:  ~

# Generally, you will want to insert your own filters here

cache:     ~
common:    ~
execution: ~

Queste dichiarazioni non hanno parametri (in YAML, la tilde, ~, significa "null"), perché ereditano i parametri definiti nel core di symfony. All'interno del proprio core, infatti, symfony definisce impostazioni class e param per ognuno di questi filtri. Ad esempio, il Listato 6-30 mostra i parametri di default per il filtro rendering.

Listato 6-30 - Parametri di default per il filtro rendering, in $sf_symfony_lib_dir/config/config/filters.yml

rendering:
  class: sfRenderingFilter   # Classe del filtro
  param:                     # Parametri del filtro
    type: rendering

Lasciando un valore vuoto (~) nel file filters.yml dell'applicazione, dici a symfony di utilizzare i filtri con i parametri di default definiti nel core.

Puoi personalizzare la catena di filtri in diversi modi:

  • Disabilitando alcuni filtri dalla catena aggiungendo il parametro enabled: off. Ad esempio, per disabilitare il filtro comune, che inserisce CSS e Javascript in head, scrivi: common: enabled: off

  • Non eliminare una riga da filters.yml per disabilitare un filtro; symfony in questo caso genererebbe un'eccezione.

  • Aggiungi le tue dichiarazioni da qualche parte nella catena (di solito dopo il filtro security) per inserire un filtro personalizzato (come discusso nella prossima sezione). Fai attenzione a che il filtro rendering sia sempre il primo ed il filtro execute sempre l'ultimo della catena.

  • Sovrascrivi la classe e dei parametri di default del filtro di default (specialmente per modificare il sistema di sicurezza ed usare i tuoi filtri).

Creare filtri personalizzati

È abbastanza semplice costruire un filtro. Crea una classe simile a quella del Listato 6-28, e posizionala in una delle cartelle lib/ approfittando della funzionalità di autoloading.

Dato che un'azione può fare un redirect od un forward ad un'altra azione e di conseguenza rilanciare tutta la catena di filtri, potresti voler restringere l'esecuzione dei tuoi filtri alla sola prima chiamata della richiesta. Il metodo isFirstCall() della classe sfFilter restituisce un booleano a questo scopo. Questa chiamata ha senso esclusivamente prima dell'azione execution.

Questi concetti sono più chiari con un esempio. Il Listato 6-31 mostra un filtro utilizzato per autenticare automaticamente utenti che possiedono uno specifico cookie MyWebSite, che presumibilmente viene creato dall'azione di login. È un modo rudimentale ma funzionante di implementare la funzionalità "ricordati di me" offerta spesso nelle form di login.

Listato 6-31 - Esempio di filtro, in apps/frontend/lib/rememberFilter.class.php

[php]
class rememberFilter extends sfFilter
{
  public function execute($filterChain)
  {
    // Esegui questo filtro una sola volta
    if ($this->isFirstCall())
    {
      // I filtri non hanno accesso diretto agli oggetti request e user.
      // Hai bisogno dell'oggetto context per ottenerli
      $request = $this->getContext()->getRequest();
      $user    = $this->getContext()->getUser();

      if ($request->getCookie('MyWebSite'))
      {
        // entra
        $user->setAuthenticated(true);
      }
    }

    // Esegue il prossimo filtro
    $filterChain->execute();
  }
}

In qualche caso, invece di continuare l'esecuzione della catena di filtri, alla fine di un filtro avrai bisogno di fare un forward ad una data azione. sfFilter non possiede un metodo forward(), ma sfController si, per cui puoi fare semplicemente:

[php]
return $this->getContext()->getController()->forward('mymodule', 'myAction');

NOTE La classe sfFilter possiede un metodo initialize(), eseguito quando viene creato l'oggetto filtro. Puoi sovrascrivere tale metodo nel tuo filtro personalizzato se hai bisogno di gestire parametri (definiti in filters.yml, come descritto in seguito).

Attivazione dei filtri e parametri

Creare un filtro non significa attivarlo. Devi aggiungere il tuo filtro alla catena, e per farlo devi dichiararlo nel file filters.yml, situato nella cartella config/ dell'applicazione o del modulo, come mostrato nel Listato 6-32.

Listato 6-32 - Esempio di attivazione di un filtro, in apps/frontend/config/filters.yml

rendering: ~
security:  ~

remember:                 # I filtri hanno bisogno di un nome univoco
  class: rememberFilter
  param:
    cookie_name: MyWebSite
    condition:   %APP_ENABLE_REMEMBER_ME%

cache:     ~
common:    ~
execution: ~

Una volta attivato, il filtro viene eseguito ad ogni richiesta. Il file di configurazione dei filtri può contenere una o più definizioni di parametri sotto la chiave param. La classe ha la possibilità di ricevere i valori di tali parametri tramite il metodo getParameter(). Il Listato 6-33 ne mostra un esempio.

Listato 6-33 - Ricevere i parametri, in apps/frontend/lib/rememberFilter.class.php

[php]
class rememberFilter extends sfFilter
{
  public function execute($filterChain)
  {
      // ...
      if ($request->getCookie($this->getParameter('cookie_name')))
      // ...
  }
}

Il parametro condition viene testato dalla catena di filtri per verificare se il filtro debba essere eseguito o meno. Quindi la dichiarazione del tuo filtro si potrebbe basare sulla configurazione di un'applicazione, come nel Listato 6-32. Il filtro remember verrà eseguito esclusivamente se nel tuo app.yml è presente la seguente impostazione:

all:
  enable_remember_me: on

Filtri d'esempio

La funzionalità di filtro è utile nella ripetizione di codice per ogni azione. Ad esempio, nell'utilizzo di un sistema di tracking esterno, probabilmente avrai bisogno di inserire un pezzo di codice in ogni pagina dell'applicazione. Potresti metterlo nel layout globale, ma così sarebbe disponibile per tutte le applicazioni. Oppure, potresti metterlo in un filtro, come mostrato nel Listato 6-34, ed attivarlo a seconda del modulo.

Listato 6-34 - Filtro Google Analytics

[php]
class sfGoogleAnalyticsFilter extends sfFilter
{
  public function execute($filterChain)
  {
    // Non serve fare nulla prima dell'azione
    $filterChain->execute();

    // Decora la risposta con il codice di tracciamento
    $googleCode = '
<script src="http://www.google-analytics.com/urchin.js"  type="text/javascript">
</script>
<script type="text/javascript">
  _uacct="UA-'.$this->getParameter('google_id').'";urchinTracker();
</script>';
    $response = $this->getContext()->getResponse();
    $response->setContent(str_ireplace('</body>', $googleCode.'</body>',$response->getContent()));
   }
}

Fai attenzione al fatto che il filtro nell'esempio non è perfetto, in quanto non dovrebbe aggiungere il tracker in risposte che non siano HTML.

Un altro esempio potrebbe essere un filtro che commuta la risposta in SSL, se non lo è già, come mostrato nel Listato 6-35.

Listato 6-35 - Filtro per comunicazione sicura

[php]
class sfSecureFilter extends sfFilter
{
  public function execute($filterChain)
  {
    $context = $this->getContext();
    $request = $context->getRequest();

    if (!$request->isSecure())
    {
      $secure_url = str_replace('http', 'https', $request->getUri());

      return $context->getController()->redirect($secure_url);
      // Non continuiamo la catena dei filtri
    }
    else
    {
      // La richiesta è già sicura, quindi possiamo continuare
      $filterChain->execute();
    }
  }
}

I filtri sono utilizzati abbondantemente nei plugin, dato che ti permettono di estendere le funzionalità di una applicazione globalmente. Controlla il Capitolo 17 per conoscere meglio i plugin, e controlla il wiki online (http://www.symfony-project.com/trac/wiki) per altri filtri d'esempio.

Configurazione del modulo

Pochi comportamenti dei moduli dipendono dalla configurazione. Per modificarli, devi creare un file chiamato module.yml nella cartella config/ del modulo, e definire le impostazioni per ogni ambiente (o sotto l'header all: per tutti gli ambienti). Il Listato 6-36 ne mostra un esempio.

Listato 6-36 - Configurazione del modulo, in apps/frontend/modules/mymodule/config/module.yml

all:                 # Per tutti gli ambienti
  enabled:     true
  is_internal: false
  view_name:   sfPHP

Il parametro enabled ti permette di disabilitare tutte le azioni di un modulo. Tutte le azioni vengono redirezionate a module_disabled_module/module_disabled_action (come impostato in settings.yml).

Il parametro is_internal ti permette di restringere a chiamate interne tutte le azioni di un modulo. Ad esempio, questo torna utile per tutte le azioni che spediscono e-mail, le quali dovrebbero essere disponibili alle altre azioni ma non direttamente dall'esterno.

Il parametro view_name definisce la classe delle viste. Deve ereditare da sfView. Sovrascrivendo questo parametro puoi utilizzare altri sistemi per i template, come ad esempio Smarty.

Riepilogo

In symfony, il controller è diviso in due parti: il front controller, unico punto di accesso all'applicazione per un dato ambiente, e le azioni, che contengono la logica delle pagine. Un'azione ha la capacità di determinare come sarà eseguita la propria vista, restituendo una delle costanti sfView. All'interno di un'azione, puoi manipolari diversi elementi del contesto, compresa l'oggetto richiesta (sfRequest) e l'oggetto sessioni utente (sfUser).

Unendo la potenza dell'oggetto sessioni, dell'oggetto azioni, e delle configurazioni di sicurezza si ottiene un sistema completamente sicuro, con gestione di restrizioni e credenziali. I metodi speciali validate() e handleError() delle azioni permettono la gestione della validazione. Ed i metodi perExecute() e postExecute() sono pensati per la riusabilità del codice in un modulo, mentre i filtri autorizzano la stessa riusabilità per tutte le applicazioni eseguendo il codice del controller ad ogni richiesta.