Résumé d'une discussion et notes sur ce que j'aurai aimé comprendre plus rapidement lors de mon apprentissage Symfony2 et Doctrine2

Tags

Durant les derniers mois j'ai lu et relu la documentation de Symfony2 et voici quelques éléments qui ont attiré mon attention et que j'aimerai partager avec vous.

L'un des concepts architecturaux clé faisant que Symfony2 se démarque des autres frameworks PHP est l'usage des concepts de la programmation orienté aspect (AOP).

L'idée directrice de la programmation orienté aspect est d'éviter au maximum dépendances fonctionnelles communes (appelé dépendances transversales). Ces fonctionnalités fréquentes comme l'accès a la base de donnée, par exemple. La manière de séparer ces besoins est de notamment utiliser les commentaires des classes et d'y ajouter des fonctionnalités avant ou apreès les méthodes. C'est ce qu'on appelle des Intercepteurs et des Pointcut.

Ce type d'architecture demande une capacité de compilation de code supporté par le langage de programmation.

Nous savons que PHP n'est pas un langage compilé. Il y a des alternatives, mais ce n'est pas comme ce qui peut être fait avec AspectJ et Spring AOP, pour Java, et ce qu'il existe pour le C#.

Pourtant, depuis peu, avec l'avènement de l'implémentation des normes PSR-x, et des librairies de génération de code. C'est maintenant possible.

Beaucoup de changements sont a venir dans le monde des CMS et pour beaucoup d'autres projets.

Le mouvement est tellement fort que les communautés de d'autres projets libres s'impliquent: Ce projet s'appelle le "PHP Framework Interoperability group" Il y a notamment le projet Magento, Drupal, Joomla!, MidgardTypo3 (Flow3), voir la liste des groupes et du draft sur github.

Donc, ces choses que j'ai beaucoup apprécié lors de ma découverte de Symfony2.

Conversion des entités et éviter requêtes à la base de donnée

Comme la documentation le décrit, le travail d'une méthode de contrôleur est de convertir la requête qui lui a été demandée d'exécuter (via le routing). L'utilisation des patterns AOP vient permettre de maigrir énormément.

Au lieu d'utiliser manuellement les Exception, le render, et les appels à Doctrine faits "à bras" dans le corps de la méthode comme suit.

namespace PSS\Bundle\BlogBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

/**
 * Blog Controller
 **/
class BlogController extends Controller
{

    /**
     * @Route("/{slug}")
     */
    public function showAction($slug)
    {
        $entityManager = $this->get('doctrine.orm.entity_manager');
        try {
            $post = $entityManager
                ->getRepository('PSS\Bundle\BlogBundle\Entity\Post')
                ->findPublishedPostOrPage($slug);
        } catch (\Doctrine\ORM\NoResultException $exception) {
            throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException('Page Not Found');

        }

        return $this->render('PSSBlogBundle:Blog:show.html.twig', array('post' => $post));
    }
}

On peut sauver des lignes de code et utiliser les @annotation pour définir le Template qui sera utilisé, va aller chercher l'entrée dans la base de donnée pour nous (via le @ParamConverter), retourner une Exception si elle existe pas.

Vous pouvez voir plus en détails l'implémentation dans Dans mon exemple pris de mon fork du bundle PSSBlogBundle qui sert a lire la base de donnée WordPress de mon site, et utiliser Symfony2/Doctrine2/Twig pour le devant de mon site (beta.renoirboulannger.com, à l'inverse de la version hébergée sur le domaine non "beta").

< ?php
namespace PSS\Bundle\BlogBundle\Controller;

// Symfony/Doctrine internal
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;

// Entities
use PSS\Bundle\BlogBundle\Entity\Post;
use PSS\Bundle\BlogBundle\Entity\Term;

/**
 * Base Blog Controller
 *
 * @Route("/blog")
 */
class BlogController extends Controller
{
    /**
     * @Route("/{year}/{month}/{slug}")
     * @Template()
     */
    public function showAction(Post $post, Request $request)
    {
        return array(
            'post' => $post->onlyIfPublished(),
            'comment_form' => $this->comment($request,$post)
        );
    }
}

Ok, toutes les lignes de codes sauvés sont devenues des lignes use au haut de la page. Mais tout ceci a le mérite d'être très explicite. La magie se passe principalement dans l'annotation @Route qui fait la relation avec l'entité Post et va ajuster la requête de recherche dans le PostRepository et limiter sa recherche au colones slug, year, et month. Ces colones peuvent ne pas exister mais simplement être des méthodes. C'est la magie de Doctrine2 et totalement hors du sujet de ce billet.

Autres annotations d'intérêt

@Route

Cette annotation permet de contrôler les requis que l'adresse doit remplir pour être utilisée. Elle nous permet de spécifier quelle propriétée est liée a l'entitée déclarée dans la déclaration de la méthode du contrôleur.

@Template

Le @Template annotation sur un controleur sert a remplacer les appels a render avec nom de template et va suivre la convention du nom logique, exemple: AcmeBlogBundle:Blog:show qui va aller chercher le fichier de template dans le dossier Acme/BlogBundle/Resources/Blog/show.html.twig.

Il est bien de remarquer ici que par défaut l'engin de templating est Twig et la sortie est HTML. Mais tout peut être configuré. Puis j'aimerai ajouter l'emphase que le système peut permettre de fournir les requis de sortie sous format JSON et nous n'aurions qu'a créer un fichier au même nom mais insérer json à la place de html, exemple:

Donc, plus tard dans le projet, il sera faccile d'ajuster l'attribut "_format" puis dire si header is xmlhttprequest accept`applcation/json, utilise le template au meme nom mais ...json.twig et avoir les memes controlleurs repondre html ET JSON :) la methode su controlleur qui a un @Template s'attend a cwe que tu donne un array cle valeur que tu peut utiliser dans ton template.

@Observe

Celle ci est ma découverte de la semaine avec @Inject. Au lieu de déclarer dans un fichier services.yml, il est possible de déclarer directement dans la méthode de la classe l'événement qu'elle est addressée à traiter.

On peut voir dans cette réponse sur StackOverflow comment on peut ajuster le TimeZone sans avoir a modifier d'autres fichiers dans le projet.

Mes autres références

Mon fil delicious delicious avec tags: symfony2 et very-helpful vous donnera ma sélection de références que j'ai apprécié.

Pour comprendre plus

Quelques autres références et billets qui m'ont inspiré et aidé a comprendre. Des billets inspirants: Outils à considérer