ibexa/doctrine-schema
Symfony bundle that abstracts cross-DBMS schema import/export. Defines a custom YAML schema format, imports YAML into Doctrine DBAL Schema, exports Schema back to YAML, and provides an event-driven SchemaBuilder extension point via subscribers.
Install the Package
composer require ibexa/doctrine-schema
Ensure your project uses Symfony 7.x (or 6.x for v4.x) and PHP 8.3+ (v5.x).
Enable the Bundle
Add to config/bundles.php:
Ibexa\DoctrineSchema\DoctrineSchemaBundle::class => ['all' => true],
Define a Schema File
Create a YAML file (e.g., config/schema.yml) with a custom schema format:
tables:
users:
columns:
id: { type: integer, autoincrement: true, primary: true }
name: { type: string, notnull: true }
First Use Case: Import Schema
Inject SchemaBuilder and build the schema:
use Ibexa\DoctrineSchema\Builder\SchemaBuilder;
public function __construct(private SchemaBuilder $schemaBuilder) {}
public function importSchema(): void
{
$schema = $this->schemaBuilder->buildSchema();
// Use with Doctrine DBAL (e.g., $connection->getSchemaManager()->createSchema($schema))
}
config/schema/*.yml) for modularity.SchemaBuilderEvent to merge multiple schema files:
// src/EventSubscriber/LoadSchemasSubscriber.php
use Ibexa\Contracts\DoctrineSchema\Event\SchemaBuilderEvent;
use Ibexa\Contracts\DoctrineSchema\SchemaBuilderEvents;
class LoadSchemasSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [SchemaBuilderEvents::BUILD_SCHEMA => 'onBuildSchema'];
}
public function onBuildSchema(SchemaBuilderEvent $event): void
{
$files = glob(__DIR__.'/../../config/schema/*.yml');
foreach ($files as $file) {
$event->getSchemaBuilder()->importSchemaFromFile($file);
}
}
}
Doctrine\DBAL\Schema\Schema to YAML for version control or debugging:
use Ibexa\DoctrineSchema\Exporter\SchemaExporter;
public function __construct(private SchemaExporter $exporter) {}
public function exportSchema(\Doctrine\DBAL\Schema\Schema $schema): string
{
return $this->exporter->export($schema);
}
use Doctrine\DBAL\Schema\Schema;
use Ibexa\DoctrineSchema\Builder\SchemaBuilder;
public function up(Schema $schema): void
{
$customSchema = $this->schemaBuilder->buildSchema();
$schema->mergeFrom($customSchema);
}
config/schema/dev.yml, config/schema/prod.yml) and load them conditionally:
$event->getSchemaBuilder()->importSchemaFromFile(
__DIR__.'/../../config/schema/'.env('APP_ENV').'.yml'
);
SchemaBuilderEvents::BUILD_SCHEMA to add/alter tables/columns dynamically:
public function onBuildSchema(SchemaBuilderEvent $event): void
{
$schema = $event->getSchemaBuilder()->getSchema();
$schema->createTable('audit_log')->addColumn('action', 'string');
}
Yaml component) before importing:
use Symfony\Component\Yaml\Yaml;
$yaml = Yaml::parseFile('config/schema.yml');
if (!isset($yaml['tables'])) {
throw new \RuntimeException('Invalid schema: missing "tables" key');
}
$this->assertEquals(
file_get_contents('tests/_data/schema.yml'),
$this->exporter->export($schema)
);
PHP 8.3+ Requirement (v5.x)
RuntimeException.ibexa/doctrine-schema:^4.6 if needed.Symfony Version Mismatch
composer.json or use the correct branch.YAML Parsing Quirks
# Valid
columns:
id: { type: integer, primary: true }
# Invalid (will fail silently or cause errors)
columns:
id: integer # Missing key structure
symfony/yaml before importing.Event Priority Collisions
100, 200):
SchemaBuilderEvents::BUILD_SCHEMA => ['onBuildSchema', 200]
Doctrine DBAL Schema Merging
SchemaBuilderEvent to ensure dependencies are loaded first.Inspect the Schema Object
Doctrine\DBAL\Schema\Schema object to debug:
$schema = $this->schemaBuilder->buildSchema();
dump($schema->toSql($connection->getDatabasePlatform()));
Enable Debugging for Events
$dispatcher = $container->get('event_dispatcher');
dump($dispatcher->getListeners(SchemaBuilderEvents::BUILD_SCHEMA));
Validate YAML Syntax
symfony/yaml to validate files before importing:
try {
Yaml::parseFile('schema.yml');
} catch (\Exception $e) {
throw new \RuntimeException('Invalid YAML: '.$e->getMessage());
}
Custom Schema Types
SchemaImporter:
use Ibexa\Contracts\DoctrineSchema\SchemaImporterInterface;
class CustomSchemaImporter implements SchemaImporterInterface
{
public function import(string $yaml): \Doctrine\DBAL\Schema\Schema
{
// Parse custom YAML and build schema
}
}
ibexa.doctrine_schema.schema_importer tag.Post-Import Hooks
SchemaBuilderEvents::POST_BUILD to run logic after schema construction:
SchemaBuilderEvents::POST_BUILD => 'onPostBuild'
Schema Diffing
$prodSchema = $this->exporter->export($prodSchemaObj);
$devSchema = $this->exporter->export($devSchemaObj);
$this->assertEquals($prodSchema, $devSchema);
Bundle Prefix
ibexa.doctrine_schema as the default configuration key. Override in config/packages/ibexa_doctrine_schema.yaml if needed.Autowiring
SchemaBuilder, SchemaImporter, and SchemaExporter are autowired. If not, add to services.yaml:
services:
Ibexa\DoctrineSchema\Builder\SchemaBuilder: ~
Doctrine DBAL Dependency
doctrine/dbal. Install it if missing:
composer require doctrine/dbal
How can I help you explore Laravel packages today?