Symfony 2, comment ça fonctionne ?
Par Noël GUILBERT le dimanche, février 21 2010, 16:05 - Lien permanent

Attention : la version actuelle du Symfony 2 est une "preview" : les choses vont changer. Cette article est donc une découverte du framework tel qu'il a été présenté lors du Symfony Live 2010.
Lors du Symfony Live 2010, Fabien Potencier a révélé la toute nouvelle version du framework : Symfony 2 (avec un "S" majuscule la v2).
Pour l'occasion, j'ai pu essayer et mettre en place quelques briques de code, notamment une version adaptée du tutorial "Easy-Ajax" de symfony (avec un "s" minuscule pour les version 1.x).
Il est donc temps de vous raconter comment Symfony fonctionne. Dans ce billet, j'utiliserai le code de l'application Shopping Cart, disponible sur github : http://github.com/noelg/symfony-demo.
Structure
Avec symfony 1.x, la structure d'un projet est assez riche : apps, cache, data, config, lib, plugins, web. Avec Symfony 2, c'est beaucoup plus simple:
- app : le répertoire pour l'application, et le nom n'as pas d'importance. Par exemple, dans l'application "Shopping Cart", ce répertoire s'appelle "cart"
- src : l'ensemble du code de l'application
- web : le répertoire public du projet, avec la même fonction qu'avec symfony 1, il contient doc naturellement le(s) front-controller(s)
Le front controller
Dans l'application "Shopping Cart", nous trouvons deux front-controllers dans le répertoire web/ :
- index_dev.php
- index.php
Comme avec symfony 1, ils contiennent le code nécessaire pour initialiser le framework:
<?php
require_once __DIR__.'/../cart/CartKernel.php';
$kernel = new CartKernel('dev', true);
$kernel->run();
Le code est assez simple :
- On récupère le "kernel" de notre application Cart
- On instancie le susdit kernel, en lui passant deux paramètres : l'environnement (ici "dev"), et l'activation du mode débug (ici, "true")
- Enfin, l'application est lancée à l'appel de la méthode "run" du kernel
Le Kernel
Le Kernel, avec Symfony 2, est l'endroit où l'ensemble de l'application est configurée :
- autoloading
- enregistrement des bundles (nous y reviendrons plus tard)
- configuration de l'injection de dépendance et du routing
Autoloading
L'autoloading est simplement chargé grâce au fichier autoload.php prévu à cet effet :
<?php
require_once __DIR__.'/../src/autoload.php';
Ce fichier est lui-même assez simple à comprendre:
<?php
require_once __DIR__.'/vendor/symfony/src/Symfony/Foundation/UniversalClassLoader.php';
use Symfony\Foundation\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony' => __DIR__.'/vendor/symfony/src',
'Application' => __DIR__,
'Bundle' => __DIR__,
'Doctrine' => __DIR__.'/vendor/doctrine/lib',
));
$loader->registerPrefixes(array(
'Swift_' => __DIR__.'/vendor/swiftmailer/lib/classes',
'Zend_' => __DIR__.'/vendor/zend/library',
));
$loader->register();
// for Zend Framework & SwiftMailer
set_include_path(__DIR__.'/vendor/zend/library'.PATH_SEPARATOR.__DIR__.'/vendor/swiftmailer/lib'.PATH_SEPARATOR.get_include_path());
On utilise ici la classe UniversalClassLoader, qui est une implémentation permettant de charger les classes respectant:
- les standards d'interopérabilité de PHP 5.3 http://groups.google.com/group/php-standards/web/final-proposal
- Les conventions de nommage PEAR
Deux méthodes sont appellées :
- l'enregistrement des namespaces, avec registerNamespace()
- L'enregistrement des prefix PEAR, avec registerPrefixes()
Si vous voulez ajouter des bibliothèques, vous devrez sans doute modifier ce fichier.
L'enregistrement des bundles
Dans Symfony 2, un bundle est l'équivalent de "plugin" avec symfony 1. Les bundles utilisés par l'application sont définis par la méthode registerBundles():
<?php
/* ... */
class CartKernel extends Kernel
{
/* ... */
public function registerBundles()
{
return array(
new Symfony\Foundation\Bundle\KernelBundle(),
new Symfony\Framework\WebBundle\Bundle(),
new Symfony\Framework\ZendBundle\Bundle(),
new Symfony\Framework\SwiftmailerBundle\Bundle(),
new Symfony\Framework\DoctrineBundle\Bundle(),
new Bundle\CartBundle\Bundle(),
);
}
}
Dans cette application, plusieurs bundles sont utilisés:
- les bundles du framework
- le bundle CartBundle, qui est le code de l'application "ShoppingCart"
Configuration de l'injecteur de dépendance et du routing
Deux autres méthodes sont définies pour configurer le routing et l'injecteur de dépendances :
public function registerContainerConfiguration()
{
$loader = new ContainerLoader($this->getBundleDirs());
return $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}
public function registerRoutes()
{
$loader = new RoutingLoader($this->getBundleDirs());
return $loader->load(__DIR__.'/config/routing.yml');
}
Encore une fois, le code est facile à comprendre, il n'y a rien de magique : on charge les fichiers config_*.yml et routing.yml. Et comme leur noms l'indiquent, ils permettent de configurer l'application et le routing.
config.yml
kernel.config: ~
web.web: ~
web.templating: ~
web.user:
class: Bundle\CartBundle\User
Avec symfony 1, les fichiers de configuration sont plus nombreux (settings.yml, app.yml, factories.yml, filters.yml, etc), et nécessitent un peu de connaissance dès lors qu'on souhaite les customiser. Avec Symfony 2, un seul fichier : config.yml.
routing.yml
homepage:
pattern: /
defaults: { _bundle: WebBundle, _controller: Default, _action: index }
cart:
resource: CartBundle/Resources/config/routing.xml
Le fichier routing.yml est similaire à ce que l'on trouve dans symfony 1. La seule différence est dans le nommage des paramètres. Une fonctionnalité supplémentaire est de pouvoir charger un fichier de routing externe. C'est le cas ici, où l'on charge le fichier de routing du bundle CartBundle.
Les bundles
Un bundle, je l'ai dis plus tôt, est un "plugin". La différence majeure avec symfony 1, c'est qu'ils ont une place de premier choix dans Symfony 2 : tout est bundle, et tout doit être dans un implémenté dans un bundle. Plusieurs types de bundles sont possibles :
- les bundles du framework : le code du framework
- les bundles externes : typiquement, un brique logicielle autonome, et réutilisable dans différents projets
- les bundles privés : une brique logicielle exclusivement développée pour une application
Structure
La structure d'un bundle est la suivante:
- Bundle.php : la classe d'initialisation
- Controller/ : le répertoire pour les contrôleurs
- Model/ : le répertoire des classes du modèle
- Resources/ : les ressources du bundle, à savoir la configuration, les templates et les ressources publiques (images, css, js, etc.)
Configuration
Rappelez-vous, les bundles sont initialisés dans le kernel :
<?php
/* ... */
class CartKernel extends Kernel
{
/* ... */
public function registerBundles()
{
return array(
/* ... */
new Bundle\CartBundle\Bundle(),
);
}
}
Avec Symfony 2, tout est explicite. La configuration du bundle se fait donc dans cette classe. Par exemple, le bundle "WebBundle" de Symfony implémente une méthode buildContainer() pour étendre l'injecteur de dépendance :
# src/vendor/symfony/src/Symfony/Framework/WebBundle/Bundle.php
class Bundle extends BaseBundle
{
public function buildContainer(ContainerInterface $container)
{
Loader::registerExtension(new WebExtension());
$dirs = array('%kernel.root_dir%/views/%%bundle%%/%%controller%%/%%name%%%%format%%.php');
foreach ($container->getParameter('kernel.bundle_dirs') as $dir)
{
$dirs[] = $dir.'/%%bundle%%/Resources/views/%%controller%%/%%name%%%%format%%.php';
}
$container->setParameter('templating.loader.filesystem.path', $dirs);
}
}
MVC
Symfony est un framework MVC (enfin, presque). On trouve donc plusieurs éléments :
- Le modèle
- Les contrôleurs
- les vues
Les contrôleurs
Les contrôleurs doivent être placés das le répertoire Controllers du bundle, et les méthodes d'action doivent être suffixées par "Action". Avec symfony 1, elles sont préfixées par "execute".
Voici un extrait du contrôleur CartController du bundle CartBundle:
# src/Bundle/CartBundle/Controller/CartController.php
namespace Bundle\CartBundle\Controller;
use Symfony\Framework\WebBundle\Controller;
use Bundle\CartBundle\Model\Product;
class CartController extends Controller
{
public function indexAction()
{
return $this->render('CartBundle:Cart:index', array(
'products' => Product::findAll(),
'cart' => $this->getUser()->getCart()
));
}
/* ... */
}
Les ressources
Dans le répertoire "Resources" d'un bundle, on peut avoir plusieurs répertoires:
- config/ : la configuration du bundle. Dans CartBundle, il y a la configuration du routing (routing.xml)
- views/ : les templates associés aux contrôleurs
- public : les ressources publiques du bundle : CSS, JS et images.
Conclusion
Une attention particulière a été apporté sur la facilité d'approche du framework. Pour cela, beaucoup de choses "magiques" avec symfony 1, sont désormais explicites : l'appréhension d'une application Symfony est donc beaucoup plus facile. De plus, grâce à ce tutorial, vous pourrez apprendre à l'utiliser en quelques dizaines de minutes. Je vous invite également à jeter un oeil au code de l'application Shopping Cart.


Commentaires
Symfony 2 Internals ? :-)
@Geoffrey: Presque :D
Yeah, ça commence les articles sur Symfony 2 :)
Merci Noël, grâce a toi j'ai compris comme fonctionne Symfony 2 sans même telecharger le framework.
Mettre / écrire " Symfony2 "... à la place de " Symfony 2 "
Explications: http://tinyurl.com/38wugpw
... et l'article ne sera que mieux référencé. ;-)