@extends('layouts.docs') @section('title', 'Migrations - OG Framework') @section('body')
{{-- Floating shapes for styling --}}
{{-- Left Sidebar: Navigation --}} @include('docs.partials.sidebar') {{-- Main Content --}}
{{-- Page Header --}}
Voltar para Documentação

Database

Migrations

Sistema de versionamento de esquema de base de dados — Crie, execute e reverta alterações de forma controlada, rastreável e reversível.

{{-- Visão Geral --}}

Visão Geral

As migrations são arquivos PHP que definem alterações incrementais no banco de dados. Cada migration possui dois métodos:

{{-- Methods --}}

up()

Aplica as alterações (criar tabelas, adicionar colunas, etc.)

down()

Reverte as alterações (desfaz o que up() fez)

{{-- Characteristics Table --}}
Característica Descrição
Localização padrãoModules/database/migrations/
Tabela de controlemigrations
VersionamentoSubpastas opcionais por versão
Lock de execuçãoEvita execuções simultâneas
TransaçõesCada migration roda em transação
{{-- Arquitetura --}}

Arquitetura

Modules/database/
├── Migrate.php              # Runner principal
├── Migration.php            # Classe base para migrations
├── MigrationInterface.php   # Interface que todas migrations implementam
├── MigrationException.php   # Exceções do sistema
└── migrations/              # Pasta padrão de migrations
    ├── 2024_01_01_*.php
    ├── 4086/                # Subpasta de versão
    │   └── 2023_08_22_*.php
    └── 4087/
        └── 2023_08_23_*.php
{{-- Migrations Table --}}

Tabela migrations

Campo Tipo Descrição
idBIGINTIdentificador único
migrationVARCHAR(255)Nome do arquivo (sem .php)
batchINTNúmero do batch (agrupamento)
versionVARCHAR(255)Versão/subpasta da migration
pathVARCHAR(255)Caminho base usado pelo runner
{{-- Pré-requisitos --}}

Pré-requisitos

⚠️ Contexto de Tenant

Em ambiente multi-tenant, os comandos CLI precisam do contexto HTTP para resolver a conexão correta:

# Executar para um tenant específico
HTTP_HOST=meudominio.com php og migrate

# Exemplo com subdomínio
HTTP_HOST=cliente1.officegest.com php og migrate
{{-- Criando Migrations --}}

Criando Migrations

{{-- Basic Command --}}

Comando Básico

php og make:migration add_email_verified_at_to_users

Gera: Modules/database/migrations/2024_12_17_001726_add_email_verified_at_to_users.php

{{-- Create Table --}}

Migration de Criação de Tabela

php og make:migration create_orders_table --table=orders

💡 O comando também detecta automaticamente migrations com padrão create_*_table no nome e usa o stub de criação de tabela.

{{-- Versioning --}}

Versionamento por Subpasta

php og make:migration create_invoices_table --table=invoices --migration-version=202512

Resultado: Modules/database/migrations/202512/2024_12_17_001726_create_invoices_table.php

{{-- Custom Path --}}

Path Customizado

# Caminho relativo ao root
php og make:migration create_addon_table --table=addon_items --path=addons/MeuAddon/migrations

# Caminho absoluto
php og make:migration create_addon_table --table=addon_items --path=/abs/custom/migrations
{{-- Estrutura de uma Migration --}}

Estrutura de uma Migration

{{-- Anatomy --}}

Anatomia Completa

<?php

use Og\Modules\database\Migration;
use Og\Modules\database\MigrationInterface;

return new class extends Migration implements MigrationInterface {

    public function up(): void
    {
        // Alterações a aplicar
    }

    public function down(): void
    {
        // Reverter as alterações
    }
};
{{-- Properties --}}
Propriedade Tipo Descrição
$this->databaseOGDBInstância do query builder
$this->dbforgeDatabaseForgeAPI para manipular estrutura
$this->applicationobjectConfigurações da aplicação ($a)
$this->modulesobjectReferência aos módulos ($m)
{{-- DBForge API --}}

DBForge API — Manipulação de Estrutura

O $this->dbforge é a API principal para criar e modificar estrutura de tabelas.

{{-- Create Table Example --}}

Criar Tabela

public function up(): void
{
    $this->dbforge->add_field([
        'id' => [
            'type'           => 'BIGINT',
            'constraint'     => 20,
            'unsigned'       => true,
            'auto_increment' => true,
        ],
        'name' => [
            'type'       => 'VARCHAR',
            'constraint' => 255,
            'null'       => false,
        ],
        'status' => [
            'type'       => 'ENUM',
            'constraint' => ['active', 'inactive', 'pending'],
            'default'    => 'pending',
        ],
        'created_at' => [
            'type'    => 'DATETIME',
            'null'    => false,
            'default' => 'CURRENT_TIMESTAMP',
        ],
    ]);

    $this->dbforge->add_key('id', true); // Chave primária
    $this->dbforge->add_key('email');    // Índice simples

    $this->dbforge->create_table(table: 'orders', if_not_exists: true);
}

public function down(): void
{
    $this->dbforge->drop_table('orders');
}
{{-- Field Types --}}

Tipos de Campo Comuns

Tipo Uso Exemplo
INTNúmeros inteiros['type' => 'INT', 'constraint' => 11]
BIGINTIDs, contadores['type' => 'BIGINT', 'unsigned' => true]
VARCHARStrings curtas['type' => 'VARCHAR', 'constraint' => 255]
TEXTStrings longas['type' => 'TEXT']
DECIMALValores monetários['type' => 'DECIMAL', 'constraint' => '10,2']
DATETIMEData e hora['type' => 'DATETIME', 'null' => true]
ENUMValores fixos['type' => 'ENUM', 'constraint' => ['a', 'b']]
JSONDados estruturados['type' => 'JSON']
{{-- Add Column --}}

Adicionar Coluna a Tabela Existente

public function up(): void
{
    $this->dbforge->add_column('users', [
        'phone' => [
            'type'       => 'VARCHAR',
            'constraint' => 20,
            'null'       => true,
            'after'      => 'email', // Posição da coluna
        ],
    ]);
}

public function down(): void
{
    $this->dbforge->drop_column('users', 'phone');
}
{{-- Modify Column --}}

Modificar Coluna

public function up(): void
{
    $this->dbforge->modify_column('users', [
        'name' => [
            'name'       => 'full_name', // Renomear
            'type'       => 'VARCHAR',
            'constraint' => 500,         // Aumentar tamanho
        ],
    ]);
}
{{-- Foreign Keys --}}

Foreign Keys

$this->dbforge->add_field([
    'id' => ['type' => 'BIGINT', 'unsigned' => true, 'auto_increment' => true],
    'user_id' => ['type' => 'BIGINT', 'unsigned' => true, 'null' => false],
    'product_id' => ['type' => 'BIGINT', 'unsigned' => true, 'null' => false],

    // Foreign keys inline (padrão usado no projeto)
    'CONSTRAINT FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE',
    'CONSTRAINT FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE RESTRICT ON UPDATE CASCADE',
]);

$this->dbforge->add_key('id', true);
$this->dbforge->create_table(table: 'orders', if_not_exists: true);

⚠️ Importante

  • • As tabelas referenciadas já devem existir
  • • Os tipos das colunas devem ser compatíveis (ambos BIGINT UNSIGNED)
  • • O engine da tabela deve suportar FKs (InnoDB)
{{-- Query Builder --}}

Usando o Query Builder

Para alterações de dados (DML), use $this->database:

public function up(): void
{
    // Inserir dados iniciais
    $this->database->insert('settings', [
        'key'   => 'app_version',
        'value' => '2.0.0',
    ]);

    // Atualizar registros
    $this->database->where('status', 'old')->update('orders', [
        'status' => 'legacy',
    ]);
}

public function down(): void
{
    $this->database->delete('settings', ['key' => 'app_version']);

    $this->database->where('status', 'legacy')->update('orders', [
        'status' => 'old',
    ]);
}
{{-- Executando Migrations --}}

Executando Migrations

Rodar Todas Pendentes

php og migrate

Versão Específica

php og migrate --migration-version=202512
{{-- Execution Flow --}}

Fluxo de Execução

1 Verifica/cria tabela migrations
2 Adquire lock (migration.lock)
3 Lista arquivos e filtra já executados
4 Para cada pendente: transação → up() → registra → commit
5 Libera lock
{{-- Revertendo Migrations --}}

Revertendo Migrations

Comandos de Rollback

# Reverter último batch (padrão: 1)
php og migrate:rollback

# Reverter N batches
php og migrate:rollback --step=2

# Reverter versão específica
php og migrate:rollback --step=1 --migration-version=202512
{{-- Batch Explanation --}}

💡 O que é um Batch?

Um batch é um grupo de migrations executadas juntas em uma única chamada de php og migrate. Cada execução incrementa o número do batch.

| migration                            | batch |
|--------------------------------------|-------|
| 2024_01_01_create_users_table        |   1   |
| 2024_01_02_create_orders_table       |   1   |  ← Executadas juntas
| 2024_01_15_add_phone_to_users        |   2   |  ← Executada depois
| 2024_01_16_create_invoices_table     |   2   |

--step=1 reverte batch 2 · --step=2 reverte batches 2 e 1

{{-- Boas Práticas --}}

Padrões e Boas Práticas

{{-- Naming --}}

✅ Bom: específico e descritivo

add_email_verified_at_to_users
create_order_items_table
drop_legacy_columns_from_products

❌ Evitar: genérico ou ambíguo

update_users
fix_database
changes
{{-- Always implement down() --}}

Sempre Implemente o down()

// ✅ Migration reversível
public function up(): void
{
    $this->dbforge->add_column('users', [
        'avatar_url' => ['type' => 'VARCHAR', 'constraint' => 500, 'null' => true],
    ]);
}

public function down(): void
{
    $this->dbforge->drop_column('users', 'avatar_url');
}
{{-- Safety Checks --}}

Verificações de Segurança

public function up(): void
{
    // Verificar se tabela existe antes de adicionar coluna
    if ($this->database->table_exists('users')) {
        // Verificar se coluna não existe antes de adicionar
        if (!$this->database->field_exists('phone', 'users')) {
            $this->dbforge->add_column('users', [
                'phone' => ['type' => 'VARCHAR', 'constraint' => 20],
            ]);
        }
    }
}
{{-- Lock Mechanism --}}

Mecanismo de Lock

Propriedade Valor
Arquivocache/migration/migration.lock
Timeout5 minutos (300 segundos)
ConteúdoPID do processo

Comportamento

  • Lock existe e PID ativo → Execução bloqueada
  • Lock existe mas PID morto → Lock removido, execução continua
  • Lock existe e timeout expirado → Lock removido, execução continua
{{-- DDL Warning --}}

⚠️ Operações DDL e Transações

Operações DDL (CREATE TABLE, ALTER TABLE) no MySQL/MariaDB fazem commit implícito. Se uma migration falhar após criar uma tabela, a tabela não será automaticamente removida. Sempre verifique o estado do banco após falhas.

{{-- Troubleshooting --}}

Troubleshooting

{{-- Common Errors --}}

⚠️ "HTTP_HOST is not defined"

HTTP_HOST=meudominio.com php og migrate

⚠️ "Another migration is in progress"

# Ver se há processos de migration
ps aux | grep migrate

# Se não houver, o lock está órfão:
rm cache/migration/migration.lock

⚠️ Tabela migrations Corrompida

-- Ver migrations registradas
SELECT * FROM migrations ORDER BY id;

-- Marcar migration como executada manualmente
INSERT INTO migrations (migration, batch, version, path)
VALUES ('2024_01_01_create_users_table', 1, NULL, '/Modules/database/migrations');
{{-- Referência Rápida --}}

Referência Rápida

{{-- CLI Commands --}}

Comandos CLI

Comando Descrição
php og make:migration <nome>Criar migration
php og make:migration <nome> --table=<tabela>Criar migration de tabela
php og migrateExecutar pendentes
php og migrate --migration-version=<v>Executar versão específica
php og migrate --path=<path>Executar migrations em um caminho específico
php og migrate:rollbackReverter último batch
php og migrate:rollback --step=<N>Reverter N batches
{{-- DBForge Methods --}}

DBForge Métodos

Método Descrição
add_field(array $fields)Definir campos para nova tabela
add_key($key, $primary, $unique)Adicionar índice
create_table($table, $if_not_exists)Criar tabela
drop_table($table)Remover tabela
add_column($table, $fields)Adicionar coluna
drop_column($table, $column)Remover coluna
modify_column($table, $fields)Modificar coluna
{{-- Template --}}

Template de Migration

<?php

use Og\Modules\database\Migration;
use Og\Modules\database\MigrationInterface;

return new class extends Migration implements MigrationInterface {
    public function up(): void
    {
        // Sua lógica aqui
    }

    public function down(): void
    {
        // Reverter alterações
    }
};
@endsection