Jusqu’à symfony 1.2, si vous vouliez ajouter facilement de nouvelles fonctionnalités au framework PHP5 il fallait se diriger vers son importante base de plugins. Avec symfony 1.3 et 1.4, bien sûr vous pouvez toujours compter sur les plugins symfony mais viennent maintenant s’ajouter les extensions pour Doctrine

Si vous utilisez Doctrine comme Object Relational Mapper (ORM), vous connaissez certainement les behaviours Timestampable, Sluggable ou encore SoftDelete. Les extensions Doctrine vous permettent justement d’en ajouter de nouveaux très facilement, en quelques clics ou lignes de commande.

Aujourd’hui, je vous propose d’installer et d’utiliser l’extension Taggable. Cette dernière permet d’ajouter des tags sur les modèles de votre choix. L’intérêt peut être de faire des recommandations ou retrouver des éléments relatifs à un autre.

Commençons d’abord par installer l’extension Taggable :

  1. Placez-vous dans le dossier racine de votre projet symfony.
    cd /chemin/vers/le/projet/symfony/
  2. Créez un dossier doctrine_extension dans lib.
    mkdir lib/doctrine_extension
  3. Téléchargez l’extension Taggable avec subversion.
    svn co http://svn.doctrine-project.org/extensions/Taggable/branches/1.2-1.0 lib/doctrine_extension/Taggable
  4. Videz le cache symfony (peut-être plus nécessaire mais c’est encore un réflexe chez moi).
    php symfony cc
  5. Activez l’extension Doctrine :
    • Éditez le fichier config/ProjectConfiguration.class.php
    • Si vous ne l’avez pas encore, créez la méthode configureDoctrine
        public function configureDoctrine(Doctrine_Manager $manager)
        {
        }
    • Ajoutez le code ci-dessous dans la méthode.
          Doctrine::setExtensionsPath(sfConfig::get('sf_lib_dir').'/doctrine_extension');
          $manager->registerExtension('Taggable');

Voilà l’extension avec maintenant installée et activée. Voyons maintenant, comment l’utiliser. Pour cela je vais prendre un exemple très simple avec des articles qui possèdent des tags.

  1. Définissez le modèle Article en copiant le contenu ci-dessous dans un fichier config/doctrine/10_articles.yml
    Article:
      actAs:
        Taggable:
      columns:
        title:          { type: string(255), notnull: true, notblank: true }
        body:           { type: clob, notnull: true, notblank: true }
    
  2. Ajoutez des fixtures en copiant le contenu ci-dessous dans un fichier data/fixtures/10_articles.yml
    Article:
      Article_1:
        title: Mon premier article
        body: |
          Hello World !
          Ceci est mon premier article.
        tags: hello, world, article 1
    
      Article_2:
        title: Mon second article
        body: |
          Hello World !
          Ceci est mon second article.
        tags: hello, world, article 2
    
  3. Créez les fichiers php associés au modèle et chargez les fixtures avec la commande suivante :
    php symfony doctrine:build --all --and-load
  4. Créez un module Article dans l’application frontend
    php symfony generate:module frontend article
  5. Éditez le fichier apps/frontend/modules/article/actions.class.php et remplacez le code de la méthode executeIndex par le code ci-dessous
        $this->articles = Doctrine::getTable('Article')->findAll();
        $this->popular_tags = Doctrine::getTable('TaggableTag')->getPopularTags();
  6. Éditez le fichier apps/frontend/modules/article/indexSuccess.php et collez le code ci-dessous
    <h1>Articles</h1>
    
    <h2>Liste des articles</h2>
    <?php foreach ($articles as $article): ?>
      <h3><?php echo $article->title ?></h3>
      <p><?php echo nl2br($article->body) ?></p>
      <p>Tags : <?php echo $article->getTagsString() ?></p>
    <?php endforeach ?>
    
    <h2>Tags populaires</h2>
    <ul>
    <?php foreach ($popular_tags as $tag): ?>
      <li><?php echo $tag->name ?> (<?php echo $tag->total_num ?>)</li>
    <?php endforeach ?>
    </ul>

Si vous avez utilisez la sandbox symfony 1.4 pour suivre ce tutoriel, vous ne devriez pas avoir de problème. Toutefois, il existe quelques subtilités dans l’utilisation de l’extension Taggable que j’ai rencontré lors de son utilisation dans un projet réel. Je les mets ci-dessous, si vous en avez d’autres n’hésitez pas à le signaler dans les commentaires.

  • Pour utiliser la table TaggableTag vous devez obligatoirement avoir chargé une autre table utilisant l’extension. Dans mon exemple, si vous inversez les deux lignes de la méthode executeIndex, vous obtiendrez une belle exception « Couldn’t find class TaggableTag ».
  • Si vous essayez de supprimer un élément « Taggable », une exception sera levée. En effet, les tags associés à l’objet doivent être supprimés avant l’objet lui-même. Vous pouvez soit utiliser l’évènement preDelete pour supprimer les tags avant l’objet soit appliquer le patch que j’ai proposé dans l’issue tracker de Doctrine pour ajouter un onDelete CASCADE sur la relation.
  • Les relations many-to-many entre votre modèle « Taggable » et les tags sont ajoutées à la volée, il faut donc « charger » le modèle avant l’utilisation d’une de ces relations. Ainsi, si vous voulez afficher un nuage de tags avec la méthode getPopularTags, il faudra charger chaque modèle « Taggable » où alors vous n’aurez que les tags populaires des modèles déjà chargés.

En conclusion, l’extension Taggable est très intéressante mais il subsiste quelques défauts qui peuvent bloquer son utilisation dans un projet concret. Si j’ai réussi à corriger les problèmes 1 et 2, le 3e reste non résolu pour le moment. La solution serait d’ajouter les relations dans les fichiers php générés par Doctrine lors de la création des modèles. Pas sûr que ce soit possible dans l’état actuel…


10 commentaires

fch · 12 janvier 2010 à 09:25

Compliqué, tout cela, compliqué, entre la logique symfony/doctrine, le yml, le cache, et les relations dans la base de données qui ne sont même pas géré par doctrine (c’est bien la peine de faire une telle usine à gaz).
J’avais regardé en détail doctrine et propel lors de la sortie de leur première version.
j’avais hurlé à l’époque, et je vois que cela serait toujours le cas si je remettais le nez la-dedans.
Et pendant ce temps, tout le monde se plaint, voir s’étonne, de problèmes de performances et de difficulté à monter en charge avec ce genre d’outil…

Hugo · 12 janvier 2010 à 10:34

@fch: Sur quelles données quantifiables te reposes-tu pour dire que les applications sous symfony (ou autre framework) ne tiennent pas la charge ? As-tu déjà essayé de développer avec ces outils ? Nous avons plein de sites à fort trafic qui fonctionnent en production et qui tiennent très bien la charge.

L’objectif d’un framework c’est avant tout de standardiser et de pérenniser des développements web. Il donne un certain nombre d’outils au développeur pour l’aider à développer mieux et plus vite. La question des performances arrive au second plan car les applications développées n’ont pas toutes les mêmes objectifs de performances. Un site bancaire aura besoin d’être performant mais surtout sécurisé. Un site de ventes en ligne comme RueDuCommerce, Amazon, PriceMinister… aura besoin de performances et de tenue en charge. Le site de l’association sportive de football du fin fond de la campagne n’aura certainement pas besoin d’avoir des performances extraordinaires. En effet, pour chaque application développée ses solutions. Les performances sont à prendre en considération au début du projet mais les optimisations ne peuvent se faire qu’à partir de la mise en recette et production quand l’application est véritablement sollicitée.

Si le client me dit dès le départ que les performances l’importent peu (par exemple parce qu’il souhaite une application intranet pour lui et ses 50 collaborateurs) alors pourquoi vouloir s’entêter avec les performances quand ce n’est pas véritablement justifié ? Autant prendre le parti de l’ergonomie par exemple, c’est-à-dire délaisser un peu les performances au profit du développement d’IHM plus pertinente pour les besoins du client.

Par ailleurs, j’ajoute que la notion des performances n’est pas la même pour un développeur, un chef de projet et un client. Le développeur entendra par performance une application qui répond le plus vite possible, dont les requêtes SQL sont optimisées, qui dispose de cache (sur le système de fichiers ou en mémoire) et dont les éléments d’interface sont correctement optimisés (packing des JS et CSS, utilisation de sprites, utilisation d’un CDN…). Pour le chef de projet, une application performante sera celle qui est toujours disponible, répond dans des temps acceptables et qui satisfait son client. Quant au client, ce qu’il entendra généralement par performance c’est que l’application s’affiche en moins de X secondes sur son écran car il estime qu’au delà de ces X secondes c’est lent.

Pour conclure, la question des performance n’est pas simple et n’a pas la même sens pour chacune des personnes impliquées dans le projet. Certes, il ne faut pas négliger les performances au début du projet mais le chef de projet se doit de déterminer le degré d’importance des performances de l’application à développer.

Pour tout te dire, nous avons plein de sites en production avec des trafics très importants quotidiennement et ces applications tiennent très bien la charge alors qu’elles sont développées sur symfony (qui d’ailleurs possède un système de cache natif soit dit en passant). Elles ne sont pas toutes optimisées à l’extrême mais elles répondent aux besoins des clients et des spécifications techniques.

Hugo.

Loïc · 12 janvier 2010 à 12:09

Merci Hugo.
Loïc

fch · 12 janvier 2010 à 12:30

@Hugo:
Je me repose sur ma propre expérience et celles de mes connaissances qui ont des sites à fort trafic sous symfony/zend framework/propel/doctrine/bloatware et qui sont obligées d’augmenter les ressources matérielles et/ou de tuner les serveurs de manière importante et/ou de rajouter du cache/proxy pour au final répondre à un besoin relativement simple, ou tout du moins qui ne devrait pas nécéssiter un tel effort en terme de ressources humaine et matérielle.
par ailleurs, je ne remet aucunement en cause dans mon commentaire précédent le principe des frameworks et, merci pour le cours magistral, mais je peux t’assurer que je suis très au fait de la problématique de la performance et de la relativité de cette notion en fonction de l’interlocuteur (et l’auteur de ce blog est très bien placé pour le savoir :)).
Je dis juste que le volume de code à développer et à éxécuter me parait grandement éxagérer pour répondre au besoin exprimé.

Fabien · 12 janvier 2010 à 22:39

Les fichiers de configuration yaml sont mis en cache (php) avec symfony. Ici le fichier yaml ne sert qu’à décrire le modèle pour Doctrine qui va alors générer le modèle au format php.

Les performances dépendent en partie de la qualité des développeurs. J’ai vu récemment un projet symfony où l’apprentissage du framework a été faite sur le tas. Forcément niveau performance, c’était catastrophique. Les requêtes SQL n’étaient pas du tout optimisées, ce qui est quand même la base.

Après, c’est au client de voir si il préfère un site en php « bas-niveau » donc plus long à développer et certainement plus cher (le temps c’est de l’argent comme on dit) ou si il préfère se doter de l’architecture serveurs adaptée à son projet.

Dans tous les cas, l’utilisation d’un framework aura un impact plus ou moins important sur les performances en fonction du projet. Il y a un compris à faire entre la facilité/rapidité de développement et les performances.

fch · 13 janvier 2010 à 10:50

Cache ou pas cache, ca reste complexe à mettre en oeuvre et à éxécuter par rapport au besoin.
Et évidement que les performances dépendent de la qualité du développement, en terme d’algorithmique, de qualité du code, et cela aussi bien en PHP qu’en SQL.
Et je pense que tu serais surpris par rapport à la fameuse « rapidité de codage » d’un site sans framework, mais avec une bonne méthodologie de développement (convention de codage, algorithme récurrent, etc) et d’un site avec framework.
En effet, pour certaines choses, le framework peut devenir tellement « long » à mettre en oeuvre qu’il n’est plus concurrentiel (fabien, souvient toi des grid d’opentime…) par rapport à une solution sans framework.
En général, on ne se rend compte de ce phénomène que lorsque pour une raison ou une autre, on redéveloppe sans framework.
De plus, même si le coût des ressources matérielles a bien baissé, le coût des personnes capable d’administrer et de maintenir tout cela correctement, lui, ne baisse pas et c’est même plutôt le contraire, car une personne capable de gérer correctement du clustering avec du proxy ou du cache à plusieurs niveaux, aussi bien côté web que sql, et qui est capable de faire évoluer l’architecture et de garantir un uptime respectable, ca se paye avec pas mal d’euros.
Et enfin, le green IT passe par là, et bientôt, les sites web gourmands en énergie vont devenir une réelle préocupation et les sites écologiques un élement marketing puisssant.
Nous le constatons déjà chez no parking, puisque nous avons des demandes dans ce sens, et pas de petits clients.
Gacher de la ressource ne sera donc plus acceptable.

Gert · 22 février 2010 à 18:53

I get
500 | Internal Server Error | Doctrine_Exception
Couldn’t find class TaggableTag

The class is never generated. Any idea how to solve this problem?

keo · 13 septembre 2010 à 17:46

Bonjour, comment utiliser ton patch et les options du behavior pour gérer le onDelete cascade automatiquement ?

    Fabien · 13 septembre 2010 à 22:29

    Depuis mon patch a été appliqué sur la version officielle de l’extension. Il n’est donc plus nécessaire ;-)

keo · 14 septembre 2010 à 16:12

Effectivement, j’ai vu que ton patch a été ajouté à la version officielle :) (le merge des options je crois) Cependant, je voudrais éviter de supprimer les liaisons (objettag) via le preDelete() et automatiser le onDelete: cascade entre la table de mon objet et la table de liaison mais … je dois certainement mal m’y prendre. Comment préciser le onDelete ?

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

:D :) :o :eek: :( :lol: :wink: :arrow: :idea: :?: :!: :evil: :p

Articles similaires

Développement

Installer et utiliser NodeJS et le module Less sur Mac OS X

Connaissez-vous LESS ? Il s’agit d’une sur-couche de CSS apportant son lot d’améliorations pour faciliter l’écriture de feuille de styles. LESS vous permettra par exemple de définir des fonctions ou des variables que vous pourrez Lire la suite…

Développement

Installer Apache 2, MySQL 5 et PHP 5.3 sur Mac OS 10.7 Lion avec MacPort

Bien que Apache et PHP soient pré-installés sur Mac OS X, j’évite depuis plusieurs années de les utiliser. Au départ pour un problème de compilation d’une extension PHP, aujourd’hui pour ne plus être dépendant d’Apple Lire la suite…

Développement

Critique du livre « Symfony 1.3 Web Application Development »

Il y a quelques semaines, l’éditeur PacktPublishing m’a proposé d’écrire une critique de son livre fraichement sorti intitulé « Symfony 1.3 Web Application Development » et écrit par Tim Bowler et Wojciech Bancer. Comme son nom l’indique, Lire la suite…