Sommaire


'}}
Ajouter un préfixe aux tables en base de données sans changer le nom des entités dans Symfony 7
Catégories associées :

J'avais déjà fait un article sur le sujet à propos de Symfony 6, mais il se trouve que la manière d'ajouter un préfixe aux tables en base de données sans changer le nom de nos entités a changé avec Symfony 7.

Dans l'idée, c'est la même chose. On va créer un service, qui sera appelé lors d'un évènement de doctrine, et qui ajoutera le préfixe qu'on aura choisi.

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'une base de données pour tous vos sites, préfixer vos 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 !

Sauf que dans Symfony, l'ORM doctrine récupère les entités, et à partir de leur nom, crée les tables automatiquement. C'est super pratique, mais ça pose un problème : est-ce que si je veux préfixer mes tables, j'accepte d'avoir des noms de classes bizarres, comme CkUser ou BkUser, à la place de User, tout simplement ?

C'est la solution qui vient à l'esprit quand on ne sait pas trop comment imposer à Doctrine le préfixe : on choisit de préfixer directement les entités dans le code.

Cependant, il est possible de garder des noms d'entités simples ET de préfixer les tables ! C'est ce que nous allons voir.

Création d'un service d'ajout d'un préfixe

S'il est impossible de trouver quoi que ce soit dans la documentation de Symfony, la doc de Doctrine donne quelques indications à propos des préfixes.

Tout d'abord, créer un fichier DatabaseTablePrefix.php, stocké dans un dossier src\Service.

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

namespace App\Service;

use Doctrine\ORM\Event\LoadClassMetadataEventArgs;

class DatabaseTablePrefix
{
private $prefix;

public function __construct(string $prefix)
{
$this->prefix = $prefix;
}

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;
}
}
}
}
service pour préfixer les tables en base de données

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(DB_PREFIX)%'
tags:
- { name: doctrine.event_listener, event: loadClassMetadata }
Configurer le service

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

DB_PREFIX="prefix_"

Les guillemets autour du prefix_ sont facultatifs. Évidemment, vous remplacerez "prefix" par ce que vous voudrez ! 😉

Vous aviez déjà fait votre migration sans préfixe ?

Si vous aviez déjà lancé votre projet, créé vos fichiers de migration, et tout le tralala, il va falloir revenir en arrière. En effet, on vient de voir comment ajouter un préfixe à nos tables, mais cela n'est pas rétroactif : si vous avez déjà fait vos fichiers de migration avec des noms de tables sans préfixe, lorsque vous demanderez à Doctrine d'effectuer la migration, elle n'ajoutera pas les préfixes, puisqu'ils ne sont pas écrits.

C'est au moment de la création des fichiers de migrations que le préfixe pourra être ajouté. Pour cela, on va avoir quelques lignes de commandes à faire.

Nettoyer la base de données des tables existantes :

php bin/console doctrine:schema:drop --full-database --force

Cette commande permet de vider totalement la base de données.

Vider le cache de Symfony

php bin/console cache:clear

Faire la migration avec les préfixes des tables

php bin/console doctrine:migration:diff

Effectuer la migration en base de données

php bin/console symfony doctrine:migrations:migrate

// ou

php bin/console d:m:m

Conclusion

Ça y est ! vous avez vos tables préfixées en base de données, et pourtant vous pouvez continuer à travailler avec vos entités sans que rien ne change dans 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 de deux tables tables (il faut toujours des exceptions) : la table messenger_messages et la table doctrine_migration_versions, qui sont gérées par symfony (et ses dépendances), ne prennent pas en compte le préfixe. Allez savoir pourquoi, c'est un mystère.

C'est un peu embêtant, parce que lorsqu'on fera la mise en production de plusieurs projets symfony sur un hébergement mutualisé par exemple, et bien il n'y aura qu'une seule fois ces tables pour tous les projets.

Cependant ce n'est pas dramatique, parce qu'en production ( et particulièrement sur mutualisé où vous n'avez peut-être même pas accès à la ligne de commande), ces deux tables ne nous serviront pas (pour les migrations c'est sûr, pour les messages, ça dépend des cas...)

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

Commentaires

One Reply to “Ajouter un préfixe aux tables en base de données sans changer le nom des entités dans Symfony 7”

Amal N.

Excellent sujet et très bonne explication comme d’hab 😉
Un grand merci

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.

' skin='skin3'}}