Vous souhaitez nous soutenir ? Devenez sponsor de l'association sur notre page Github
Tutoriels

Migrations avec Laravel : Le Guide Ultime

Laravel Jutsu avatar
Publié le 31 janvier 2025
Couverture de l'article Migrations avec Laravel : Le Guide Ultime

Les migrations sont un pilier essentiel pour la gestion de la base de données dans les frameworks modernes comme Symfony et Laravel. Pourtant, les approches diffèrent.

Là où Symfony favorise une configuration explicite et un couplage direct à Doctrine, Laravel mise sur un système fluide, où l'élégance syntaxique prime, et où les développeurs bénéficient de raccourcis efficaces grâce à Eloquent.

Une migration est donc une classe prête à l'emploi avec une structure pensée pour les besoins courants. Découvrons ensemble de quoi il en retourne !

Les bases des migrations

Qu'est-ce qu'une migration ?

Une migration est un fichier scripté qui décrit comment une base de données doit évoluer. Elle permet de versionner les modifications (création de tables, ajout de colonnes, etc.) et de garantir que tous les environnements de développement et de production restent synchronisés.

Par ailleurs, après avoir défini la structure de votre base de données, vous pouvez facilement peupler vos tables avec les seeders.

Simple et puissant

Avec Laravel, une migration est une classe générée via la commande Artisan make:migration :

1return new class extends Migration
2{
3 /**
4 * Run the migrations.
5 */
6 public function up(): void
7 {
8 Schema::create('users', function (Blueprint $table) {
9 $table->id();
10 $table->string('name');
11 $table->string('email')->unique();
12 $table->timestamp('email_verified_at')->nullable();
13 $table->string('password');
14 $table->rememberToken();
15 $table->timestamps();
16 
17 // ...
18 });
19 }
20 
21 public function down(): void
22 {
23 Schema::dropIfExists('users');
24 Schema::dropIfExists('password_reset_tokens');
25 Schema::dropIfExists('sessions');
26 }
27};

Ce code provient directement du repository de Laravel lui-même, il permet de versionner la création de la table users.

Pour créer un nouveau fichier de migration vous passerez par une commande artisan :

1php artisan make:migration create_products_table
2# ou
3php artisan make:migration CreateProductsTable

Mais elle peut être créée en même temps que votre Modèle avec l'option -m :

1php artisan make:model Product -m

La classe générée dans le dossier database/migrations contient deux méthodes : up() et down().

La méthode up() exécute la migration. On y retrouve le nom de la table. La valeur $table est une instance de Blueprint et permet d'utiliser de nombreuses méthodes pour créer les colonnes de sa table ou effectuer d'autres changements.

La méthode down() inverse la migration. C'est le code exécuté lorsque vous faites un rollback de vos migrations.

Revenir en arrière

Il se peut que vous souhaitiez revenir sur une migration précédemment exécutée. Voyons les 4 commandes possibles pour ce faire.

php artisan migrate:rollback

Cette commande annule le dernier lot de migrations enregistré dans la table existante. Un "lot" (batch) désigne un groupe de migrations exécutées ensemble lors d'une même commande php artisan migrate.

1php artisan migrate:rollback

Vous pouvez spécifier le nombre de lots à annuler avec l’option --step :

1php artisan migrate:rollback --step=2

php artisan migrate:reset

La commande php artisan migrate:reset diffère de rollback. Avec reset, toutes les migrations appliquées sont annulées, peu importe quand elles ont été exécutées.

1php artisan migrate:reset

php artisan migrate:refresh

La commande php artisan migrate:refresh annule toutes les migrations, puis les exécute à nouveau. Cela permet de reconstruire la base de données depuis zéro. Vous pouvez utiliser l'option --step pour limiter les migrations concernées.

1php artisan migrate:refresh --step=1

php artisan migrate:fresh

La commande php artisan migrate:fresh supprime toutes les tables de la base de données, puis exécute à nouveau toutes les migrations. Contrairement à refresh, elle ne revient pas sur les migrations mais supprime directement les tables.

1php artisan migrate:fresh

Gérer des cas complexes

Les relations entre tables

Les bases de données relationnelles nécessitent de gérer des relations (one-to-many, many-to-many). Laravel facilite cela via des méthodes intégrées pour les clés étrangères et leurs contraintes.

Prenons l'exemple d'une relation belongsTo() (many-to-one) :

1return new class extends Migration
2{
3 public function up(): void
4 {
5 Schema::create('products', function (Blueprint $table) {
6 $table->id();
7 $table->timestamps();
8 
9 // Première syntaxe
10 $table->unsignedBigInteger('order_id');
11 $table->foreign('order_id')->references('id')->on('orders');
12 
13 // Deuxième syntaxe
14 $table->foreignId('order_id')->constrained();
15 
16 // Troisième syntaxe
17 $table->foreignIdFor(\App\Models\Product::class)->constrained();
18 });
19 }
20};

Conventions Laravel pour les clés étrangères

Laravel s'attend à ce que les noms des tables et des clés étrangères suivent ces conventions :

Respecter ces conventions garantit un code plus clair et compatible avec le reste de l'écosystème Laravel et son ORM.

Aller plus loin

Indexation et optimisation des performances

Utiliser des index : Ajoutez des index aux colonnes fréquemment utilisées dans les requêtes pour améliorer les performances. Cela réduit le temps de recherche dans des tables volumineuses. Pour un guide pratique, consultez ce tutoriel vidéo sur les index dans Laravel.

1$table->index('column_name');

Pour des recherches complexes, pensez aussi à des index composites :

1$table->index(['column1', 'column2']);

Sous-requêtes : Exploitez les sous-requêtes pour des calculs avancés ou pour récupérer des données liées sans créer des relations complexes dans vos modèles. Par exemple, voici comment inclure une sous-requête dans un champ calculé :

1$users = User::query()
2 ->addSelect([
3 'total_orders' => Order::selectRaw('COUNT(*)')
4 ->whereColumn('orders.user_id', 'users.id')
5 ])->get();

Pour plus de détails, consultez ce tutoriel sur les sous-requêtes dans Laravel.

Modifier les colonnes avec précaution : Privilégiez des migrations non bloquantes pour éviter des temps d'arrêt prolongés. Par exemple, au lieu de modifier directement une colonne existante, créez une nouvelle colonne, migrez les données, puis supprimez l’ancienne colonne :

1Schema::table('users', function (Blueprint $table) {
2 $table->string('new_column')->nullable();
3});
4 
5// Migrer les données via un script artisan ou un job
6DB::table('users')->update(['new_column' => DB::raw('old_column')]);
7 
8Schema::table('users', function (Blueprint $table) {
9 $table->dropColumn('old_column');
10});

Réindexation : Après des modifications majeures (comme le changement de type d’une colonne ou la suppression de nombreuses lignes), reconstruisez les index pour maintenir des performances optimales. Utilisez des commandes SQL spécifiques ou intégrez des scripts Artisan adaptés.

Calculs de distance : Dans des projets nécessitant une gestion géospatiale (comme trouver les magasins proches d’un utilisateur), vous pouvez utiliser la fonction SQL ST_Distance combinée à un scope Eloquent. Voici un exemple :

1$coordinates = [5.93333, 43.116669];
2 
3$shops = Shop::query()
4 ->addDistance($coordinates)
5 ->latest()
6 ->get();
7 
8// Dans le modèle Shop
9public function scopeAddDistance(Builder $query, array $coordinates): void
10{
11 $query
12 ->when(is_null($query->getQuery()->columns), static fn (Builder $query) => $query->select('*'))
13 ->selectRaw(
14 expression: 'ST_Distance(
15 ST_SRID(Point(longitude, latitude), 4326),
16 ST_SRID(Point(?, ?), 4326)
17 ) AS distance',
18 bindings: $coordinates
19 );
20}

Pour un guide complet, explorez ce tutoriel vidéo sur le calcul de distance avec Eloquent.

Conclusion

Laravel fournit un puissant système de gestion des bases de données via ses migrations et ses outils avancés. En respectant les conventions, en adoptant de bonnes pratiques, et en exploitant des fonctionnalités comme les index, les sous-requêtes ou les calculs géospatiaux, vous pouvez garantir une base de données performante, maintenable et évolutive.

A lire

Autres articles de la même catégorie