Migrations et Seeders — Laravel 12
Gestion du schéma de base de données versionné et peuplement.
🏗️ Migrations
Création
php artisan make:migration create_users_table
php artisan make:migration add_avatar_to_users_table --table=users
php artisan make:model Post -m # avec migrationStructure
<?php
// database/migrations/2023_12_01_000000_create_users_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('users');
}
};📊 Types de colonnes
Types de base
Schema::create('posts', function (Blueprint $table) {
// Numériques
$table->integer('votes');
$table->bigInteger('views');
$table->decimal('price', 8, 2);
$table->float('rating');
// Chaînes
$table->string('title');
$table->string('slug', 100);
$table->text('content');
$table->longText('description');
$table->char('status', 1);
// Dates
$table->date('published_at');
$table->datetime('created_at');
$table->timestamp('deleted_at')->nullable();
$table->year('birth_year');
// Booléen et binaire
$table->boolean('is_active');
$table->binary('data');
// JSON et enum
$table->json('settings');
$table->jsonb('metadata'); // PostgreSQL
$table->enum('status', ['draft', 'published', 'archived']);
// UUID et IP
$table->uuid('identifier');
$table->ipAddress('visitor_ip');
$table->macAddress('device_mac');
// Géométrie (PostgreSQL)
$table->geometry('coordinates');
$table->point('location');
});Modificateurs
Schema::create('users', function (Blueprint $table) {
$table->string('name')->after('id'); // Position
$table->string('email')->unique(); // Unique
$table->string('phone')->nullable(); // Nullable
$table->integer('age')->default(18); // Valeur par défaut
$table->string('slug')->index(); // Index
$table->text('content')->comment('Post content'); // Commentaire
// Timestamps automatiques
$table->timestamps();
$table->softDeletes(); // deleted_at nullable
});🔗 Relations et contraintes
Clés étrangères
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->foreignId('user_id')->constrained(); // users.id
$table->foreignId('category_id')->nullable()
->constrained()->onDelete('set null');
$table->timestamps();
});
// Contrainte personnalisée
$table->foreign('author_id')
->references('id')
->on('users')
->onDelete('cascade')
->onUpdate('restrict');Tables pivot
Schema::create('role_user', function (Blueprint $table) {
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('role_id')->constrained()->onDelete('cascade');
$table->primary(['user_id', 'role_id']); // Clé composite
$table->timestamps();
});📝 Index
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('sku')->unique();
$table->decimal('price', 8, 2);
$table->integer('category_id');
$table->boolean('active');
// Index simple
$table->index('name');
// Index composite
$table->index(['category_id', 'active']);
// Index unique
$table->unique(['name', 'category_id']);
// Index personnalisé
$table->index('name', 'products_name_index');
// Full-text (MySQL/PostgreSQL)
$table->fullText('description');
$table->fullText(['title', 'description'], 'products_search_index');
// Spatial (PostgreSQL)
$table->spatialIndex('location');
});🔧 Modifier des tables
Ajouter des colonnes
php artisan make:migration add_avatar_to_users_table --table=usersSchema::table('users', function (Blueprint $table) {
$table->string('avatar')->nullable();
$table->integer('login_count')->default(0)->after('email');
$table->timestamp('last_login_at')->nullable();
});Modifier des colonnes
// Renommer
$table->renameColumn('name', 'full_name');
// Modifier type (requiert doctrine/dbal)
$table->string('name', 100)->change();
$table->integer('votes')->unsigned()->change();
$table->text('description')->nullable(false)->change();Supprimer des éléments
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('avatar');
$table->dropColumn(['avatar', 'login_count', 'last_login_at']);
// Supprimer index
$table->dropIndex('users_name_index');
$table->dropUnique('users_email_unique');
// Supprimer contraintes
$table->dropForeign(['category_id']);
$table->dropForeign('posts_category_id_foreign');
});🌱 Seeders
Création
php artisan make:seeder UserSeeder
php artisan make:model Category --seedSeeder de base
<?php
// database/seeders/UserSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
class UserSeeder extends Seeder
{
public function run(): void
{
// Création manuelle
User::create([
'name' => 'Admin User',
'email' => 'admin@example.com',
'password' => Hash::make('password'),
'email_verified_at' => now(),
]);
// Avec factory
User::factory()->count(10)->create();
User::factory()->unverified()->count(5)->create();
User::factory()->admin()->create();
}
}DatabaseSeeder principal
<?php
// database/seeders/DatabaseSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
UserSeeder::class,
CategorySeeder::class,
PostSeeder::class,
]);
// Ou directement
// User::factory(10)->create();
}
}🏭 Factories
Création
php artisan make:factory UserFactory --model=UserFactory de base
<?php
// database/factories/UserFactory.php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
'remember_token' => Str::random(10),
];
}
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
public function admin(): static
{
return $this->state(fn (array $attributes) => [
'name' => 'Admin User',
'email' => 'admin@example.com',
]);
}
}Factory avec relations
<?php
// database/factories/PostFactory.php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
*/
class PostFactory extends Factory
{
public function definition(): array
{
return [
'title' => fake()->sentence(),
'content' => fake()->paragraphs(3, true),
'user_id' => User::factory(),
'category_id' => Category::inRandomOrder()->first()->id,
'published_at' => fake()->optional(0.7)->dateTime(),
'created_at' => fake()->dateTimeBetween('-1 year', 'now'),
'updated_at' => now(),
];
}
public function published(): static
{
return $this->state(fn (array $attributes) => [
'published_at' => fake()->dateTimeBetween('-6 months', 'now'),
]);
}
public function draft(): static
{
return $this->state(fn (array $attributes) => [
'published_at' => null,
]);
}
}🚀 Exécution
Migrations
php artisan migrate
php artisan migrate --force # production
php artisan migrate --path=database/migrations/xxx_create_users_table.php
php artisan migrate:status
php artisan migrate:rollback
php artisan migrate:rollback --step=3
php artisan migrate:reset
php artisan migrate:refresh
php artisan migrate:refresh --seed
php artisan migrate:fresh
php artisan migrate:fresh --seedSeeders
php artisan db:seed
php artisan db:seed --class=UserSeeder
php artisan db:seed --force
php artisan migrate:fresh --seed🎯 Environnements
Migrations conditionnelles
public function up(): void
{
if (app()->environment('local', 'testing')) {
Schema::create('debug_logs', function (Blueprint $table) {
$table->id();
$table->text('message');
$table->timestamps();
});
}
}Seeders par environnement
public function run(): void
{
$this->call([
CategorySeeder::class,
UserSeeder::class,
]);
if (app()->environment('local', 'testing')) {
$this->call([DevelopmentDataSeeder::class]);
}
if (app()->environment('production')) {
$this->call([ProductionDataSeeder::class]);
}
}🔍 Debug
Inspection
php artisan tinker
>>> Schema::getColumnListing('users');
>>> Schema::getColumnType('users', 'email');
>>> Schema::hasTable('users');
>>> Schema::hasColumn('users', 'avatar');SQL généré
php artisan migrate --pretend
php artisan migrate --pretend --path=database/migrations/xxx_create_users_table.phpLe Git pour votre base de données