eril / schema-engine
Schema-first database migration engine for PHP.
Requires
- php: ^8.2
- ext-pdo: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-05-26 12:04:11 UTC
README
A modern schema-first migration engine for PHP.
PHP Schema Engine lets you define your database structure using a clean PHP DSL, compare it against the current database schema, generate SQL automatically, and apply changes through a lightweight CLI.
Current version:
0.3.0-alpha
Features
- Schema-first database definition
- Fluent PHP DSL
- MySQL/MariaDB support
- Automatic schema introspection
- Table and column diffing
- Partial index diffing
- SQL generation
- Migration execution
- Migration history
- Model generation
- Dry-run mode
- Destructive operation protection
- Database reset support
- Snapshot generation
- Foreign key support
- Debug trace mode
Installation
composer require erilshackle/php-schema-engine
For local development:
composer install
Initialization
Generate the default configuration file:
php bin/migrate --init
This creates:
schema-engine.php
Configuration
Generated config example:
<?php return [ // Schema file path 'schema' => '/database/schema.php', // Database connection configuration 'database' => [ 'driver' => 'mysql', 'host' => $_ENV['DB_HOST'] ?? '127.0.0.1', 'port' => $_ENV['DB_PORT'] ?? 3306, 'database' => $_ENV['DB_NAME'] ?? 'app', 'username' => $_ENV['DB_USER'] ?? 'root', 'password' => $_ENV['DB_PASS'] ?? '', ], // Model generator setup 'generator' => [ 'models' => [ 'enabled' => false, 'namespace' => 'App\\Models', 'path' => '/app/Models', 'extends' => null, ], ], ];
Optional bootstrap file:
bootstrap.php
Useful for:
- loading
.env - bootstrapping frameworks
- loading helpers
- custom runtime setup
Basic Usage
Create a schema file:
<?php use SchemaEngine\DSL\Schema; use SchemaEngine\DSL\Table; return function (Schema $schema) { $schema->table('users', function (Table $t) { $t->id(); $t->string('name'); $t->string('email') ->unique(); $t->timestamps(); }); };
Run a dry-run first:
php bin/migrate --dry-run
Apply the migration:
php bin/migrate --yes
Defining Tables
$schema->table('users', function ($t) { $t->id(); $t->string('name'); $t->string('email') ->unique(); $t->int('age') ->default(0); $t->timestamps(); });
Column Types
$t->id(); $t->string('name'); $t->char('code', 2); $t->text('body'); $t->longText('content'); $t->int('age'); $t->bigInt('views'); $t->float('rating'); $t->double('score'); $t->decimal('price', 10, 2); $t->boolean('active'); $t->datetime('published_at'); $t->timestamp('created_at'); $t->json('meta'); $t->uuid('uuid');
Column Modifiers
$t->string('email') ->unique(); $t->string('slug') ->index(); $t->string('name') ->nullable(); $t->int('age') ->default(0); $t->timestamp('created_at') ->defaultCurrentTimestamp(); $t->timestamp('updated_at') ->defaultRaw('CURRENT_TIMESTAMP');
Shortcuts
$t->id(); $t->timestamps(); $t->createdAt(); $t->updatedAt(); $t->softDeletes(); $t->rememberToken();
Indexes
Single-column indexes:
$t->string('email') ->unique(); $t->string('slug') ->index();
Composite indexes:
$t->unique(['email', 'tenant_id']); $t->index(['first_name', 'last_name']);
Indexes are automatically introspected and partially diffed.
Current V1 behavior:
- missing indexes are automatically added
- removed indexes are automatically dropped
- changed indexes are ignored intentionally with warnings
Example warning:
Index 'email_unique' on table 'users' differs from desired schema and was ignored.
Foreign Keys
Simple inferred relation:
$t->foreignId('user_id');
Explicit references:
$t->foreignId('user_id') ->constrained() // or references('users') ->cascadeOnDelete();
Custom table:
$t->foreignId('author_id') ->references('users');
Custom referenced column:
$t->uuid('author_id') ->references('users', 'uuid');
Available relation actions:
->cascadeOnDelete() ->cascadeOnUpdate() ->restrictOnDelete() ->restrictOnUpdate() ->nullOnDelete() ->nullOnUpdate()
In V1, foreign keys are generated when creating new tables only. Foreign key changes after table creation are not automatically migrated yet.
Model Generation
Generate models from your schema:
php bin/migrate --generate-models
Example generated model:
<?php namespace App\Models; class User { protected string $table = 'users'; }
Generator configuration:
'generator' => [ 'models' => [ 'enabled' => true, 'namespace' => 'App\\Models', 'path' => '/app/Models', 'extends' => 'App\\Core\\Model', ], ],
CLI
Dry-run:
php bin/migrate --dry-run
Apply migrations without confirmation:
php bin/migrate --yes
Allow destructive operations:
php bin/migrate --force
Show migration history:
php bin/migrate --status
Reset database:
php bin/migrate --fresh --force --yes
Reset database and clear history:
php bin/migrate --fresh --force --yes --clear-history
Generate models:
php bin/migrate --generate-models
Show stack trace on exceptions:
php bin/migrate --trace
Migration History
Executed operations are stored in:
schema_migrations
This table is managed internally by the engine and ignored during schema diffing.
Safety
By default, destructive operations are blocked.
This includes:
- dropping tables
- dropping columns
- dropping indexes
To allow destructive operations:
php bin/migrate --force
Debugging
Enable stack traces during execution:
php bin/migrate --trace
Example output:
[ERROR] Table users already exists File: .../Migrator.php:62 Stack trace: #0 .../Migrator.php:62 run() #1 .../MigrateCommand.php:88 printPlannedSql() #2 .../bin/migrate:24 run()
Current Limitations
PHP Schema Engine 0.1.0-alpha is intentionally conservative.
Current V1 limitations:
- MySQL/MariaDB only
- Index diffing is partial and intentionally conservative
- Existing indexes are not automatically modified
- Foreign key changes after table creation are not automatically migrated
- Rename detection is disabled by default
- Table recreation is not implemented yet
- No PostgreSQL grammar yet
- No SQLite grammar yet
- No ORM/query builder layer yet
Philosophy
PHP Schema Engine follows a schema-first approach.
The schema file is the source of truth.
Schema DSL
↓
Schema Metadata
↓
Database Introspection
↓
Diff Engine
↓
SQL Generation
↓
Migration Execution
The DSL is designed to stay expressive, while the internal architecture keeps metadata, diffing, SQL generation, and execution separated.
Roadmap
V0.1
- Schema DSL
- MySQL introspection
- Column diffing
- Partial index diffing
- SQL generator
- CLI
- Migration history
- Model generation
- Foreign keys on create
V0.2
- migrate:fresh
- migrate:reset
- better CLI output
- schema snapshots
- explicit rename operations
V0.3
- advanced index diffing
- foreign key diffing
- table recreation mode
- rollback support
V1.0
- stable public API
- stronger type system
- advanced MySQL grammar
- multi-driver foundation
License
MIT