Newsletter Apprendre Laravel #11

Envoyée le 21 janvier 2020

Après une longue pause de plusieurs mois, j'aimerais reprendre cette newsletter. Je vais essayer d'y mettre moins de choses afin d'être plus régulier dans les publications. Si vous ne souhaitez plus recevoir cette newsletter (depuis le temps c'est compréhensible) n'hésitez pas à m'envoyer un mail pour que je vous retire de la liste.

Aujourd'hui je vais parler des factories.

En Laravel, il est possible de rapidement créer des modèles Eloquent en base de données via les factories :

$factory->define(App\Comment::class, function (Faker\Generator $faker) {
    return [
        'post_id' => factory(App\Post::class)->create()->id,
        'text' => $faker->sentances,
    ];
});

$comment = factory(App\Comment::class)->create();

Le problème avec cette approche, c'est que si nous voulons lier un commentaire avec un article existant, la ligne factory(App\Post::class)->create() va créer un App\Post dans tous les cas, par exemple :

$post = factory(App\Post::class)->create([
    'title' => 'Some title',
]);
$comment = factory(App\Comment::class)->create([
    'post_id' => $post->id,
]); // This factory will create another App\Post anyway = one more INSERT queries = slow tests.

Pour résoudre ce problème, Laravel permet depuis très longtemps l'utilisation de fonctions anonymes dans les factories.

$factory->define(App\Comment::class, function (Faker\Generator $faker) {
    return [
        'post_id' => function () {
            return factory(App\Post::class)->create()->id;
        }, // This closure will be executed only if no `post_id` is provided.
        'text' => $faker->sentances,
    ];
});

Cette solution corrige notre problème de requête supplémentaire. Mais je n'ai jamais trouvé la syntaxe très propre et très agréable à utiliser. C'est pour ça qu'il y a quelques années, j'avais proposé une nouvelle syntaxe pour résoudre ce problème :

$factory->define(App\Comment::class, function (Faker\Generator $faker) {
    return [
        'post' => factory(App\Post::class),
        'text' => $faker->sentances,
    ];
});

Ma proposition présentait plusieurs avantages :

Mais cette solution n'aura pas été acceptée malgré les quelques retours positifs (et un peu moins positifs ^^).

Il y a quelques mois, je suis tombé sur un tweet de @freekmurze présentant une nouveauté de Laravel permettant d'écrire :

$factory->define(App\Comment::class, function (Faker\Generator $faker) {
    return [
        'post_id' => factory(App\Post::class),
        'text' => $faker->sentances,
    ];
});

Je n'ai pas réussi à retrouver le commit introduisant cette amélioration mais j'en suis très content, je vais pouvoir supprimer toutes ces fonctions anonymes de mes factories. Si vous souhaitez en savoir plus, tout est documenté ici : https://laravel.com/docs/6.x/database-testing#relationships.

Voilà pour cette première newsletter de 2020, en espérant que je continue plus régulièrement qu'en 2019. Si ma chaîne YouTube vous intéresse, durant la fin de l'année 2019, j'ai réalisé de nombreuses vidéos en live sur la création d'un site e-commerce sans utiliser de framework et quelques vidéos sur ma découverte de Rust avec le Advent Of Code 2019.

Bonne semaine à tous,

Thibaud