Sommaire


symfony préfixe
Ajouter un préfixe aux tables en BDD sans changer le nom des entités dans Symfony
Catégories associées :

Préfixer ses tables en base de données est un geste simple aux multiples avantages, que nous allons découvrir plus bas. Si, dans wordpress, il est simplissime de changer le préfixe d'origine (le fameux "wp_"), c'est autrement plus laborieux dans symfony.

Vous avez choisi de faire un projet avec Symfony ? Bon choix ! Vous codez, tout se passe bien, et voici l'heure de mettre en prod… Votre hébergement, mutualisé, ne vous permet pas de créer une nouvelle base, mais seulement des tables dans la BDD associée. Et là, erreur, la table users existe déjà...

Pourquoi préfixer ses tables ?

Ajouter un préfixe à ses tables a de nombreux avantages. Tout d'abord, pour la sécurité. En effet, une table users, tous les sites du monde en ont une. En cherchant une faille de sécurité, on risque de pouvoir facilement accéder à cette table, même sans connaitre l'arborescence du projet, si elle a un nom trop courant. Et c'est un argument valable même si on peut avoir autant de base de données qu'on veut (soit une par projet) : préfixer ses tables sera toujours un gain non négligeable sur le plan de la sécurité.

Ensuite, si vous êtes sur des petits projets, que vous utilisez un hébergement mutualisé, ou que sais-je encore, qui vous contraint à n'avoir qu'un base de données pour tous vos sites, Préfixer ses tables est indispensable. Cela permet de différencier des tables qui auraient le même nom (users et users, par exemple...), en leur ajoutant un préfixe propre à chaque projet. Ainsi, nous aurions ck_users et bk_users, pour des sites comme cooking et booking par exemple. Plus de confusion possible, plus de risques d'agir sur les mauvaises tables.

Enfin, pour un travail de maintenance et de sauvegarde, le jour où vous avez besoin de faire une sauvegarde ou de réinstaller les tables d'un projet, pouvoir les sélectionner grâce à un préfixe commun est un énorme atout : plus de risques d'oublis, ou au contraire d'associer des tables d'un autre projet à votre sauvegarde...

Bref, c'est une très bonne hygiène de travail, tant sur les plans technique, sécuritaire et pratiques !

Comment ajouter un préfixe ?

Rentrons dans le vif du sujet. Si vous utilisez symfony, vous savez que vous avec un environnement de test compris. Vous savez aussi que lorsque vous créez votre base de données de test, automatique, elle est suffixée de _test.

# config/packages/doctrine.yaml

when@test:
doctrine:
dbal:
# "TEST_TOKEN" is typically set by ParaTest
dbname_suffix: '_test%env(default::TEST_TOKEN)%'

S'il existe un dbname_suffix, on doit avoir un dbname_prefix ? ou un dbtablename_prefix ? Et bien non, ce serait trop simple ! 😉

La solution la plus sale serait de renommer toutes nos entités, en leur incorporant, dans le code, le préfixe. Vous imaginez le bazar, si vous pensez à celà à la fin de votre projet ? Et même si vous êtes consciencieux et que vous le mettez en place dès le début, ça va alourdir le code et rendre sa lecture et sa compréhension d'autant plus difficile.

Non, Il va falloir entrer plus en avant pour pouvoir ajouter notre fonctionnalité.

Le principe des services

Un service, c'est un bout de code (généralement un objet), qui réalise une action. Pour appeler ce code et l'exécuter à des moments précis, on lui associe une configuration.

En gros, tout ce qu'on serait amené à faire plusieurs fois à des endroits différents du code peut être transformé en un service. C'est une factorisation, qui permet d'alléger le code et de mieux s'y retrouver.

Création du service de préfixe

Commençons donc par créer ce fameux service. La documentation de doctrine (qui n'est rien d'autre qu'un service, soit dit en passant) nous aide sur ce point :

Dans un fichier qu'on rengera dans un dossier Services et qu'on appelera DatabaseTablePrefix.php, on mettra ce code :

<?php
// Src\Service\DatabaseTablePrefix.php

namespace App\Service;

use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
use \Doctrine\ORM\Event\LoadClassMetadataEventArgs;

class DatabaseTablePrefix implements EventSubscriberInterface {

private $prefix;

// Lors de l'appel de ce service, on lui passera notre préfixe.
// IL N'EST PAS DÉFINI ICI !!
public function __construct($prefix){
$this->prefix =$prefix;
}

// Méthode qui sera appelée à chaque fois que doctrine voudra appeler la BDD
public function getSubscribedEvents(){

return array('loadClassMetadata');

}

public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();

// Première chose à faire, on change le nom de l'entité en lui ajoutant le préfixe.
if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {
$classMetadata->setPrimaryTable([
'name' => $this->prefix . $classMetadata->getTableName()
]);
}

// Deuxième chose à faire, on vérifie s'il y a des associations entre entités, pour changer le nom de l'entité appelée aussi.
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) {
$mappedTableName = $mapping['joinTable']['name'];
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
}
}
}
}

Configurer le service

Une fois ce code enregistré, il faut mettre en place la configuration associée.

Dans le fichier config/services.yaml, on met ce code :

// config\services.yaml

{...}

Services
{...}
App\Service\DatabaseTablePrefix:
arguments:
$prefix: '%env(PREFIXE_DATABASE_TABLE)%'
tags:
- { name: doctrine.event_suscriber }

Attention à l'indentation, c'est très important de bien revenir aux bonnes lignes au bon moment. Dans ce code, on dit à symfony qu'il faut appeler notre service à chaque fois que doctrine est appelé. Et on en profite pour lui dire que nous stockerons la valeur de notre variable $prefix dans notre fichier .env. C'est un choix que j'ai fait pour tout regrouper au même endroit. Bien sûr, on aurait pu lui donner sa valeur ici. Mais puisque dans le .env nous avons toutes les infos concernant la BDD, autant y ranger le préfixe aussi non ?

Définir le préfixe dans le .env

Et donc, pour finir, il faut aller dans le .env pour donner la valeur de notre préfixe :

// .env

PREFIXE_DATABASE_TABLE="prefix_"

Évidemment, vous remplacerez "prefix" par ce que vous voulez.

Conclusion

Et voilà, si vous faites votre migration, vous verrez que dans votre base de données, toutes vos tables sont bien préfixées, et que cela ne change rien à l'exécution de votre code !

Mieux encore : cela est pris en compte quelque soit votre environnement de travail : local, prod, test... Toutes vos tables seront préfixées de la même manière, ce qui est important pour un soucis de cohérence.

A l'exception d'une table (il faut toujours des exceptions) : la table messenger_messages qui est gérée par symfony, ne prend pas de préfixe. Allez savoir pourquoi, c'est un mystère.

Si vous connaissez la réponse à cette énigme, n'hésitez pas à la proposer dans les commentaires !

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont marqués *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.