Module Schema Providers
Define framework module migrations as Doctrine DBAL Schema objects instead of raw SQL.
Module Schema Providers
Framework modules should define migrations with DBAL schema providers.
Provider files live at:
packages/Vortos/src/{Module}/Resources/migrations/*.phpEach file returns an object implementing ModuleSchemaProviderInterface.
use Doctrine\DBAL\Schema\Schema;
use Vortos\Migration\Schema\AbstractModuleSchemaProvider;
return new class extends AbstractModuleSchemaProvider {
public function module(): string
{
return 'Authorization';
}
public function id(): string
{
return 'authorization.rbac';
}
public function description(): string
{
return 'Authorization rbac';
}
public function define(Schema $schema): void
{
$roles = $schema->createTable($this->t('role_permissions'));
$roles->addColumn('role', 'string', ['length' => 150]);
$roles->addColumn('permission', 'string', ['length' => 190]);
$roles->addColumn('created_at', 'datetime_immutable');
$roles->setPrimaryKey(['role', 'permission']);
$roles->addIndex(['role'], 'idx_role_permissions_role');
}
};$this->t() — Framework Table Naming
All framework module migrations must use $this->t('table_name') instead of a plain string in createTable(). t() applies the platform-specific framework namespace automatically:
| Platform | $this->t('role_permissions') |
|---|---|
| PostgreSQL | vortos.role_permissions |
| All others | vortos_role_permissions |
The prefix is set once before providers are loaded — no runtime overhead.
Required for framework tables
Plain $schema->createTable('role_permissions') creates the table in the default schema with no prefix — it will not be recognised as a framework-owned table, drift detection will not track it, and it will conflict with your application tables. Always use $this->t().
The provider gives Vortos structured ownership metadata:
{
"tables": ["role_permissions"],
"indexes": ["idx_role_permissions_role"]
}That metadata powers drift detection and adoption.
Publishing
php vortos migrate:publishPublishing generates Doctrine migration classes under:
migrations/For DBAL providers, generated CREATE TABLE, CREATE INDEX and ADD COLUMN statements are made idempotent with IF NOT EXISTS. Preflight still checks for schema drift before execution.
Alter-style providers
A provider may add columns or indexes to a table created by an earlier provider — including one
owned by another module — by guarding the change with hasTable():
public function define(Schema $schema): void
{
if ($schema->hasTable($this->t('backup_catalog'))) {
$catalog = $schema->getTable($this->t('backup_catalog'));
$catalog->addColumn('encryption_provider', 'string', ['length' => 32, 'notnull' => false]);
}
}Publishing evaluates every provider against the cumulative schema of all providers that ran before
it (in migration-filename order), then diffs before → after — so an alter-style provider sees the base
table and emits a correct ALTER TABLE … ADD COLUMN. A provider's base table must itself be defined by
another schema provider (not a raw .sql stub), since raw stubs carry no schema model to diff against.
Legacy SQL Stubs
Legacy .sql files are still supported. If a .php schema provider and .sql file share the same base name, the provider wins.
This keeps old projects compatible while moving framework modules to structured definitions.