Usage

Core patterns and workflows for WPZylos Migrations.

Creating Migrations

Migrations extend the base Migration class and use $this->db (Connection) and helper methods.

Basic Migration

// database/migrations/2024_01_15_create_products_table.php
<?php

declare(strict_types=1);

namespace MyPlugin\Database\Migrations;

use WPZylos\Framework\Migrations\Migration;

class CreateProductsTable extends Migration
{
    public function up(): void
    {
        $this->create('myplugin_products', [
            'id'          => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
            'name'        => 'varchar(255) NOT NULL',
            'description' => 'text DEFAULT NULL',
            'price'       => 'decimal(10,2) NOT NULL DEFAULT 0.00',
            'stock'       => 'int(11) NOT NULL DEFAULT 0',
            'created_at'  => 'datetime DEFAULT CURRENT_TIMESTAMP',
            'updated_at'  => 'datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
        ], [
            'PRIMARY KEY (id)',
        ]);
    }

    public function down(): void
    {
        $this->drop('myplugin_products');
    }
}

Note: The create() helper uses WordPress dbDelta() internally. Table names are automatically prefixed with $wpdb->prefix.


Migration Helpers

The base Migration class provides these protected helper methods:

create()

$this->create('table_name', [
    'column_name' => 'column_definition',
], [
    'PRIMARY KEY (id)',
    'KEY idx_status (status)',
]);

dropTable() / drop()

$this->dropTable('table_name');
// or alias:
$this->drop('table_name');

charsetCollate()

$charset = $this->charsetCollate();
// Returns e.g.: "DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"

Raw SQL via Connection

public function up(): void
{
    $prefix = $this->db->wpdb()->prefix;
    $charset = $this->charsetCollate();

    $this->runDbDelta("
        CREATE TABLE `{$prefix}myplugin_orders` (
            `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            `user_id` bigint(20) unsigned NOT NULL,
            `total` decimal(10,2) NOT NULL DEFAULT 0.00,
            `status` varchar(50) NOT NULL DEFAULT 'pending',
            `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY idx_user (user_id),
            KEY idx_status (status)
        ) {$charset};
    ");
}

Running Migrations

Via Container

$migrator = $app->make(Migrator::class);

// Run all pending migrations
$ran = $migrator->run();
// ['2024_01_15_create_products_table']

// Rollback last batch
$rolledBack = $migrator->rollback();

// Rollback last 2 batches
$rolledBack = $migrator->rollback(2);

// Get migration status
$status = $migrator->status();

// Get only pending migrations
$pending = $migrator->getPending();

Via WP-CLI

wp myplugin migrate              # Run pending
wp myplugin migrate:rollback     # Rollback last batch
wp myplugin migrate:status       # Show status

Modifying Tables

Use raw SQL for table modifications:

class AddSkuToProducts extends Migration
{
    public function up(): void
    {
        $prefix = $this->db->wpdb()->prefix;

        $this->db->query(
            "ALTER TABLE `{$prefix}myplugin_products` ADD COLUMN `sku` varchar(50) DEFAULT NULL AFTER `name`"
        );
    }

    public function down(): void
    {
        $prefix = $this->db->wpdb()->prefix;

        $this->db->query(
            "ALTER TABLE `{$prefix}myplugin_products` DROP COLUMN `sku`"
        );
    }
}

Migration Naming

Use descriptive names with timestamps. Files are sorted alphabetically:

2024_01_15_000001_create_users_table.php
2024_01_15_000002_create_products_table.php
2024_01_16_000001_add_sku_to_products.php
2024_01_17_000001_create_orders_table.php

ServiceProvider Registration

$app->register(new \WPZylos\Framework\Migrations\MigrationsServiceProvider());

This registers:

  • MigrationRepository::class — Tracks migration state
  • Migrator::class — Migration runner

Dependencies

  • wpzylos-database — Required for Connection
  • wpzylos-core — Required for ContextInterface