Skip to content

Chapter 3 of 7

Database Migrations, Schema, and Data Seeding

Migrations are Laravel's version control for database schemas. Generate one with php artisan make:migration create_posts_table, define schema changes in up(), and put the inverse in down(). Run with php artisan migrate, roll back with migrate:rollback, refresh with migrate:refresh (rollback plus re-run, keeping tables), or use migrate:fresh to drop all tables and re-run from scratch, which is preferred when the schema is broken. Add a column to an existing table with make:migration add_email_to_users_table --table=users, then $table->string('email')->after('name'). Renaming columns worked via the doctrine/dbal package on older Laravel but is native from Laravel 10 onward.

The schema builder exposes column types including string, integer, json, timestamp, softDeletes, uuid, and morphs, plus modifiers such as nullable, default, unique, index, and after. Foreign keys are concise with foreignId('user_id')->constrained()->cascadeOnDelete(), and constrained('users') overrides the inferred table name. Add nullOnDelete(), restrictOnDelete(), or cascadeOnUpdate() for finer control. Composite indexes ($table->unique(['user_id', 'slot'])) accelerate queries that filter by their leftmost columns. Drop columns with dropColumn('is_admin') or rename with renameColumn('old', 'new'). For database features the builder does not expose, fall back to DB::statement() for raw SQL inside migrations, and write the inverse in down().

Seeders populate the database with sample or fixture data, and factories generate model blueprints using Faker. Create a seeder with make:seeder and call other seeders from DatabaseSeeder::run() via $this->call([UserSeeder::class, PostSeeder::class]); run them with php artisan db:seed or combine with migrate --seed for a one-step rebuild. A factory's definition() returns the attribute defaults, and returning another factory as a value (e.g., 'user_id' => User::factory()) automatically creates and links the parent. Define states like suspended() with return $this->state(['suspended_at' => now()]), then chain User::factory()->admin()->suspended()->create(). Use truncate() to remove all rows and reset auto-increment, but note that it cannot be rolled back inside a MySQL transaction.

Laravel's database layer also handles transactions, multiple connections, and query inspection. Wrap a closure in DB::transaction($cb, $attempts) to roll back automatically on any exception and optionally retry on serialization failures (deadlocks). For more granular control, use DB::beginTransaction(), commit(), and rollBack() manually, and defer side effects until commit with DB::afterCommit(fn() => ...). Configure additional connections in config/database.php and access them via DB::connection('pgsql') or set \(connection on a model. Read/write splits use nested arrays under the connection key. To debug query traffic, register DB::listen(fn (\)q) => logger()->info($q->sql, $q->bindings)) in a service provider during development. The DB facade also supports raw table queries (DB::table('users')->where(...)->get()) returning stdClass, plus UNIONs via DB::table('b')->union($q)->get() and subqueries through addSelect(['post_count' => DB::table(...)->selectRaw('count(*)')->whereColumn(...)]).

All chapters
  1. 1The HTTP Layer: Routing and Middleware
  2. 2Eloquent ORM: Models, Relationships, and Querying
  3. 3Database Migrations, Schema, and Data Seeding
  4. 4Blade Templates, Views, and Pagination
  5. 5Authentication, Authorization, and Sessions
  6. 6Queues, Events, Notifications, and Broadcasting
  7. 7Service Container, Facades, and Cross-Cutting Concerns

Drill it

Reading is not remembering. These come from the Laravel Framework deck:

Q

What is Laravel Eloquent ORM?

Eloquent is Laravel's built-in Active Record ORM. Each database table has a corresponding Model class used to interact with that table. It provides an expressiv...

Q

How do you define a one-to-many relationship in Eloquent?

In the parent model, define a method returning hasMany():public function posts() { return $this->hasMany(Post::class); }In the child model, define the inverse w...

Q

What is eager loading in Eloquent and why is it important?

Eager loading solves the N+1 query problem by loading relationships upfront:$books = Book::with('author')->get();Without it, accessing a relationship in a loop...

Q

How do you create a new Eloquent model and migration together?

Use the Artisan command:php artisan make:model Post -mThe -m flag generates a migration file alongside the model. You can also add -f for a factory and -s for a...