Je suis développeur web freelance et propose des formations à Symfony2 ! Contactez-moi pour en discuter.

Doctrine, l’ORM de base de Symfony2

Préfixer une table de base de données est, de manière générale, une mauvaise pratique. Souvent parce que cela signifie qu’on cherche à faire cohabiter 2 applications différentes sur une même base de données, ce qui n’est pas une bonne chose du point de vue architecture et séparation des responsabilités (SoC, Separation of Concerns). Dans ce cas, il est largement accepté d’avoir chaque application sur une base séparée, et c’est d’une manière générale la méthode préconisée.

Maintenant, il arrive des cas où il est utile de pouvoir préfixer ses tables. Par exemple parce que c’est la convention requise, ou bien parce que les 2 applications sont 2 parties d’un même tout (par exemple une application Symfony pour le coeur métier, et un WordPress pour la partie blog). Préfixer les tables permet alors d’éviter les collisions de noms.

C’est tellement une mauvaise pratique que ce qui est un paramètre de configuration dans WordPress n’est même pas disponible de base avec Doctrine, l’ORM livré de base avec Symfony2. On a pas forcément l’envie ni la capacité de renommer tous les noms de tables soi-même (ce qui ne serait pas non plus une bonne chose). De plus celà veut dire qu’à chaque fois qu’on voudra rajouter une table il ne faudra pas oublier de la prefixer, bref c’est le début des emmerdes.
Quand on doit quand même avoir un système de prefixes de table à peu près viable, on fait comment ? On rajoute a fonctionnalité soi-même.

La fonctionnalité n’est donc pas supportée de base avec Doctrine, mais ils proposent une solution dans un cookbook, avec du code prêt à l’emploi. Il ne nous reste qu’à l’intégrer, et on est bon. Mais comment ?

L’exemple de la doc nous dit qu’il faut créer un listener d’évènements, qui se greffe sur l’appel de la méthode loadClassMetaData.
Un petit coup d’oeil à la documentation sur le sujet nous indique qu’il suffit de créer un service, et de le tagger doctrine.event_subscriber. C’est bon, ya plus qu’à.

C’est ce qu’on va faire, en modifiant notre fichier FooBundle/config/services.yml:

parameters:
keiruaprodfoo.doctrine.prefix: kp_foo_

services:
keiruaprodfoo.doctrineprefix_subscriber:
class: KeiruaProd\FooBundle\DoctrineExtensions\TablePrefixSubscriber
arguments: [%keiruaprodfoo.doctrine.prefix%]
tags:
- { name: doctrine.event_subscriber }

Maintenant, reste à créer notre classe. Comme le montre toujours la même doc, pour faire un subscriber d’évènements il faut que notre classe hérite de \Doctrine\Common\EventSubscriber. C’est ce qu’on va faire. On crée un fichier FooBundle/DoctrineExtensions\TablePrefixSubscriber, dont le contenu va beaucoup ressembler à celui de la documentation de départ :


prefix = (string) $prefix;
}

public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
$classMetadata->setTableName($this->prefix . $classMetadata->getTableName());
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY) {
$mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name'];
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
}
}
}

public function getSubscribedEvents()
{
return array('loadClassMetadata');
}
}

La seule différence avec le code original, c’est que comme on hérite de EventSubscriber, il faut implémenter getSubscribedEvents(), qui donne la liste des évènements auxquels on s’inscrit.

Maintenant, on en a fini avec le code. Au niveau fonctionnel, ça dépend. Si on vient de démarrer un nouveau projet et qu’aucune base n’a été créé, pas de soucis. Les nouvelles tables auront le préfixe précisé quand elles seront créés.
Maintenant si on a déjà des tables, attention. Si vous mettez à jour le schéma de base de données (via app/console doctrine:schema:update –force), bien evidemment seul le schéma est mis à jour. Il faudra sans doute réinsérer vos données factices dans la base, ou avoir une copie de la base initiale. Quoi qu’il en soit, celà doit s’intégrer dans votre process de migrations… Peut être utiliserez vous DoctrineMigrationsBundle, dont j’avais parlé il y a un bon moment déjà ?

Une Réponse à “Préfixer une table Doctrine avec Symfony2”

  1. Greg* Dit:

    Tu as des exemples où la cohabitation de tables de différentes applications dégrade les performances ?

    Je suis bien évidemment entièrement d’accord sur le principe du separation of concerns !

Répondre

Unable to load the Are You a Human PlayThru™. Please contact the site owner to report the problem.