Development

Documentation/it_IT/book/1.1/02-Exploring-Symfony-s-Code

You must first sign up to be able to contribute.

Capitolo 2 - Esplorando il codice

Ad una prima occhiata, il codice di un'applicazione può sembrare scoraggiante; consiste di diverse cartelle e script, ed i file sono un insieme di classi PHP, HTML ed anche un misto di entrambi. Vedrai anche riferimenti a classi che non sono all'interno della cartella dell'applicazione, e la profondità di tale cartella arriva a sei livelli. Ma una volta comprese le ragioni di tale apparente complessità, improvvisamente ti sentirai talmente a tuo agio che non cambieresti la struttura di symfony con nessun altra. Questo capitolo ti toglierà qualsiasi dubbio.

Il pattern MVC

Symfony è basato sul classico pattern conosciuto come architettura MVC, il quale consiste di tre livelli:

  • Il modello rappresenta l'informazione sulla quale opera l'applicazione, ovvero la sua business logic.
  • Le viste renderizzano il modello come pagine web adeguate all'interazione con l'utente.
  • Il controller risponde alle interazioni dell'utente e invoca i cambiamenti necessari sul modello o sulle viste.

La figura 2-1 illustra il pattern MVC.

L'architettura MVC separa la business logic (modello) dalla presentazione (viste) fornendo così all'applicazione una grande capacità di manutenzione. Ad esempio, se la tua applicazione dovesse girare sia su browser standard che su palmari, avresti bisogno solo di una nuova vista; il modello ed il controller non verrebbero toccati. Il controller aiuta a nascondere i dettagli del protocollo usato per la richiesta (HTTP, modalità console, mail, ecc...) dal modello e dalla vista. Ed il modello astrae la logica dei dati, il che rende le viste e le azioni indipendenti, ad esempio, dal database sottostante.

Figura 2-1 - Il pattern MVC

Il pattern MVC

Lavorare con MVC

Per comprendere meglio i vantaggi di questa architettura, vediamo come convertire un'applicazione PHP classica in una basata su MVC; una lista di post per un blog saranno un esempio perfetto.

Programmazione flat

In un file PHP flat, visualizzare una lista di record da un database potrebbe essere implementato come nel Listato 2-1:

Listato 2-1 - Uno script flat

[php]
<?php

// Connecting, selecting database
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// Performing SQL query
$result = mysql_query('SELECT date, title FROM post', $link);

?>

<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
   <h1>List of Posts</h1>
   <table>
     <tr><th>Date</th><th>Title</th></tr>
<?php
// Printing results in HTML
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "\t<tr>\n";
printf("\t\t<td> %s </td>\n", $row['date']);
printf("\t\t<td> %s </td>\n", $row['title']);
echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>

<?php

// Closing connection
mysql_close($link);

?>

Questo è abbastanza rapido da scrivere, veloce da eseguire ed impossibile da mantenere. Ecco i maggiori problemi con questo tipo di codice:

  • Non ci sono controlli di errore (che succede se la connessione al database fallisce?)
  • C'è un misto di codice PHP ed HTML.
  • Il codice è legato ad un database MySQL.

Isolare la presentazione

Le chiamate {{{echo}}} e {{{printf}}} del Listato 2-1 rendono il codice difficile da leggere. Modificare il codice HTML per migliorare la presentazione risulta essere una seccatura con questa sintassi. Così il codice può essere diviso in due parti. Per prima cosa, tutto il codice PHP puro va posto all'interno del controller, a definire la business logic come si può vedere nel Listato 2-2:

Listato 2-2 - Il Controller, dentro index.php

[php]
<?php

 // Connecting, selecting database
 $link = mysql_connect('localhost', 'myuser', 'mypassword');
 mysql_select_db('blog_db', $link);

 // Performing SQL query
 $result = mysql_query('SELECT date, title FROM post', $link);

 // Filling up the array for the view
 $posts = array();
 while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
 {
    $posts[] = $row;
 }

 // Closing connection
 mysql_close($link);

 // Requiring the view
 require('view.php');

 ?>

Il codice HTML, contenente una sintassi di PHP simile ad una template, è memorizzato nello script di visualizzazione, come si vede nel Listato 2-3:

Listato 2-3 - La Vista, in view.php

[php]
<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
    <h1>List of Posts</h1>
    <table>
      <tr><th>Date</th><th>Title</th></tr>
    <?php foreach ($posts as $post): ?>
      <tr>
        <td><?php echo $post['date'] ?></td>
        <td><?php echo $post['title'] ?></td>
      </tr>
    <?php endforeach; ?>
    </table>
  </body>
</html>

Una buona regola per capire quando una vista è abbastanza pulita, è quella di verificare che ci sia solo una minima parte di codice PHP al proprio interno, in modo che un designer HTML che non conosce PHP non abbia problemi a lavorarci. Gli statement più comuni in una vista sono echo, if/endif, foreach/endoforeach. Infine, non ci dovrebbe essere codice PHP che stampa codice HTML.

Tutta la logica viene spostata all'interno del controller, e contiene solo PHP puro, niente HTML. Per capire meglio il significato, immagina che lo stesso controller potrebbe essere riutilizzato per una presentazione completamente differente, magari in formato PDF o XML.

Isolare la manipolazione dei dati

La maggior parte del codice del controller è dedicata alla manipolazione dei dati. Ma cosa succede se avessi bisogno della lista dei post per un altro controller, diciamo uno che rende feed RSS dei post del blog? Oppure se tu volessi tenere tutte le query in un solo posto, onde evitare duplicazione? Che succede se tu decidessi di cambiare il modello dei dati in modo che la tabella {{{post}}} venga rinominata in {{{weblog_post}}}? Se tu volessi passare a PostgreSQL invece di MySQL? Per rendere possibile tutto ciò, c'è bisogno di rimuovere il codice di manipolazione dei dati dal controller per metterlo in un altro script, chiamato modello, come si vede dal Listato 2-4:

Listato 2-4 - Il Modello, in model.php

[php]
<?php

function getAllPosts()
{
  // Connecting, selecting database
  $link = mysql_connect('localhost', 'myuser', 'mypassword');
  mysql_select_db('blog_db', $link);

  // Performing SQL query
  $result = mysql_query('SELECT date, title FROM post', $link);

  // Filling up the array
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }

  // Closing connection
  mysql_close($link);

  return $posts;
}

?>

Il controller revisionato è presentato nel Listato 2-5:

Listato 2-5 - Il Controller, revisionato, in index.php

[php]
<?php

// Requiring the model
require_once('model.php');

// Retrieving the list of posts
$posts = getAllPosts();

// Requiring the view
require('view.php');

?>

Il controller diventa ora facile da leggere. Il suo unico compito è quello di prendere i dati dal modello e passarli alla vista. In applicazioni più complesse, il controller ha anche a che fare con la richiesta, le sessioni utente, l'autenticazione e così via. L'uso di nomi espliciti per le funzioni del modello rende i commenti non necessari al proprio interno.

Lo script del modello è dedicato all'accesso dei dati, ed è organizzato di conseguenza. Tutti i parametri che non dipendono dal layer dei dati (come i parametri di richiesta) devono essere forniti dal controller, e non acceduti direttamente dal modello; così le funzioni del modello possono essere facilmente riutilizzate in un altro controller.

La separazione dei livelli dietro MVC

Secondo la propria natura, il principio di MVC è quello di separare il codice in tre livelli. La data logic sta nel modello, la presentazione nella vista, e la logica dell'applicazione nel controller.

Altri design pattern permettono di facilitare ulteriormente il modello; i tre livelli possono essere infatti suddivisi ulteriormente.

Astrazione del database

Lo strato del modello può essere suddiviso in livello di accesso ai dati e livello di astrazione del database. In questo modo, le funzioni che accedono ai dati non devono eseguire delle query dipendenti dal database, ma chiameranno altre funzioni, le quali a loro volta eseguiranno le richieste. Se in un secondo momento cambierai il database, solo il livello di astrazione avrà bisogno di essere aggiornato.

Un esempio specifico di livello di accesso ai dati per un database MySQL è illustrato nel Listato 2-6, seguìto da un esempio di livello di astrazione nel Listato 2-7:

Listato 2-6 - Astrazione dei dati del modello

[php]
<?php

function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}

function close_connection($link)
{
  mysql_close($link);
}

function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);

  return mysql_query($query, $link);
}

function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

Listato 2-7 - Accesso ai dati

[php]
function getAllPosts()
{
  // Connecting to database
  $link = open_connection('localhost', 'myuser', 'mypassword');

  // Performing SQL query
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);

  // Filling up the array
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }

  // Closing connection
  close_connection($link);

  return $posts;
}

?>

Come si può vedere, nel livello di accesso ai dati non è presente alcuna funzione dipendente dal database; inoltre le funzioni create nello strato di astrazione del database possono essere riutilizzate da altre funzioni del modello che necessitino di accedere al database.

NOTE Gli esempi dei listati 2-6 e 2-7 non sono molto soddisfacenti, e mancano diverse parti per avere un'astrazione completa del database (astrarre il codice SQL tramite un costruttore di query indipendente dal database, spostare tutte le funzioni all'interno di una singola classe, ecc...). Ma lo scopo di questo libro non è quello di mostrare tutto questo codice, e si vedrà nel capitolo 8 come symfony nativamente possieda un'astrazione molto completa.

Le viste

Anche il livello vista può trarre qualche beneficio dalla suddivisione del codice. Una pagina web spesso contiene elementi che si ripetono dall'inizio alla fine di un'applicazione: gli header, il layout grafico, il piè di pagina ed il sistema di navigazione. Solo la parte interna della pagina cambia; questo è il motivo per cui la vista viene separata in layout e template. Il layout è normalmente globale, all'intera applicazione o ad un gruppo di pagine. La template non fa altro che dare una forma alle variabili messe a disposizione dal controller. C'è bisogno di una certa logica per far funzionare insieme questi componenenti, e questo livello logico manterrà il nome di vista. Secondo questi princìpi, la parte di vista del Listato 2-3 può essere suddiviso in tre parti, come mostrato nei Listati 2-8, 2-9 e 2-10.

Listato 2-8 - La template della vista, in mytemplate.php

[php]
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listato 2-9 - La logica della vista

[php]
<?php

$title = 'List of Posts';
$content = include('mytemplate.php');

?>

Listato 2-10 - Il layout della vista

[php]
<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php echo $content ?>
  </body>
</html>

Action e Front Controller

Negli esempi precedenti il controller non fa molto, ma in applicazioni web reali il controller ha molto lavoro da eseguire. Una parte importante di tale lavoro è comune a tutti i controller dell'applicazione. I task più comuni riguardano la gestione delle richieste, la gestione della sicurezza, il caricamento della configurazione dell'applicazione ed altri task simili. Per tale motivo spesso il controller viene suddiviso tra front controller, unico per l'intera applicazione, e le azioni, che contengono solo il codice del controller relativo ad una pagina.

Uno dei grandi vantaggi di un front controller è proprio il fatto di offrire un unico punto di ingresso per l'intera applicazione; qualora tu decidessi di chiudere l'accesso all'applicazione, basterebbe modificare il front controller. In un'applicazione sprovvista di front controller, andrebbero modificati tutti i singoli script di controller.

Orientamento agli oggetti

Tutti gli esempi precedenti utilizzano una programmazione procedurale. Le capacità OOP dei linguaggi moderni rendono la programmazione ancora più facile, dato che gli oggetti possono incapsulare la logica, ereditare da altri oggetti e fornire una chiara convenzione per i nomi.

Implementare un'architettura MVC in un linguaggio che non è orientato agli oggetti aumenta le problematiche relative allo spazio dei nomi ed alla duplicazione del codice, che risulta poi difficile da leggere.

L'orientamento agli oggetti permette agli sviluppatori di gestire le viste, il controller ed il modello, di trasformare le funzioni viste negli esempi precedenti in metodi; è un "must" per l'architettura MVC.

TIP Se vuoi approfondire sui design pattern per applicazioni web in un contesto orientato agli oggetti, leggi Patterns of Enterprise Application Architecture di Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0). Gli esempi del libro di Fowler sono in Java o C#, ma risultano abbastanza leggibili per uno sviluppatore PHP.

Implementazione di MVC in symfony

Aspetta un attimo. Per una pagina singola che mostra tutti i post di un blog, quante componenti servono? Come si può vedere in Figura 2-2, abbiamo le parti seguenti:

  • Strato del Modello
  • Astrazione del database
  • Accesso ai dati
  • Strato della Vista
  • Vista
  • Template
  • Layout
  • Strato del Controller
  • Front Controller
  • Azioni

Sette script, un bel pò di file da aprire e modificare ogni volta che devi creare una nuova pagina! symfony semplifica le cose. Prendendo il meglio dell'architettura MVC, symfony la implementa in maniera tale che lo sviluppo diventa veloce ed indolore.

Prima di tutto, front controller e layout sono comuni a tutte le azioni dell'applicazione. Si possono avere diversi controller e layout, ma solo uno per ognuno di essi è necessario. Il front controller è un componente a logica MVC puro, e non avrai mai bisogno di scriverne uno poichè symfony lo genera per te.

L'altra buona notizia è che anche le classi del modello sono generate automaticamente, e basate sulla tua struttura dati. Questo è il compito della libreria Propel, che fornisce lo scheletro delle classi ne genera il codice. Se Propel trova una chiave importata o dei campi date, fornirà strumenti speciali e metodi particolari che renderanno la gestione dei dati un gioco da ragazzi. E l'astrazione del database è asolutamente invisibile allo sviluppatore, perchè viene gestita da un altro componente chiamato Creole. Per cui, se decidi di cambiare database ad un dato momento, non hai da riscrivere nemmeno una linea di codice. Devi solo cambiare un parametro di configurazione.

E l'ultima particolarità è che le viste possono essere tradotte facilmente tramite un file di configurazione, non è necessario alcun sviluppo.

Figura 2-2 - Diagramma di flusso di symfony

Diagramma di flusso di symfony

Questo significa che la lista dei post descritta nel nostro esempio, ha bisogno solo di tre file per poter essere implementata in symfony, come si vede dai listati 2-11, 2-12 e 2-13.

Listato 2-11 - list Action, in myproject/apps/myapp/modules/weblog/actions/actions.class.php

[php]
<?php
class weblogActions extends sfActions
{
  public function executeList()
  {
    $this->posts = PostPeer::doSelect(new Criteria());
  }
}

?>

Listato 2-12 - list Template, in myproject/apps/myapp/modules/weblog/templates/listSuccess.php

[php]
<?php slot('title', 'List of Posts') ?>
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>

Infine, ci sarà comunque bisogno di definire un layout, come mostrato nel Listato 2-13, ma questo verrà riutilizzato molte volte.

Listato 2-13 - Layout, in myproject/apps/myapp/templates/layout.php

[php]
<html>
  <head>
    <?php echo include_slot('title') ?>
  </head>
  <body>
    <?php echo $sf_content ?>
  </body>
</html>

E questo è tutto quel che ti serve. Questo è esattamente il codice necessario per visualizzare la stessa identica pagina renderizzata precedentemente dallo script flat del Listato 2-1. Il resto (far funzionare i vari componenti) è gestito da symfony. Se conti le linee di codice, vedrai che creare la lista di post secondo l'architettura MVC con symfony non necessita di più tempo o codice che scrivere uno script flat. Tuttavia usare symfony fornisce enormi vantaggi, tra cui una chiara organizzazione del codice, riusabilità, flessibilità, e molto più divertimento. E come bonus, avrai la conformità XHTML, possibilità di debug, facili configurazioni, astrazione dal database, routing di URL intelligenti, ambienti multipli e molti altri strumenti di sviluppo.

Classi di base (core) di symfony

L'implementazione di MVC in symfony utilizza diverse classi che incontrerai di frequente in questo libro:

  • sfController è la classe del controller. Decodifica le richieste e le trasmette alle azioni.
  • sfRequest memorizza tutti gli elementi di una richiesta (parametri, cookie, header ecc...)
  • sfResponse contiene l'header di risposta ed il contenuto. Si tratta dell'oggetto che alla fine sarà convertito in HTML e spedito all'utente.
  • Il contesto (recuperato tramite sfContext::getInstance()) contiene il riferimento a tutti gli oggetti core della configurazione corrente; è accessibile da qualunque parte.

Nel capitolo 6 apprenderai di più a proposito di questi oggetti.

Come puoi vedere, tutte le classi di symfony utilizzano il prefisso sf, come anche le variabili di core nelle template (es. $sf_user). Questo dovrebbe evitare conflitti con classi e variabili scritte da te, e rende più facilmente riconoscibili le strutture di core.

NOTE Tra i vari standard di codifica utilizzati in symfony, quello usato per i nomi di classi e variabili è l' UpperCamelCase. Ci sono 2 eccezioni: le classi del core cominciano con sf, in minuscolo, e le variabili utilizzate nelle template usano nella sintassi la separazione con underscore (_).

Organizzazione del codice

Ora che conosci i vari componenti di un'applicazione symfony, ti starai probabilmente chiedendo come sono organizzati. symfony organizza il codice secondo una struttura standard.

Struttura del progetto: Applicazioni, Moduli e Azioni

In symfony, un progetto è un insieme di servizi ed operazioni disponibili ad un dato nome a dominio, che condividono lo stesso modello.

All'interno di un progetto, le operazioni sono raggruppate logicamente in applicazioni. Normalmente un'applicazione può girare indipendentemente dalle altre applicazioni appartenenti allo stesso progetto. Nella maggior parte dei casi, un progetto conterrà due applicazioni: una per il front-office ed una per il back-office, che condividono lo stesso database. Ma si potrebbe anche avere un progetto contenente diversi mini-siti, con ogni sito identificato da una diversa applicazione. Nota che gli hyperlink fra diverse applicazioni devono essere nella notazione assoluta.

Ogni applicazione è un insieme di uno o più moduli. Un modulo solitamente rappresenta una pagina od un gruppo di pagine con uno scopo comune. Per esempio, potresti avere i moduli home, articles, help, shoppingCart, account e così via.

I moduli contengono azioni, che rappresentano appunto le azioni possibili all'interno del modulo. Ad esempio, un modulo {{{shoppingCart}}} potrebbe avere le azioni {{{add}}}, {{{update}}} e {{{show}}}. Generalmente, le azioni possono essere descritte da un verbo. Gestire queste azioni è quasi come gestire pagine in una applicazione web classica, sebbene due azioni potrebbero sfociare nella stessa pagina (ad esempio, aggiungere un commento ad un post in un weblog visualizzerà di nuovo il post, con il nuovo commento aggiunto).

TIP Se questo rappresentasse troppi livelli per un nuovo progetto, sarebbe molto facile raggruppare tutte le azioni all'interno di un singolo modulo, in modo da semplificare la struttura dei file. Quando la complessità dell'applicazione aumenterà, sarà allora il momento di riorganizzare le azioni in moduli separati. Come visto nel Capitolo 1, la riscrittura del codice migliora la sua leggibilità o la sua struttura (ma ne mantiene il comportamento), viene chiamata refactoring e tu la utilizzerai molto quando applicherai una tecnica di programmazione RAD.

La Figura 2-3 mostra una semplice organizzazione del codice per un weblog, secondo la struttura progetto/applicazione/modulo/azione. Fai attenzione al fatto che l'effettiva struttura dei file del progetto sarà differente.

Figura 2-3 - Esempio di organizzazione del codice

Esempio di organizzazione del codice

Struttura ad albero dei file

Tutti i progetti web generalmente condividono lo stesso tipo di componenti, come:

  • Un database, tipo MySQL o PostreSQL
  • File statici (HTML, immagini, Javascript, style sheet e così via)
  • File caricati dagli utenti ed amministratori del sito (qualora necessari)
  • Classi e librerie PHP
  • Librerie esterne (script di terze parti)
  • File batch (script da lanciare tramite shell o via cron)
  • File di log (scritti dall'applicazione e/o dal web server)
  • File di configurazione

Symfony fornisce un albero dei file standard per organizzare tutto ciò in modo logico, e consistentemente con l'architettura scelta (pattern MVC e raggruppamento in progetto/applicazione/modulo). Questa struttura ad albero è creata automaticamente ogni qualvolta si inizializza un nuovo progetto, applicazione o modulo. Ovviamente può essere personalizzata completamente, per organizzare i file e le cartelle secondo la propria logica o secondo le richieste del cliente.

Struttura della root

Sulla root di symfony ci sono le seguenti cartelle:

apps/
  frontend/
  backend/
cache/
config/
data/
  sql/
doc/
lib/
  model/
log/
plugins/
test/
  bootstrap/
  unit/
  functional/
web/
  css/
  images/
  js/
  uploads/

La Tabella 2-1 descrive il contenuto di tali cartelle.

Tabella 2-1 - Cartelle di root

Cartella Descrizione
`apps/` Contiene una cartella per ogni applicazione del progetto (gereralmente, `frontend` e `backend` per il front ed il back office)
`cache/` Contiene la versione cache della configurazione, e (se attivata) la versione cache di actions e template del progetto. Il meccanismo di cache (spiegato nel Capitolo 12) utilizza questi file per velocizzare le risposte alle richieste web. Ogni applicazione avrà qui una propria sottocartella, contenente i file precompilati PHP e HTML
`config/` Contiene la configurazione generale del progetto
`data/` Qui potrai archiviare i file di dati del tuo progetto, come schemi di database, file SQL per creare le tabelle, oppure un file SQLite
`doc/ Contiene la documentazione del progetto, inclusa la nostra documentazione e quella generata da PHPdoc
`lib/` Dedicata a classi esterne o librerie. Qui, puoi aggiungere il codice di cui hai bisogno condividendolo tra tutte le applicazioni. La sottocartella `model/` contiene l'object model del progetto (descritto nel Capitolo 8)
`log/` Contiene i log files generati direttamente da symfony. Può anche contenere i file di log del web server e database, o i file di log di qualsiasi parte del progetto. Symfony crea un file di log per ogni applicazione ed ambiente (i log verranno spiegati nel Capitolo 16)
`plugins/` Contiene i plugin installati nell'applicazione (i plugin verranno spiegati nel Capitolo 17)
`test/` Contiene test unitari e funzionali scritti in PHP e compatibili con il framework di symfony (discusso nel Capitolo 15). Durante il setup del progetto, symfony aggiunge automaticamente piccole porzioni di codice con alcuni test di base
`web/ La root del web server. Il questa cartella sono presenti tutti i file accessibili da Internet

Struttura dell'applicazione

La struttura dei file di ogni applicazione è la stessa:

apps/
  [application name]/
    config/
    i18n/
    lib/
    modules/
    templates/
      layout.php

La Tabella 2-2 ne descrive le sottocartelle.

Tabella 2-2 - Sottocartelle della cartella applicazione

Cartella Descrizione
`config/` Contiene un ricco insieme di file di configurazione YAML. Qui è dove risiede la maggior parte della configurazione dell'applicazione, a parte i parametri di default che possono essere trovati nel framework stesso. Tali parametri possono essere sovrascritti qui se necessario. Maggiori dettagli sulla configurazione dell'applicazione nel Capitolo 5
`i18n/` Contiene i file utilizzati per l'internazionalizzazione dell'applicazione; di solito i file di traduzione dell'interfaccia (Capitolo 13). Puoi non utilizzare questa cartella se deciderai di usare il database per l'internazionalizzazione
`lib/` Contiene classi e librerie specifiche per un'applicazione
`modules/ Contiente tutti i moduli di una applicazione
`templates/` Contiene i template globali dell'applicazione, condivisi da tutti i moduli. Di default, questa contiene un file `layout.php`, layout principale nel quale verranno inseriti i template dei moduli

NOTE Le cartelle i18n/, lib/ e modules/ sono vuote appena una nuova applicazione viene creata.

Le classi di un'applicazione non possono accedere metodi od attributi di altre applicazioni dello stesso progetto. Inoltre gli hyperlink fra due applicazioni devono essere espressi con la notazione assoluta. E' importante avere questi concetti in mente, quando durante la fasee di inizializzazione si decide di dividere un progetto in applicazioni.

Struttura dei moduli

Ogni applicazione contiene uno o più moduli. Ogni modulo ha la propria sottocartella dentro {{{modules}}}, ed il nome di tale cartella è scelto durante il setup.

Di seguito la struttura tipica di un modulo:

apps/
  [application name]/
    modules/
      [module name]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php

La tabella 2-3 ne descrive le sottocartelle.

Tabella 2-3 - Sottocartelle di un modulo

Cartella Descrizione
`actions/` Generalmente contiene un solo file chiamato `actions.class.php`, nel quale tu puoi archiviare tutte le azioni del modulo. Inoltre potrai scrivere azioni differenti su files separati
`config/` Può contenere i files di configurazione personalizzati con i parametri locali del modulo
`lib/` Contiene classi e librerie specifici per il modulo
`templates/` Contiene i templates corrispondenti alle azioni del modulo. Un template di default, chiamato`indexSuccess.php`, è creato durante il setup del modulo

NOTE Le cartelle config/ e lib/ sono vuote appena un nuovo modulo viene creato.

Struttura Web

Ci sono pochi vincoli per la cartella web, che è quella accessibile pubblicamente. Seguendo poche regole per la convenzione dei nomi, fornisce comportamenti di default ed utili scorciatoie nei template. Ecco un esempio di struttura della cartella web:

web/
  css/
  images/
  js/
  uploads/

Come convenzione, i file statici sono distribuiti nelle cartelle secondo la tabella 2-4.

Tabella 2-4 - Tipica cartella web

Cartella Descrizione
`css/` Contiene i fogli di stile con estensione `.css`
`images/` Contiene le immagini nei formati `.jpg`, `.png`, or `.gif`
`js/` Contiene i files JavaScript con estensione `.js`
`uploads/` Può contenere i file caricati dagli utenti. Anche se questa cartella conterrà delle immagini, è diversa dalla cartella `images` perché la sincronizzazione tra il server di sviluppo e quello di produzione non interessa le immagini caricate

NOTE Anche se è caldamente raccomandato mantenere la struttura di default, è possibile cambiarla per bisogni particolari, come ad esempio permettere al progetto di girare su un web server con diverse regole e convenzioni. Controlla il Capitolo 19 per maggiori informazioni su come cambiare la struttura delle cartelle di default.

Strumenti comuni

Ci sono alcune tecniche usate spesso in symfony, e altrettanto spesso le vedrai in questo libro e le utilizzerai nei tuoi progetti. Fra queste ci sono gestione dei parametri, costanti e auto caricamento delle classi.

Gestione dei parametri

La maggior parte delle classi di symfony contengono una gestione dei parametri. E' un modo molto conveniente di incapsulare attributi con setter e getter puliti. Ad esempio, la classe sfRequest contiene un gestore di parametri che possono essere richiamati tramite il metodo getParameterHolder. Ogni gestore di parametri memorizza i dati nello stesso modo, come si vede dal Listato 2-14.

Listato 2-14 - Utilizzare il gestore di parametri sfRequest

[php]
$request->getParameterHolder()->set('foo', 'bar');
echo $request->getParameterHolder()->get('foo');
 => 'bar'

La maggior parte delle classi che posseggono una gestione dei parametri utilizzano dei metodi proxy per fare in modo di accorciare le chiamate. Questo è il caso dell'oggetto sfRequest, perciò il codice del Listato 2-14 può essere riscritto anche come:

Listato 2-15 - Utilizzare il gestore di parametri sfRequest tramite proxy

[php]
$request->setParameter('foo', 'bar');
echo $request->getParameter('foo');
 => 'bar'

Il gestore di parametri in get accetta un valore di default come secondo argomento. Questo fornisce un meccanismo di fallback molto più conciso di uno statement condizionale tradizionale. Un esempio è riportato nel Listato 2-16.

Listato 2-16 - Utilizzare un argomento di default per il gestore di get

[php]
// The 'foobar' parameter is not defined, so the getter returns an empty value
echo $request->getParameter('foobar');
 => null

// A default value can be used by putting the getter in a condition
if ($request->hasParameter('foobar'))
{
  echo $request->getParameter('foobar');
}
else
{
  echo 'default';
}
 => default

// But it is much faster to use the second getter argument for that
echo $request->getParameter('foobar', 'default');
 => default

Alcune classi chiave di symfony usano a loro volta un gestore di parametri che supporta i namespaces (grazie alla classe sfNamespacedParameterHolder). Se si specifica un terzo argomento per un setter o un getter, esso viene usato come namespace, e il parametro sarà definito solo all'interno di quel namespace. I listato 2-17 mostra un esempio.

Listato 2-17 - Utilizzo di namespace con sfUser

[php]
$user->setAttribute('foo', 'bar1');
$user->setAttribute('foo', 'bar2', 'my/name/space');
echo $user->getAttribute('foo');
 => 'bar1'
echo $user->getAttribute('foo', null, 'my/name/space');
 => 'bar2'

Ovviamente potresti scrivere un gestore di parametri per la tua classe, per avvantaggiarti della sua semplice sintassi. Il Listato 2-18 mostra come definire una classe con un gestore di parametri.

Listato 2-18 - Aggiungere un gestore di paramteri ad una classe

[php]
class MyClass
{
  protected $parameterHolder = null;

  public function initialize ($parameters = array())
  {
    $this->parameterHolder = new sfParameterHolder();
    $this->parameterHolder->add($parameters);
  }

  public function getParameterHolder()
  {
    return $this->parameterHolder;
  }
}

Costanti

Sorprendentemente, non troverai nessuna costante in symfony. Questo è dovuto al fatto che le costanti hanno un grande svantaggio in PHP: non se ne può cambiare il valore dopo che è stato definito. Perciò symfony utilizza un proprio oggetto di configurazione, sfConfig, che rappresenta le costanti. Esso fornisce metodi statici per accedere a parametri da qualsiasi punto. Il Listato 2-19 mostra l'utilizzo della classe sfConfig.

Listato 2-20 - Utilizzo della classee sfConfig invece di costanti

[php]
// Invece delle costanti PHP,
define('PIPPO', 'pluto');
echo PIPPO;
// symfony usa l'oggetto sfConfig
sfConfig::set('sf_pippo', 'pluto');
echo sfConfig::get('sf_pippo');

Il metodo sfConfig supporta valori di default, ed è sufficiente chiamare il metodo sfConfig::set() più di una volta sullo stesso parametro per cambiarne il valore. Il Capitolo 5 illustra i metodi sfConfig dettagliatamente.

Auto caricamento delle classi

Classicamente, quando devi utilizzare un metodo di una classe od un oggetto in PHP, devi prima includerne la definizione.

[php]
include 'classes/MyClass.php';
$myObject = new MyClass();

Ma in progetti di grandi dimensioni, magari con strutture delle cartelle molto profonde, richiede molto tempo tenere traccia di tutte le classi ed i loro percorsi fisici per poterle includere. Fornendo una funzione spl_autoload_register(), symfony rende non necessario l'utilizzo di include, e si può scrivere direttamente:

[php]
$myObject = new MyClass();

In questo modo symfony cercherà all'interno delle cartelle lib/ del progetto tutti i file con estensione .php, fino a trovarne uno con la definizione della classe MyClass. Se viene trovata, viene inclusa automaticamente. Così, se memorizzi le tue classi all'interno delle cartelle lib/, non avrai più bisogno di includerle; questo è il motivo per cui solitamente i progetti symfony non utilizzano include o require.

NOTE Per migliori performance, l'auto caricamento di symfony controlla una lista di cartelle (definita in un file di configurazione interno) durante la prima richiesta. Quindi registra tutte le classi trovate in queste cartelle, e memorizza come array associativo tutte le corrispondenze file/classe in un file PHP. In questo modo, per ogni richiesta futura non c'è bisogno di ricontrollare le cartelle. Per questo motivo avrai bisogno di pulire la cache ogni volta che sposterai o aggiungerai un file di una classe, chiamando il comando {{{symfony cache:clear}}} (tranne nell'ambiente di sviluppo, in cui symfony pulisce la cache ogni volta che non trova una classe). Maggiori dettagli sulla cache saranno mostrati nel Capitolo 12, e sulla configurazione dell'auto caricamento nel Capitolo 19.

Riepilogo

Utilizzare un framework MVC ti obbliga a suddividere ed organizzare il codice secondo le convenzioni del framework stesso. Il codice di presentazione va nella vista, la manipolazione dei dati nel modello, e la logica della gestione delle richieste nel controller. Questo fa si che l'applicazione del pattern MVC sia allo stesso tempo molto utile ma anche molto restrittiva.

Symfony è un framework MVC scritto in PHP 5. La sua struttura è stata pensata per ottenere il meglio dal pattern MVC, ma con grande facilità d'uso. Grazie alla sua versatilità e configurabilità, symfony è adeguato a qualsiasi progetto web.

Ora che hai compreso la teoria alla base di symfony, sei quasi pronto per sviluppare la tua prima applicazione. Ma prima di poter fare ciò, hai bisogno di avere symfony funzionante sul tuo server di sviluppo.