Capítulo 05: Migrations e Schema Builder do Laravel
Especialização em Backend com PHP & Laravel • Laravel 11 & PHP 8.3+ • Eloquent ORM, Sanctum, Filas, Horizon e APIs Corporativas
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Migration (arquivo versionado)"] --> B["Schema::create() / Schema::table()"]
B --> C["Blueprint: colunas, tipos, índices"]
C --> D["Foreign Keys e Constraints"]
D --> E["php artisan migrate"]
E --> F["Banco de Dados atualizado"]
F -.rollback.-> G["método down() reverte"]
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style E fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style G fill:#ffebee,stroke:#e53935,stroke-width:2px
🏛️ 1. Migrations como Controle de Versão do Schema
Uma migration é um arquivo PHP com timestamp no nome que descreve uma alteração no schema do banco de forma reversível: todo up() (aplica a mudança) deve ter um down() correspondente (desfaz exatamente essa mudança). Isso torna o schema do banco versionável junto com o código-fonte, permitindo que qualquer desenvolvedor reconstrua o banco do zero rodando as migrations em ordem:
<?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('produtos', function (Blueprint $table) {
$table->id();
$table->string('nome', 150)->index();
$table->string('sku', 50)->unique();
$table->decimal('preco', 10, 2);
$table->boolean('ativo')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('produtos');
}
};
O index() acelera buscas por nome; o unique() em sku garante integridade a nível de banco (não apenas na validação do Form Request) — a última linha de defesa contra duplicidade em cenários de concorrência.
🔗 2. Chaves Estrangeiras e Integridade Referencial
foreignId()->constrained() é açúcar sintático que infere a tabela (users) e coluna (id) referenciadas a partir do nome da coluna (user_id), evitando repetir strings mágicas:
Schema::create('pedidos', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->decimal('total', 10, 2);
$table->string('status')->default('PENDENTE');
$table->timestamps();
});
cascadeOnDelete() delega ao banco de dados a exclusão em cascata dos pedidos quando o usuário é removido — mais confiável que apagar registros relacionados manualmente em PHP, pois é atômico e não depende de o código da aplicação lembrar de fazê-lo em todo ponto de exclusão.
🛠️ 3. Alterando Tabelas Existentes com Segurança
Schema::table() (em vez de create()) altera uma tabela já existente. O down() de uma migration de alteração deve desfazer exatamente o que o up() fez — nunca recriar a tabela inteira:
public function up(): void
{
Schema::table('pedidos', function (Blueprint $table) {
$table->decimal('desconto', 8, 2)->default(0.0)->after('total');
});
}
public function down(): void
{
Schema::table('pedidos', function (Blueprint $table) {
$table->dropColumn('desconto');
});
}
Gotcha de produção: php artisan migrate:fresh (dropa e recria tudo) é destrutivo e só deve ser usado em desenvolvimento/CI. Em produção, aplica-se sempre php artisan migrate --force, incremental, e o down() de cada migration é o único caminho seguro de rollback (php artisan migrate:rollback).