jorgemddev/kumbiaphp-migrations

Sistema de migraciones de base de datos para KumbiaPHP, inspirado en Laravel Migrations.

Maintainers

Package info

github.com/jorgemddev/kumbiaphp-migrations

Type:composer-plugin

pkg:composer/jorgemddev/kumbiaphp-migrations

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.2.4 2026-03-15 00:50 UTC

This package is auto-updated.

Last update: 2026-03-15 00:51:34 UTC


README

Sistema de migraciones de base de datos para KumbiaPHP, inspirado en Laravel Migrations. Permite versionar y gestionar los cambios en el esquema de tu base de datos de forma ordenada y reproducible.

Requisitos

  • PHP 7.0 o superior
  • Extensión PDO habilitada
  • KumbiaPHP (cualquier versión con app/config/databases.php)
  • MySQL/MariaDB, PostgreSQL o SQLite

Instalación

Via Composer (recomendado)

composer require jorgemddev/kumbiaphp-migrations

El plugin copia automáticamente los binarios a app/bin/ al instalar. Luego crea la tabla de control:

php app/bin/migrate --install

Manual

Copia la carpeta migration/ dentro de app/libs/ de tu proyecto KumbiaPHP:

app/
├── libs/
│   └── migration/       ← aquí
├── migrations/          ← se crea automáticamente
├── database/
│   └── seeds/           ← seeders aquí
└── config/
    └── databases.php

Luego crea la tabla de control en tu base de datos:

php app/bin/migrate --install

Configuración

El sistema lee la configuración desde app/config/databases.php. Debe retornar un array con las conexiones:

<?php
return [
    'development' => [
        'type'     => 'mysql',       // mysql | pgsql | sqlite
        'host'     => 'localhost',
        'name'     => 'mi_base',
        'username' => 'root',
        'password' => '',
        'charset'  => 'utf8mb4',     // opcional, por defecto utf8mb4
        'port'     => 3306,          // opcional
    ],
    'production' => [
        'type'     => 'mysql',
        'host'     => 'db.servidor.com',
        'name'     => 'mi_base_prod',
        'username' => 'usuario',
        'password' => 'secreto',
    ],
];

El entorno se detecta automáticamente: si la constante PRODUCTION está definida y es true, se usa la conexión production; de lo contrario, development.

Comandos

Todos los comandos se ejecutan desde la raíz del proyecto:

# Ejecutar migraciones pendientes
php app/bin/migrate

# Ver estado de todas las migraciones
php app/bin/migrate --status

# Revertir el último lote de migraciones
php app/bin/migrate --rollback

# Revertir todas las migraciones
php app/bin/migrate --reset

# Revertir todo y volver a ejecutar
php app/bin/migrate --refresh

# Crear la tabla de control (solo la primera vez)
php app/bin/migrate --install

# Ayuda
php app/bin/migrate --help

Crear migraciones

# Migración en blanco
php app/bin/migrate make:migration nombre_descriptivo

# Para crear una tabla nueva
php app/bin/migrate make:migration create_products_table --create=products

# Para modificar una tabla existente
php app/bin/migrate make:migration add_price_to_products --table=products

Esto genera un archivo en app/migrations/ con el formato YYYY_MM_DD_HHMMSS_nombre.php.

Escribir migraciones

Cada migración es una clase PHP con dos métodos: up() para aplicar el cambio y down() para revertirlo.

Crear una tabla

<?php

class CreateProductsTable extends Migration
{
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('sku', 100)->unique();
            $table->decimal('price', 10, 2);
            $table->integer('stock')->default(0);
            $table->boolean('is_active')->default(true);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('products');
    }
}

Modificar una tabla existente

<?php

class AddDescriptionToProductsTable extends Migration
{
    public function up()
    {
        Schema::table('products', function (Blueprint $table) {
            $table->text('description')->nullable()->after('name');
            $table->string('image')->nullable();
        });
    }

    public function down()
    {
        Schema::table('products', function (Blueprint $table) {
            $table->dropColumn(['description', 'image']);
        });
    }
}

Tabla con clave foránea

<?php

class CreateOrdersTable extends Migration
{
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->enum('status', ['pending', 'paid', 'cancelled'])->default('pending');
            $table->decimal('total', 10, 2);
            $table->timestamps();
            $table->softDeletes();

            $table->foreign('user_id')
                  ->references('id')
                  ->on('users')
                  ->onDelete('cascade');
        });
    }

    public function down()
    {
        Schema::dropIfExists('orders');
    }
}

Tipos de columnas disponibles

Método Tipo SQL
bigIncrements('id') BIGINT UNSIGNED AUTO_INCREMENT PK
increments('id') INT UNSIGNED AUTO_INCREMENT PK
string('col', 255) VARCHAR
char('col', 10) CHAR
text('col') TEXT
mediumText('col') MEDIUMTEXT
longText('col') LONGTEXT
integer('col') INT
tinyInteger('col') TINYINT
smallInteger('col') SMALLINT
bigInteger('col') BIGINT
unsignedInteger('col') INT UNSIGNED
unsignedBigInteger('col') BIGINT UNSIGNED
float('col', 8, 2) FLOAT
double('col') DOUBLE
decimal('col', 10, 2) DECIMAL
boolean('col') TINYINT(1)
enum('col', ['a','b']) ENUM
json('col') JSON
date('col') DATE
dateTime('col') DATETIME
timestamp('col') TIMESTAMP
timestamps() created_at + updated_at
softDeletes() deleted_at nullable
binary('col') BLOB
uuid('col') CHAR(36)
ipAddress('col') VARCHAR(45)
macAddress('col') VARCHAR(17)

Modificadores de columna

Se encadenan sobre cualquier definición de columna:

$table->string('email')->unique()->nullable(false);
$table->integer('views')->default(0)->unsigned();
$table->string('bio')->nullable()->after('email');
$table->timestamp('verified_at')->nullable()->useCurrent();
$table->string('slug')->index();
Modificador Descripción
->nullable() Permite NULL
->default($valor) Valor por defecto
->unsigned() Sin signo (MySQL)
->unique() Índice único
->index() Índice normal
->after('columna') Posición (MySQL)
->first() Primera posición (MySQL)
->comment('texto') Comentario (MySQL)
->useCurrent() DEFAULT CURRENT_TIMESTAMP

Claves foráneas

// Definición completa
$table->foreign('category_id')
      ->references('id')
      ->on('categories')
      ->onDelete('cascade')   // CASCADE | RESTRICT | SET NULL | NO ACTION
      ->onUpdate('restrict');

// Métodos abreviados
$table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete();
$table->foreign('role_id')->references('id')->on('roles')->restrictOnDelete();
$table->foreign('parent_id')->references('id')->on('categories')->nullOnDelete();

// Eliminar clave foránea
$table->dropForeign('user_id_foreign');

Índices

// En la definición de columna
$table->string('email')->unique();
$table->string('slug')->index();

// Como comando independiente
$table->unique(['email', 'tenant_id'], 'unique_email_per_tenant');
$table->index(['last_name', 'first_name']);
$table->primary(['id', 'type']);

// Eliminar índices
$table->dropUnique('unique_email_per_tenant');
$table->dropIndex('products_name_index');
$table->dropPrimary();

Operaciones de esquema

// Verificar existencia
if (!Schema::hasTable('products')) { ... }
if (!Schema::hasColumn('products', 'price')) { ... }

// Renombrar tabla
Schema::rename('old_name', 'new_name');

// Eliminar tabla
Schema::drop('products');
Schema::dropIfExists('products');

// Deshabilitar claves foráneas temporalmente
Schema::withoutForeignKeyConstraints(function () {
    Schema::drop('users');
});

Introspección de base de datos

Permite inspeccionar la estructura actual de la base de datos. Compatible con MySQL, PostgreSQL y SQLite.

Listar tablas

$tables = Schema::getTables();
// ['categories', 'migrations', 'orders', 'products', 'users']

Columnas de una tabla

$columns = Schema::getColumns('products');

Cada elemento retorna:

Campo Descripción
name Nombre de la columna
type Tipo de dato (varchar(255), int, etc.)
nullable true si acepta NULL
default Valor por defecto o null
key PRI, UNI, MUL (MySQL) / PRIMARY (otros) / null
extra auto_increment, etc. (MySQL)
comment Comentario de la columna (MySQL)
// Ejemplo de resultado
[
    ['name' => 'id',    'type' => 'bigint unsigned', 'nullable' => false, 'default' => null, 'key' => 'PRI', 'extra' => 'auto_increment', 'comment' => null],
    ['name' => 'name',  'type' => 'varchar(255)',    'nullable' => false, 'default' => null, 'key' => null,  'extra' => null,             'comment' => null],
    ['name' => 'price', 'type' => 'decimal(10,2)',   'nullable' => false, 'default' => null, 'key' => null,  'extra' => null,             'comment' => null],
]

Índices de una tabla

$indexes = Schema::getIndexes('products');

Cada elemento retorna:

Campo Descripción
name Nombre del índice
columns Array de columnas que lo componen
unique true si es único
primary true si es la clave primaria
// Ejemplo de resultado
[
    ['name' => 'PRIMARY',           'columns' => ['id'],  'unique' => true,  'primary' => true],
    ['name' => 'products_sku_unique', 'columns' => ['sku'], 'unique' => true,  'primary' => false],
    ['name' => 'products_name_index', 'columns' => ['name'],'unique' => false, 'primary' => false],
]

Claves foráneas de una tabla

$fks = Schema::getForeignKeys('orders');

Cada elemento retorna:

Campo Descripción
name Nombre de la constraint
columns Columnas locales
on_table Tabla referenciada
references Columnas referenciadas
on_delete Acción ON DELETE (CASCADE, RESTRICT, SET NULL, NO ACTION)
on_update Acción ON UPDATE
// Ejemplo de resultado
[
    [
        'name'       => 'orders_user_id_foreign',
        'columns'    => ['user_id'],
        'on_table'   => 'users',
        'references' => ['id'],
        'on_delete'  => 'CASCADE',
        'on_update'  => 'RESTRICT',
    ],
]

Uso con conexión específica

Todos los métodos de introspección respetan la conexión activa:

Schema::connection('production');

$tables = Schema::getTables();
$cols   = Schema::getColumns('users');
$fks    = Schema::getForeignKeys('orders');

Seeders

Los seeders poblan la base de datos con datos iniciales o de prueba. Se ubican en app/database/seeds/.

Crear un seeder

<?php
// app/database/seeds/UsersSeeder.php

class UsersSeeder extends Seeder
{
    public function run()
    {
        $this->insert('users', [
            [
                'name'       => 'Administrador',
                'email'      => 'admin@example.com',
                'password'   => password_hash('secret', PASSWORD_DEFAULT),
                'is_active'  => 1,
                'created_at' => date('Y-m-d H:i:s'),
            ],
        ]);
    }
}

DatabaseSeeder principal

<?php
// app/database/seeds/DatabaseSeeder.php

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call([
            UsersSeeder::class,
            CategoriesSeeder::class,
            ProductsSeeder::class,
        ]);
    }
}

Ejecutar seeders

# Ejecutar DatabaseSeeder
php app/bin/seed

# Ejecutar un seeder específico
php app/bin/seed --class=UsersSeeder

Métodos disponibles en Seeder

// Insertar filas
$this->insert('tabla', $filas);

// Vaciar tabla
$this->truncate('tabla');

// SQL personalizado
$this->query('UPDATE config SET value = ? WHERE key = ?', ['activo', 'status']);

// Llamar a otro seeder
$this->call(OtroSeeder::class);
$this->call([SeederA::class, SeederB::class]);

Flujo de trabajo recomendado

# 1. Crear la migración
php app/bin/migrate make:migration create_orders_table --create=orders

# 2. Editar app/migrations/YYYY_MM_DD_HHMMSS_create_orders_table.php

# 3. Aplicar
php app/bin/migrate

# 4. Si hay un error, revertir, corregir y volver a aplicar
php app/bin/migrate --rollback
php app/bin/migrate

# 5. Versionar
git add app/migrations/
git commit -m "feat: add orders table"

Despliegue en producción

git pull origin main
php app/bin/migrate

No ejecutes --reset ni --refresh en producción. Solo migrate para aplicar los cambios pendientes.

Estructura de archivos

app/
├── bin/
│   ├── migrate          # CLI de migraciones
│   └── seed             # CLI de seeders
├── libs/
│   └── migration/
│       ├── grammar/
│       │   ├── Grammar.php
│       │   ├── MySqlGrammar.php
│       │   ├── PostgresGrammar.php
│       │   └── SQLiteGrammar.php
│       ├── Migration.php
│       ├── MigrationCreator.php
│       ├── MigrationDatabase.php
│       ├── MigrationRepository.php
│       ├── Migrator.php
│       ├── Blueprint.php
│       ├── ColumnDefinition.php
│       ├── ForeignKeyDefinition.php
│       ├── Schema.php
│       └── Seeder.php
├── migrations/
│   └── 2024_01_15_120000_create_users_table.php
└── database/
    └── seeds/
        ├── DatabaseSeeder.php
        └── UsersSeeder.php