Skip to content

erilshackle/php-schema-engine

Repository files navigation

Schemage - Schema Engine

Smart database schema management for PHP. A modern schema-first migration engine for PHP.

Schemage 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.

PHP Tests License


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 eril/schemage

Initialization

Generate the default configuration file:

php bin/schemage --init

This creates:

Schemage.php
database/schema.php
database/bootstrap.sql
database/snapshots/

Configuration

Generated config example:

<?php

return [

    // Schema file path
    'schema' => 'database/schema.php',

    'bootstrap' => 'bootstrap.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 Schemage\DSL\Schema;
use Schemage\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/schemage --dry-run

Apply the migration:

php bin/schemage --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->foreign('user_id');

Explicit references:

$t->foreign('user_id')
    ->constrained() // or references('users')
    ->cascadeOnDelete();

Explicit references:

$t->foreign('user_id')
    ->constrained() // or references('users')
    ->cascadeOnDelete();

ou:

$t->foreign('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/schemage --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

Command Description
--init Initialize a new project
--yes Don't ask for confirmation
--dry-run Preview pending changes
--force Execute dangerous SQL like delete
--status Show migration history
--sql Export SQL for pending schema changes
--schema-sql Export SQL for the entire schema
--rollback Rollback the latest batch
--fresh Drop all tables
--clear-history Clear histries
--generate-models Generate model classes

Dry-run:

php bin/schemage --dry-run

Apply migrations without confirmation:

php bin/schemage --yes

Allow destructive operations:

php bin/schemage --force

Show migration history:

php bin/schemage --status

Reset database:

php bin/schemage --fresh --force --yes

Reset database and clear history:

php bin/schemage --fresh --force --yes --clear-history

Generate models:

php bin/schemage --generate-models

Show stack trace on exceptions:

php bin/schemage --trace

Migration History

Executed operations are stored in:

schema_migrations

This table is managed internally by the engine and ignored during schema diffing.


Exporting SQL

Schemage can generate SQL without executing migrations.

Export migration SQL (diff)

Generate the SQL required to migrate the current database schema:

php bin/schemage --sql

Example:

ALTER TABLE users
ADD COLUMN phone VARCHAR(20);

CREATE TABLE posts (...);

This command uses schema introspection and the diff engine.


Export full schema SQL

Generate SQL for the entire schema definition:

php bin/schemage --schema-sql

Example:

CREATE TABLE users (...);

CREATE TABLE posts (...);

This command ignores the current database and generates SQL directly from database/schema.php.


Save SQL to a file

bin/schemage --schema-sql > install.sql

or

bin/schemage --sql > migration.sql

notice not to use php because > stdout is not a tty

This is useful for:

  • Manual deployments
  • Database reviews
  • Backup scripts
  • Installation packages
  • CI/CD pipelines

Safety

By default, destructive operations are blocked.

This includes:

  • dropping tables
  • dropping columns
  • dropping indexes

To allow destructive operations:

php bin/schemage --force

Debugging

Enable stack traces during execution:

php bin/schemage --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/schemage:24 run()

Current Limitations

Schemage is intentionally conservative.

Current V1 limitations:

  • MySQL/MariaDB and SQLite 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 manual
  • No PostgreSQL grammar yet
  • No ORM/query builder layer yet

Philosophy

Schemage 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.


License

MIT

About

A schema-first migration engine for PHP with DSL, introspection, diffing and automatic SQL generation.. What if I use prisma ideia in fluent php?

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages