Development

Documentation/fr_FR/book/1.0/trunk/02-Exploring-Symfony-s-Code

You must first sign up to be able to contribute.

Version 12 (modified by forresst, 9 years ago)
Respect mardown, comparaison avec l'original et correctifs en tout genre

Cette partie de la documentation est en cours de traduction. Cela signifie qu'elle est traduite de manière soit incomplète, soit inexacte. En attendant que cette traduction soit terminée, vous pouvez consulter la version en anglais pour des informations plus fiables.



Chapitre 2 – L'exploration du code de Symfony

Au premier regard le code de Symfony peut sembler déroutant. Il est réparti sur plusieurs répertoires, comporte beaucoup de scripts comme des classes PHP ou du HTML, et quelque fois un mélange des deux. Il y est aussi fait référence à des classes que vous aurez du mal à retrouver dans cette arborescence étendue sur six niveaux. Mais une fois compris le pourquoi du comment de cette apparente complexité, tout vous semblera tellement naturel que vous n’aurez pas envie d’échanger votre symfony pour autre chose (pas même contre un baril d’Ariel). Ce chapitre a pour but de détailler le mécanisme de symfony et de vous ôter vos dernières peurs.

Le modèle MVC

Symfony est basé sur le modèle classique de conception web connu sous le nom architecture MVC (Modèle – Vue – Contrôleur), composé de trois parties :

  • Le Modèle, qui représente le comportement de l’application, sa logique de métier.
  • La Vue, qui représente l’interface web proprement dite.
  • Le Contrôleur, qui prend en charge la gestion des évènements et commande la mise à jour de la Vue ou du Modèle.

Le schéma 2-1 illustre l’architecture MVC

Au sein de l’architecture MVC la logique de métier (modèle) est séparée de la présentation (vue). La maintenance en est donc simplifiée. Par exemple, si votre application doit être déployée sur plusieurs navigateurs ainsi que sur des appareils mobiles, vous aurez besoin de plusieurs vues (plusieurs présentations) mais pourrez conserver le même contrôleur et le même modèle (une seule logique de métier). Le contrôleur rend l’utilisation des différents protocoles utilisés (http, email, etc… ) complètement transparente pour le modèle et la vue. Le modèle gère la logique de donnée rendant, par exemple, la vue et le contrôleur indépendants du type de base de données utilisé pour l’application.

*Schéma 2-1 L'architecture MVC*

Le modèle MVC

L’architecture MVC

Pour mieux comprendre les avantages du modèle MVC, nous allons voir comment convertir une application PHP standard en une application basée sur l’architecture MVC. Pour cela, nous étudierons une liste de messages (posts) d’un weblog.

La programmation standard

En programmation PHP standard, l’affichage d’une liste d’éléments d’une base de données pourrait ressembler au listing 2-1

Listing 2-1 - Un script standard

[php]
<?php

// Connection , sélection de la base de données
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// Exécution de la requête
$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
// Affichage des résultats en 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

// Fermeture de la connexion
mysql_close($link);

?>

C’est rapide à écrire et à exécuter mais difficile à maintenir. Les principaux problèmes de ce code sont :

  • Pas de contrôle d’erreur (que se passe-t-il si la connexion échoue ?).
  • La syntaxe s’appuie sur du PHP et du HTML entremêlés.
  • Le code n’est adapté qu’à la base de données MySQL.

Isoler la présentation

Sur le listing 2-1, l’utilisation des ordres echo et printf complexifie la lisibilité du code et tout changement de présentation, via une modification du source HTML, devient périlleux. Par conséquent nous diviserons ce script en deux parties. La première contiendra le code PHP et la logique de métier qui seront déplacés dans le script du contrôleur, comme indiqué dans l’exemple 2-2

Exemple 2-2 La partie contrôleur dans index.php

[php]
<?php

 // Connexion, sélection de la base de données
 $link = mysql_connect('localhost', 'myuser', 'mypassword');
 mysql_select_db('blog_db', $link);

 // Excécution de la requête sql
 $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;
 }

 // Fermeture de la connection
 mysql_close($link);

 // Affichage du résultat
 require('view.php');

 ?>

Le code HTML, contenant la syntaxe PHP dédiée à la présentation, sera stocké dans le script de vue comme indiqué dans l’exemple 2-3.

Exemple 2-3 – La partie vue, dans 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>

Pour qu’un script de vue soit de bonne qualité, il doit contenir le moins de code PHP possible et aucun ordres PHP encapsulant des balises HTML de manière à être compréhensible par un graphiste démuni de connaissances en PHP. Les ordres les plus communs dans un script vue sont echo, if/endif, foreach/endforeach.

Toute la logique de traitement des données est placée dans le script Contrôleur et ne contient que la syntaxe PHP, sans aucun code HTML. Dans l’idéal, il faut pouvoir réutiliser le même contrôleur pour une présentation complètement différente comme une impression PDF ou un fichier XML.

Isoler la manipulation des données

La majeure partie du code d’un script contrôleur est dédiée à la manipulation des données. Mais que se passerait-il si vous aviez besoin de la même liste de messages pour un autre contrôleur, comme pour la gestion d’un flux RSS par exemple ? Comment faire si vous souhaitez conserver toutes les requêtes de base de données dans un même fichier pour éviter de dupliquer le code ? Que faire si vous décidez de changer la structure de la base de données et que la table post devient weblog_post ? N’allez-vous pas un jour migrer de PostgreSQL à MySQL ? Pour permettre toutes ces opérations nous allons devoir placer la manipulation des données dans un autre script appelé modèle (cf exemple 2-4).

Exemple 2-4 La partie modèle, dans model.php

[php]
<?php

function getAllPosts()
{
  // Connexion, sélection de la base de données
  $link = mysql_connect('localhost', 'myuser', 'mypassword');
  mysql_select_db('blog_db', $link);

  // Excécution de la requête sql
  $result = mysql_query('SELECT date, title FROM post', $link);

  // Remplissage du tableau
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }

  // Fermeture de la connection
  mysql_close($link);

  return $posts;
}

?>

L’exemple 2-5 montre le contrôleur modifié.

Exemple 2-5 la partie contrôleur, dans index.php

[php]
<?php

// Appel du modèle
require_once('model.php');

// Récupération de la liste des posts
$posts = getAllPosts();

// Appel de la vue
require('view.php');

?>

Le contrôleur devient plus facile à lire. Son principal rôle est de transférer les données du modèle à la vue. Au sein d’applications plus complexes, il s’occupe aussi des requêtes, des sessions utilisateurs, de l’authentification etc… L’emploi de noms de fonctions compréhensibles peut même rendre les commentaires du contrôleur obsolètes.

Le script model.php est dédié à l’accès aux bases et doit être pensé en ce sens. Toutes les informations qui ne sont pas propres aux bases (comme les paramètres des requêtes) doivent être reçues du contrôleur et non intégrées directement dans le modèle. De cette manière le modèle peut être réutilisé dans un autre contrôleur.

La séparation en couche au-delà de MVC

Pour résumer, le principe de l’architecture MVC est de répartir les codes sur trois niveaux : la logique de données est placée dans le modèle, la présentation dans la vue, et enfin la logique applicative dans le contrôleur.

Afin de simplifier encore le codage, il est possible d’aller plus loin. Ainsi le modèle, la vue et le contrôleur peuvent aussi être divisés.

L’abstraction de données

La couche modèle peut être elle-même divisée en deux sous-couches : le niveau accès aux données et le niveau abstraction de données. De la sorte la partie accès de données n’utilisera pas de requêtes dépendantes du SGBD, mais fera appel à des fonctions prévues à cet effet. Si la base de données vient à changer, seule la couche d’abstraction de données devra alors être adaptée.

Le listing 2-6 vous montre un exemple d’abstraction de données lié à MySQL. Le listing montre l’exemple d’accès de données associés.

Listing 2-6 L’abstraction de données du modèle

[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);
}

Listing 2-7 - Accès aux données du modèle

[php]
function getAllPosts()
{
  // Connection à la base de données
  $link = open_connection('localhost', 'myuser', 'mypassword');

  // Excécution de la requête sql
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);

  // Remplissage du tableau
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }

  // Fermeture de la connection
  close_connection($link);

  return $posts;
}

?>

On constate qu’aucune des fonctions dépendantes de la base de données ne se trouvent dans la partie accès de données. De plus, les fonctions de la partie abstraction de données peuvent être réutilisées par d’autres fonctions du modèle ayant besoin d’un accès à la base de données.

NOTE Quelques petites améliorations devraient encore être apportées aux exemples 2-6 et 2-7 pour qu’ils soient pleinement satisfaisants (indépendance du code SQL, mise en classes de toutes les fonctions etc …). Mais tel n’est pas le but de ce livre et dans le chapitre 8 vous découvrirez que Symfony gère très bien tous les types d’abstraction.

La partie Vue

La vue aussi peut faire l’objet d’une séparation de code. Une page contient souvent des éléments communs à toute l’application comme l’entête, le pied de page, la charte graphique, les principes de navigations etc… Seule le contenu de la page est appelé à changer. Ainsi on peut distinguer deux parties dans la vue : la maquette (layout) et le gabarit (template). La maquette est généralement globale à l’application ou à un groupe de pages et le gabarit met en forme les données fournies par le contrôleur. Une troisième partie est la vue logique (ou simplement vue) qui permet aux deux premières de travailler de concert. En appliquant ces principes, le listing 2-3 peut être séparé en trois parties comme le montrent les listings 2-8, 2-9 et 2-10

Listing 2-8 La partie gabarit de la vue, dans 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>

Listing 2-9 La vue logique de la vue

[php]
<?php

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

?>

Listing 2-10 La partie maquette de la vue

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

Action et contrôleur principal (front controller)

Dans les exemples précédents, le contrôleur ne fait pas grand chose, mais dans une application web réelle, il est sûrement celui qui a le plus de travail. De plus, la plupart des contrôleurs partagent en grande partie les mêmes actions comme la gestion des requêtes, la sécurité, la configuration de l’application etc… C’est pourquoi les contrôleurs sont plus souvent représentés par un contrôleur principal unique à l’application et par les actions, qui ne contiennent que le code contrôleur spécifique à certaines pages.

L'un des grands avantages d'un contrôleur frontal est qu'il offre un point d'entrée unique pour toute l'application. Si vous décidez de fermer l'accès à l'application, vous aurez seulement besoin de modifier le script de contrôleur frontal. Dans une application sans un contrôleur frontal, chaque contrôleur doit être désactivé.

Orienté objet.

Tous les exemples précédents s'appuient sur les principes de la programmation procédurale. Mais les possibilités de la programmation orientée objet (POO ou OOP) des langages modernes permettent de rendre les développements encore plus simples grâce à l’encapsulation, l’héritage et permettent, en outre, d’avoir des normes de codifications simples.

Implémenter une architecture avec un langage non objet augmente les risques de duplications de codes, de collisions de nom et d’une manière générale rend les sources plus difficiles à lire.

L’orientation objet permet aux développeurs de travailler avec les objets vue, contrôleur et les classes du modèle, et permet de transformer toutes les fonctions des exemples précédents (php standard) en méthodes. C'est un avantage considérable.

TIP Si vous voulez approfondir vos connaissances sur les motifs de conception pour le web en orienté objet, procurez-vous le livre Patterns of Entreprise Application Architecture de Martin Fowler (Addison-Wesley, ISBN : 0-32112-742-0). Les exemples sont en Java ou C#, mais restent compréhensibles pour un développeur PHP.

L’implémentation MVC de Symfony

Revenons à notre liste de messages. Combien de couches faut-il donc implémenter pour obtenir cette simple page ? Nous avons identifié les éléments illustrés par le schéma 2-2:

  • Niveau Modèle
    • Abstraction de données
    • Accès aux données
  • Niveau Vue
    • Vue (logique de vue)
    • Gabarit
    • La maquette
  • Le niveau Contrôleur
    • Le contrôleur principal
    • Les actions

Sept scripts ! Beaucoup de fichiers à ouvrir et à modifier pour chaque nouvelle page! Cependant, Symfony rend les choses faciles car tout en prenant le meilleur de l’architecture MVC, il l’implémente de manière à rendre le développement rapide et aisé.

Tout d’abord, le contrôleur principal et la maquette sont communs à toute l’application. Vous pouvez en avoir plusieurs mais un seul de chaque est nécessaire. Le contrôleur principal est un composant pur MVC, ce qui veut dire que vous n’aurez jamais à le coder, Symfony le générera pour vous.

L’autre bonne nouvelle est que les classes du niveau modèle sont aussi générées automatiquement en fonction de votre base de données et ce grâce à Propel. Cette librairie gère les clés étrangères et les champs de date, génère les accesseurs, etc facilitant encore plus la manipulation de données. De plus, l’abstraction de données est complètement transparente pour vous puisqu’entièrement gérée par une autre composant appelé Creole. Ainsi, si vous décidez de changer de SGBD, vous n’aurez pas de code à réécrire, mais juste à modifier un paramètre de la configuration.

Enfin, la logique de vue peut s’appuyer sur un simple fichier de configuration, sans avoir besoin de programmation.

Schéma 2-2 Le processus Symfony

Diagramme de flux de Symfony

Il en résulte que notre liste de messages ne requiert plus que trois fichiers pour fonctionner comme le montrent les listings 2-11, 2-12, 2-13.

Listing 2-11 Action de list de myproject/apps/myapp/modules/weblog/actions/actions.class.php

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

?>

Listing 2-12 Template de liste, de myproject/apps/myapp/modules/weblog/templates/listSuccess.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->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listing 2-13 Vue de liste, de myproject/apps/myapp/modules/weblog/config/view.yml

listSuccess:
  metas: { title: List of Posts }

De plus, vous allez aussi devoir définir une maquette, comme le montre le listing 2-14. Elle sera régulièrement réutilisée.

Listing 2-14 – Maquette de liste, dans myproject/apps/myapp/templates/layout.php

[php]
<html>
  <head>
    <?php echo include_title() ?>
  </head>
  <body>
    <?php echo $sf_data->getRaw('sf_content') ?>
  </body>
</html>

Voilà donc tout ce qui vous est nécessaire. Ce code a exactement le même effet que celui écrit en PHP standard vu plus haut sur le listing 2-1. Faire fonctionner tous les composants ensemble n'est pas de votre ressort et est géré pas Symfony. Si vous inspectez le code vous verrez que le développement de la liste de messages en architecture MVC ne requiert pas plus de temps, ni de code que l'écriture en PHP standard. Cependant, cela vous apporte de grands avantages comme une organisation plus claire du code, la possibilité de le réutiliser facilement, de la flexibilité, des possibilités de deboggage, une configuration aisée, l'abstraction de données, la réécriture d'URL, le multi-environnement et encore beaucoup d'outils de développement.

Le noyau de Symfony

L'implémentation MVC de Symfony utilise plusieurs classes que vous rencontrerez plusieurs fois dans ce livre :

  • sfController est la classe contrôleur. Elle interprète les requêtes et les transmet aux actions.
  • sfRequest conserve les éléments des requêtes comme les paramètres, les cookies, les entêtes, etc ...
  • sfResponse contient les entêtes et le contenu des réponses. C'est l'objet qui peut éventuellement être converti en HTML et renvoyé à l'utilisateur.
  • Le contexte (récupéré par sfContext::getInstance()) conserve une référence de chaque objet du noyau et de la configuration courante. Il est accessible de n'importe où.

Vous pourrez en apprendre plus sur ces objets au chapitre 6.

Comme vous pouvez le constater, les classes Symfony sont préfixées par sf comme le sont toutes les variables du noyau. Cela permet d'éviter les conflits de nom avec vos classes et variables, et rend les classes du noyau plus parlantes et faciles à reconnaître.

NOTE Dans Symfony, la méthode de codification UpperCamelCase est utilisée pour nommer les classes et variables. Deux exceptions à cela, les classes du noyau commençant par sf qui est en minuscule et les variables des gabarits qui utilisent la séparation par underscore.

L'organisation du code

Maintenant que vous connaissez les différents composants de Symfony vous vous demandez sûrement comment ils sont agencés. Symfony organise le code par projets et chaque projet possède une arborescence standard.

La structure des projets: applications, modules et actions

Avec Symfony, un projet est un ensemble de services et opérations réunis sous un nom de domaine donné, partageant le même modèle objet.

Au sein d'un projet, les opérations sont regroupées logiquement en applications. Une application peut normalement fonctionner indépendamment des autres applications du même projet. Dans la plupart des cas, un projet contiendra deux applications : le front-office et le back-office partageant les mêmes bases de données. Mais un même projet peut contenir plusieurs mini-sites, qui sont alors chacun une application différente. Dans ce cas les liens entre les applications doivent être des adresses absolues.

Chaque application est un ensemble d'un ou plusieurs modules. Un module correspond généralement à une page ou un groupe de pages traitant du même sujet. Par exemple, vous pouvez avoir les modules accueil, articles, aide, panier, compte etc ...

Les modules contiennent des actions qui peuvent être utilisées au sein de ce module. Par exemple, un panier peut comporter les actions ajouter, afficher, mettre à jour etc ... Généralement, les actions peuvent être décrites par un simple verbe. Une action peut être vue comme une page particulière d'une application web classique, à ceci près que deux actions peuvent avoir comme résultat la même page. (par exemple, ajouter une commentaire dans un message d'un weblog réaffiche ce message avec le nouveau commentaire).

TIP Si cette organisation est trop étoffée pour un premier projet, il est possible de regrouper toutes les actions dans un seul module. Quand l'application deviendra plus complexe, il sera encore temps d'organiser les actions dans des modules séparés. Comme indiqué dans le chapitre 1, réécrire le code pour améliorer son organisation ou sa lisibilité tout en conservant son comportement est appelé la refactorisation, et vous ferez souvent ça lorsque vous appliquerez les principes du RAD.

Le schéma 2-3 vous montre un exemple d'organisation de code pour un weblog avec une structure projet/application/module/action. Notez que l'arborescence réelle du projet différera de l'organisation montrée dans ce schéma.

Schéma 2-3 Exemple d'organisation de code

Exemple d'organisation du code

L'arborescence

La plupart des projets web partagent un certains nombres d'éléments comme :

  • une base de données comme MySQL ou PostgreSQL
  • des fichiers statiques (HTML, images, fichiers JavaScript, feuilles de styles etc ...)
  • des fichiers ajoutés par les utilisateurs ou administrateurs
  • des classes et librairies PHP
  • des librairies externes (scripts externes)
  • des fichiers batch (des scripts exécutés en ligne de commande ou via les tâches planifiées)
  • des fichiers journaux (traces laissées par une application et/ou le serveur)
  • des fichiers de configuration

Symfony fournit une arborescence standard de dossiers afin d’organiser le contenu de façon logique et conformément aux choix de l’architecture (modèles MVC regroupement projet/application/module). Cette arborescence est automatiquement créée à l’initialisation de chaque projet, application ou module. Naturellement, libre à vous de l’adapter à vos besoins ou à ceux des clients.

L’arborescence à la racine

Voici l’arborescence standard d’un projet Symfony :

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

Le tableau 2-1 décrit le contenu de ces répertoires

Tableau 2-1 Répertoires racines

Répertoire | Description ---------- | ------------ apps/ | Contient un répertoire par applications du projet (communément frontend et backend pour le front et le back office). batch/ | Contient les scripts PHP appelés par une ligne de commande ou un planificateur de tâches. cache/ | Contient le cache de la configuration et (si vous l’activez) le cache des actions et des gabarits du projet. Le cache (décrit au chapitre 12) utilise ces fichiers pour améliorer les temps de réponses des requêtes. Chaque applications y aura donc un sous-répertoire contenant des processus PHP ou HTML prétraité. config/ | Contient la configuration générale du projet. data/ | Tout ce qui est relatif aux bases de données est conservé ici : le schéma de la base de donné, les fichiers SQL de création etc … doc/ | Contient la documentation du projet y compris votre propre documentation et celle générée par PHPdoc. lib/ | Ce répertoire est réservé aux classes et librairies extérieures. Vous pouvez-y ajouter du code devant être partagé par toutes vos applications. Le répertoire model/ contient le modèle objet du projet (cf. Chapitre 8). log/ | Ici sont conservés les fichiers journaux générés par Symfony. Il peut aussi contenir les journaux du serveur web, des bases de données ou n’importe quels autres fichiers journaux du projet. Symfony crée un fichier journal par application et environnement (cf Chapitre 16). plugins/ | Ce répertoire contient les plugins installés pour cette application (cf Chapitre 17). test/ | Il contient des jeux d’essai unitaires et fonctionnels écrit en php et compatibles avec le framework de test de Symfony (cf Chapitre 15). web/ | La racine du serveur web. Seuls les fichiers se trouvant dans ce fichier sont accessible via Internet.

L’arborescence d’une application

L’arborescence d’une application est identique :

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

Le tableau 2-2 décrit les répertoires d’une application.

Tableau 2-2 Répertoires d’une application

Répertoire | Description ------------ | ----------- config/ | Contient l’ensemble des fichiers de configuration YAML. Le plus gros de la configuration de l’application se trouve ici mis à part les paramètres par défaut conservé au sein du framwork lui-même. Remarque : les paramètres par défaut peuvent être écrasés ici si nécessaire. Vous en apprendrez plus sur la configuration d’une application au chapitre 5. i18n/ | Contient les fichiers d’internationalisation de l’application, des fichiers de traduction de l’interface la plus part du temps. (Cf Chapitre 13) Vous pouvez vous passer de ces fichiers si vous gérez l’internationalisation pas la base de donnée. lib/ | Contient les librairies et les classes spécifiques à l’application. modules/ | Contient tous les modules propres à l’application. templates/ | Contient tous les gabarits de l’application, ceux partagé par tous les modules. Par défaut, il contient layout.php qui est la maquette dans laquelle tous les gabarits sont insérés.

NOTE Les répertoires i18n/, lib/ et modules/ sont vides pour une nouvelle application.

Les classes d’une application ne sont pas capables d’accéder aux méthodes ou attributs d’une tierce application d’un même projet. Lorsque vous déciderez de la manière de diviser votre projet en plusieurs applications, vous devrez garder à l’esprit que les liens hypertextes entre ces différentes applications devront être de forme absolue.

L’arborescence du module

Chaque application contient un ou plusieurs modules. Chaque module possède sa propre arborescence au sein du répertoire modules, et le nom du module est précisé au moment de l'installation.

Voici l’arborescence classique d’un module :

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

Le tableau 2-3 décrit les répertoires du module.

Tableau 2-3 Répertoires du module

Répertoire | Description ------------ | ------------ actions/ | En règle générale, ce répertoire possède ne contient que la classe actions.class.php qui regroupe toutes les actions du modules. Il est aussi possible d’écrire plusieurs actions dans des fichiers séparés config/ | Il peut contenir des fichiers de paramétrages locaux pour les modules lib/ | Contient les classes et librairies spécifiques au module template/ | Contient les gabarits correspondant aux actions du module. Un gabarit par défaut appelé indexSucces.php, est crée par défaut à l’installation du module validate/ | Dédié à la configuration des fichiers utilisés par le formulaire de validation (cf Chapitre 10)

NOTE Les dossiers config/, lib/ et validate/ sont vides à la création d’un nouveau module.

L’arborescence web

Le dossier web, qui regroupe tous les fichiers accessibles sur le net, n’a que peu de sous-répertoires. Le respect de conventions de nommage claires facilitera l’utilisation de ces dossiers dans les gabarits. Voici un exemple de l’arborescence d’un dossier web:

web/
  css/
  images/
  js/
  uploads/

Par convention, les fichiers statiques sont répartis dans les dossiers décrits par le tableau 2-4.

Tableau 2-4 – Arborescence Web classique

Répertoire | Description ---------- | ----------- css/ | Contient les feuilles de styles avec l’extension .css images/ | Contient les images aux formats .jpg, .png ou .gif js/ | Contient les fichiers JavaScript avec l’extension .js uploads/ | Doit contenir les images uploadées par l’utilisateur. Même si le dossier contient des images, il est bien distinct du dossier des images afin que la synchronisation des serveurs de développement et de production, n’affecte pas les images uploadées.

NOTE Même s’il est fortement conseillé de ne pas modifier l’arborescence par défaut, il est possible de le faire pour des besoins spécifiques, comme permettre à un projet de tourner sur un serveur à l’arborescence et aux conventions de codage différents. Cf Chapitre 19 pour plus d’information sur la modification d’arborescence.

Outils communs

Dans ce livre et dans vos projets, vous serez fréquemment confronté à certaines techniques très couramment utilisées au sein de Symfony comme les conteneurs de paramètres, les constantes et l’auto-chargement des classes

Le conteneur de paramètres

Beaucoup de classes Symfony possèdent un conteneur de paramètres. C’est un moyen simple d’encapsuler un attribut avec des méthodes getter et setter correctes. Par exemple, la classe sfResponse possède un conteneur de paramètres que vous pouvez utiliser en appelant la méthode getParameterHolder(). Chaque conteneur de paramètres conserve les données comme illustré par le listing 2-15.

Listing 2-15 Utilisation du conteneur de paramètre de sfResponse

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

La plupart des classes utilisant le conteneur de paramètres se servent de méthodes intermédiaires pour raccourcir le code saisi pour les opérations get/set. Dans le cas de l’objet sfResponse, l’opération décrite dans le listing 2-15 peut-être simplifiée comme le montre le listing 2-16

Listing 2-16 Utilisation des méthodes intermédiaires des conteneurs de paramètres de sfResponse

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

La méthode getter du conteneur de paramètres accepte une valeur par défaut comme second argument ce qui permet un échappement automatique bien plus léger qu’un équivalent réalisé par un codage conditionnel. Le listing 2-17 illustre ce point de vue.

Listing 2-17 – Utilisation de la valeur par défaut de l’attribut du getter

[php]
// Le paramètre 'foobar' n’est pas défini, donc le getter renvoie une valeur vide
echo $response->getParameter('foobar');
 => null

// Une valeur par défaut peut-être utilisée en ajoutant le getter dans la condition suivante
if ($response->hasParameter('foobar'))
{
  echo $response->getParameter('foobar');
}
else
{
  echo 'default';
}
 => default

// Mais il est plus rapide d’utiliser le deuxième argument du getter pour cela.
echo $response->getParameter('foobar', 'default');
 => default

Les conteneurs de paramètres peuvent aussi supporter les espaces de nommage. Si vous spécifiez un troisième paramètre, il sera interprété comme un espace de nommage et le paramètre sera alors défini uniquement dans cet espace. Le Listing 2-18 illustre ce point.

Listing 2-18 – Utilisation de l’espace de nommage du conteneur de paramètres de la méthode sfResponse

[php]
$response->setParameter('foo', 'bar1');
$response->setParameter('foo', 'bar2', 'my/name/space');
echo $response->getParameter('foo');
 => 'bar1'
echo $response->getParameter('foo', null, 'my/name/space');
 => 'bar2'

Vous pouvez bien évidemment ajouter un conteneur de paramètres dans vos propres classes pour profiter de cette fonctionnalité. Le listing 2-19 vous explique comment définir une classe avec un conteneur de paramètres.

Listing 2-19 Ajouter un conteneur de paramètres à une classe

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

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

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

Constantes

Étrangement vous ne trouverez que peu de constantes dans Symfony et ce pour le simple fait qu’il existe une contrainte majeure en PHP : une fois définie, leur valeur devient non modifiable. C’est pour cela que pour remplacer les constantes, Symfony utilise son propre objet de configuration : sfConfig. Grace à ses méthodes statiques, vous pouvez atteindre ce paramètre de n’importe où. Le listing 2-20 vous montre comment utiliser l’objet sfConfig.

Listing 2-20 Utilisation de l’objet sfConfig à la place des constantes

[php]
// A la place des constantes,
define('SF_FOO', 'bar');
echo SF_FOO;
// Symfony utilise l’objet `sfConfig`
sfConfig::set('sf_foo', 'bar');
echo sfConfig::get('sf_foo');

La méthode sfConfig accepte un paramètre par défaut et vous pouvez appeler plusieurs fois la méthode sfConfig::set() pour le même paramètre afin de modifier sa valeur. Vous trouverez plus d’informations sur la méthode sfConfig au chapitre 5.

Auto-chargement des classes

De façon standard, lorsque vous souhaitez utiliser une classe ou créer un objet en PHP, vous devez tout d’abord inclure explicitement la définition de cette classe

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

Mais au sein de projets volumineux possédant beaucoup de classes et une architecture complexe, se souvenir de tous les fichiers à inclure et de leur chemin devient fastidieux. En proposant la fonction __autoload() –ou la fonction spl_autoload_register()), Symfony rend obsolète l’emploi de l’ordre include. Vous pouvez alors directement écrire

[php]
$myObject = new MyClass();

Dès lors, Symfony recherche la définition de MyClass dans tous les fichiers d’extension .php présents dans l’un des répertoires lib/ du projet. S’il la trouve, elle est incluse automatiquement.

Par conséquent, si vous conservez toutes vos classes dans les répertoires lib, vous n’aurez jamais à inclure aucune classes. C’est exactement pourquoi les projets Symfony n’utilisent jamais les ordres include ou require.

NOTE Dans le but d’améliorer les performances, l’auto-chargement de Symfony parcourt une liste de répertoires (définis dans un fichier interne de configuration) à l’occasion de la première requête. Toutes les classes sont passées en revue par répertoire et l’association classe/fichier est sauvegardée dans un tableau associatif. De cette manière, les futurs requêtes n’auront pas besoin de refaire la vérification. Voilà aussi pourquoi vous devez vider le cache à chaque fois que vous ajoutez ou supprimez une classe dans votre projet en lançant la commande Symfony clear-cache. Vous en apprendrez plus sur le cache au chapitre 12 et la configuration de l’auto-chargement au chapitre 19.

Résumé

L’usage d’un framework basé sur MVC vous oblige à répartir et organiser votre code en accord avec certaines règles précises. Le code de présentation est placé dans la vue, le modèle hérite du code de manipulation de données, et toute la partie gestion des requêtes se retrouve au niveau du contrôleur. Ceci rend les applications s’appuyant sur l’architecture MVC à la fois plus simples à utiliser et plus contraignantes.

Symfony est un framework MVC écrit en PHP 5. Il tire le meilleur parti de l’architecture MVC tout en conservant une grande simplicité d’utilisation. Au regard de sa souplesse et de ses grandes possibilités de configuration, Symfony est adapté à tout type de projet web.

Maintenant que vous avez compris la théorie sous-jacente à Symfony, vous êtes presque prêt pour développer votre première application. Mais avant cela vous avez besoin d’installer et configurer Symfony sur un serveur de développement.