Fixtures - Les données de tests
Une fois qu'on a nos entités, qu'on a créé notre base de données, on aimerait pouvoir insérer des données dans la base.
Avant de s'attaquer à la création de formulaires et d'interfaces de saisie d'un nouvel article, on peut aussi demander à un package de générer des données de tests : un ensemble initial d'enregistrements qui seront créés dans la base de données et nous permettront de commencer à travailler.
Ainsi, si on récupère le code de cette application, on n'aura pas à passer du temps à saisir manuellement des données de tests.
Ce type de données s'appelle des fixtures.
Le package de fixtures
On installe la dépendance de développement suivante : orm-fixtures (il s'agit de l'alias Flex).
composer require --dev orm-fixtures
orm-fixtures est un alias pour désigner le package doctrine/doctrine-fixtures-bundle
La recette exécutée par Symfony Flex lors de l'installation a créé un fichier src/DataFixtures/AppFixtures.php.
C'est dans ce fichier qu'on va créer nos objets et les enregistrer en base de données.
Créer des fixtures
Notre fichier de fixtures, à la base, ressemble à quelque chose comme ça :
<?php
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// $product = new Product();
// $manager->persist($product);
$manager->flush();
}
}
Dans la méthode load de notre classe, on va donc vouloir instancier toutes les entités qu'on souhaite enregistrer en base de données.
Par exemple :
<?php
//...
public function load(ObjectManager $manager)
{
$article = new Article();
$article->setTitle('Mon premier article !')
->setDateCreated(new DateTime())
->setContent("Le contenu de l'article");
$manager->flush();
}
//...
On peut ensuite générer notre base de tests avec la commande suivante :
php bin/console doctrine:fixtures:load
# ou plus court
php bin/console d:f:l
Si on exécute la commande, il ne se passe rien. C'est parfaitement normal. Pour qu'un enregistrement soit créé en base de données, il faut au préalable le persister.
La persistance des entités
Dans l'extrait de code ci-dessus, reprenons les différentes étapes empruntées afin de pouvoir sauvegarder une entité en base de données :
- On instancie un nouvel objet de type
Article - On utilise l'interface fluide pour assigner des valeurs à ses différents attributs, via les setters
- On
flushles changements effectués pour qu'ils soient exécutés en base de données
Lorsqu'on va vouloir créer un nouvel enregistrement en base de données, on va passer par ces étapes. Ici, il en manque une qui est la plus importante : persister notre article avant de valider les changements avec flush.
Pour ça, avant l'appel à la méthode flush, on va demander à notre gestionnaire (l'instance d'ObjectManager) de persister l'entité :
$article = new Article();
$article->setTitle('Mon premier article !')
->setDateCreated(new DateTime())
->setContent("Le contenu de l'article");
$manager->persist($article); // <-- ICI
$manager->flush();
L'étape de persistance, c'est-à-dire l'appel à la méthode persist de votre gestionnaire d'entités, est indispensable. Elle permet tout simplement, après la création d'un objet, de dire à votre gestionnaire que vous souhaitez qu'il gère cette entité. Vu qu'elle n'existe pas encore (on vient de la créer manuellement, dans le code, on ne l'a pas récupérée d'une source de données existante), elle sera donc persistée, c'est-à-dire créée en base de données, lorsque vous exécuterez la méthode flush du gestionnaire
L'appel à la méthode flush permet de pousser vers la base de données tous les changements que vous avez demandés à votre gestionnaire d'entités. Pour notre exemple, il s'agit, après avoir demandé à notre gestionnaire de gérer l'entité, de l'insérer de manière concrète dans la base de données, donc d'exécuter le code SQL nécessaire à son insertion. Ceci nous permet de pouvoir demander plusieurs opérations à notre gestionnaire (insertion, modification, suppression), puis d'effectuer un seul appel à flush pour regrouper toutes les requêtes. Ce serait trop lourd si on devait exécuter une requête à chaque fois qu'on demandait quelque chose au gestionnaire
Si on passe en mode trace au moment de l'exécution des fixtures, on pourra constater que l'instance $manager est en réalité une instance de la classe Doctrine\ORM\EntityManager. On a donc bien un gestionnaire d'entités qui va nous aider à gérer les échanges avec la base de données
Générer des données aléatoires
Pour le moment, nos fixtures nous créent un seul article à chaque fois qu'on les exécute.
Par ailleurs, à chaque fois qu'on exécute nos fixtures, elles ne viennent pas se rajouter à la base de données, mais viennent bien remplacer ce qui existe en base de données. La base est donc purgée à chaque fois qu'on exécute les fixtures, pour avoir de nouvelles données fraîches, prêtes à l'utilisation.
Ce qu'on aimerait, c'est pouvoir disposer de données créées pour nous, automatiquement, sans qu'on ait particulièrement à passer du temps sur la rédaction d'articles qui serviraient pour nos tests.
La libraire faker
Pour ce faire, on va déléguer le travail à une librairie : fakerphp/faker.
On va donc l'ajouter aux dépendances de développement avec la commande suivante :
composer require --dev fakerphp/faker
Pour l'utiliser, il suffit de suivre la documentation. On va créer une instance de la classe Faker\Generator puis l'utiliser pour lui demander des noms, des prénoms, des textes, etc...
$faker = \Faker\Factory::create();
$article = new Article();
$article->setTitle($faker->realText(50))
->setDateCreated($faker->dateTimeBetween('-2 years'))
->setContent($faker->realTextBetween(250, 500));
$manager->persist($article);
$manager->flush();
Générer une collection d'articles
Si on veut générer, mettons, une cinquantaine d'articles aléatoires, il nous suffit alors de faire une boucle qui créera nos 50 articles :
$faker = \Faker\Factory::create();
for ($i = 0; $i < 50; $i++) {
$article = new Article();
$article->setTitle($faker->realText(50))
->setDateCreated($faker->dateTimeBetween('-2 years'))
->setContent($faker->realTextBetween(250, 500));
$manager->persist($article);
}
$manager->flush();
On a vu l'importance de la persistance des données avec la méthode persist.
On exécutera la méthode flush à la fin, une fois qu'on aura demandé à notre gestionnaire de gérer les 50 entités créées.
En effet, on ne veut pas effectuer 50 allers-retours entre l'application et la base de données, mais bien un seul qui regroupera la création des 50 enregistrements