IBX-11939: [Doctrine Migrations] Added Doctrine Migrations-based schema and data install path (inlined SQL)#787
Open
Steveb-p wants to merge 12 commits into
Conversation
This was referenced Jul 20, 2026
Open
Open
Open
This was referenced Jul 21, 2026
Steveb-p
force-pushed
the
feature/fix-schema-rename-migration-5.0-postgres
branch
from
July 24, 2026 11:13
ad77c98 to
2e5073b
Compare
… install path Same as the 4.6 branch: adds an ibexa/doctrine-migrations-based alternative to the event-driven SchemaBuilderEvent mechanism CoreInstaller has used to install the core schema and bootstrap data. Controlled by the new "ibexa.installer.schema_builder_event.enabled" setting (defaults to true, preserving current behavior): - When enabled (default), CoreInstaller keeps dispatching SchemaBuilderEvent and importing cleandata.sql directly, unchanged. - When disabled, schema creation and data import are each modeled as an AbstractVersion migration (InstallSchemaMigration, ImportDataMigration), tagged for discovery, and executed via TaggedMigrationsRunner through the IbexaOnlyDependencyFactory service - an independent Doctrine Migrations DependencyFactory scoped to only Ibexa-tagged migrations, so a project's own migrations are never accidentally executed. Each migration's execution is recorded in the standard Doctrine Migrations versioning table, making repeated installs idempotent. InstallSchemaMigration's SQL (mysql/postgresql/sqlite, one statement per file) is generated from this branch's own schema.yaml (ibexa_* table naming); ImportDataMigration's from this branch's own per-DBMS cleandata.sql files (with the mysql statements reused verbatim for sqlite, which previously had no cleandata support at all).
Replaces the AbstractVersion + YAML manifest + one-SQL-statement-per-file mechanism with InstallSchemaMigration/ImportDataMigration implemented as plain Doctrine\Migrations\AbstractMigration subclasses with all SQL inlined via addSql(), still implementing IbexaMigrationInterface and tagged with IbexaMigrationTag so TaggedMigrationsRunner/IbexaOnlyDependencyFactory work unchanged. Long/multiline statements use PHP NOWDOC for readability, with CREATE TABLE bodies pretty-printed one column per line. Same conversion as #785 for the 4.6 line, adapted for this branch's ibexa_*-renamed schema and dbal 3.x platform class names (AbstractMySQLPlatform/PostgreSQLPlatform instead of MySqlPlatform/ PostgreSqlPlatform).
…diff
InstallSchemaMigration/ImportDataMigration previously recreated the
already-renamed (ibexa_*) schema from scratch on every branch, tagged
4.6.0. This meant an existing 4.6 install (still using legacy ez*
table names) had no real migration path to 5.0/6.0.
Reuse the 4.6-shaped baseline (legacy ez* names, correct on the 4.6
branch) and add RenameSchemaTo5_0Migration (tagged 5.0.0) containing
the actual rename statements, sourced from ibexa/installer's own
shipped upgrade SQL (upgrade/db/{mysql,postgresql}/ibexa-4.6.latest-to-5.0.0.sql).
SQLite equivalents were derived and verified end-to-end against a
real SQLite connection (table/index names now match a fresh dump-sql
of the current schema.yaml exactly).
… calls Matches InstallSchemaMigration's own style: heredoc/NOWDOC only for genuinely multi-line statements, plain quoted strings otherwise.
…lines InstallSchemaMigration/ImportDataMigration were copied verbatim from the 4.6 branch, which pins doctrine/dbal 2.x (MySqlPlatform/PostgreSqlPlatform). This branch pins doctrine/dbal 3.x, where those classes were renamed/removed with no compat alias - replaced with AbstractMySQLPlatform/PostgreSQLPlatform, matching every other migration file already on this branch. Also removes extra blank lines in RenameSchemaTo5_0Migration.php that php-cs-fixer's no_extra_blank_lines rule flagged (before each } elseif and before the closing brace, plus around the appended data-value UPDATE statements).
Extends AbstractSqlMigration (added in ibexa/doctrine-migrations) instead of the plain Doctrine AbstractMigration, replacing `$this->platform instanceof ...` checks with isMySQL()/isPostgreSQL()/isSqlite(), and moving each platform Statement block out of the PHP file into its own sql/*.sql file loaded via addSqlFile(). Mechanical, content-preserving change: every migration was run before and after against all three platforms and the resulting SQL statement lists are byte-for-byte identical.
Call abortIfUnsupportedPlatform() as the first statement of up(), so installs on a database this migration doesn't build SQL for fail loudly instead of silently queuing zero statements.
Each statement now ends with `;`, matching ibexa:doctrine:schema:dump-sql's own convention, so the files are directly executable via mysql/psql/sqlite3 CLI clients. addSqlFile() still splits on the delimiter and passes one statement per addSql() call, unaffected by the trailing terminator.
…tions Guards ibexa/core's InstallSchemaMigration/ImportDataMigration/ RenameSchemaTo5_0Migration and ibexa/fieldtype-page's RenameSchemaTo5_0Migration so they skip when an existing install already built its schema the old way. ImportDataMigration checks for the absence of its pre-rename input table, since migrations run in a strict, deterministic order -- if it's missing, this system never went through this migration, meaning it built its schema (and bootstrap data) via schema.yaml directly. The two data-value fixes hidden inside the rename migrations (core's ez_lock/ezstring identifier fixes, fieldtype-page's block-ID HTML reference fix) are split into their own always-run migrations (FixLegacyIdentifiersMigration, FixBlockIdReferencesMigration), since a schema-shape guard on the rename would otherwise also skip these on a system whose schema is already renamed but whose row data still holds the old values -- schema.yaml only defines structure, not content. Both run unconditionally right after their corresponding rename (same target version, later creation date), so the tables they touch are always at their final name by then, and each statement is a no-op via its own WHERE clause once already applied.
Doctrine Migrations only calls MetadataStorage::complete() (the write to
doctrine_migration_versions) when a migration's up() returns normally --
never when it throws SkipMigration. So every migration using skipIf() was
being silently re-evaluated on every future doctrine:migrations:migrate
run instead of being permanently recorded as applied, even though its
guard condition (the schema already being in place) never changes back.
Replaces every `$this->skipIf($condition, $message);` with
`if ($condition) { return; }`: up() now returns normally with zero queued
SQL when the guard fires, so the migration is correctly recorded as
executed (with a "did not result in any SQL statements" warning logged,
which is expected and harmless) and never re-evaluated again.
Verified end-to-end: fresh ibexa:install (schema_builder_event enabled)
followed by doctrine:migrations:migrate now records all 44 tagged
migrations in doctrine_migration_versions in one pass, and a second
migrate run does zero work at all ("Already at the latest version").
…emaBuilderEvent ibexa:doctrine:migrations:diff (and any other Doctrine Migrations command that needs a target schema) previously failed with 'The schema provider is not available.', since IbexaOnlyDependencyFactory never had an entity manager or SchemaProvider configured. SchemaBuilderEventSchemaProvider bridges Doctrine Migrations' SchemaProvider to Ibexa's own legacy, event-driven schema builder (SchemaBuilderInterface, backed by SchemaBuilderEvent and every installed package's BuildSchemaSubscriber) -- the same mechanism CoreInstaller::importSchema() already uses for the legacy install path. A new compiler pass wires it onto IbexaOnlyDependencyFactory via setService(), a no-op if ibexa/doctrine-schema or ibexa/doctrine-migrations isn't installed.
Steveb-p
force-pushed
the
feature/fix-schema-rename-migration-5.0-postgres
branch
from
July 24, 2026 11:37
2e5073b to
be5d0a0
Compare
…he schema-rename migration These UPDATE statements (identifier/data_type_string values referencing the old ez_lock/ezstring names) were missing from the 5.0 rename migration's SQL files, even though the equivalent renames were already applied on 6.0.
|
Steveb-p
marked this pull request as ready for review
July 24, 2026 12:14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Warning
This PR's base branch (
base/ibx-11939-4.6-merged-5.0) is not the real5.0branch — it's5.0with the4.6feature branch (#785) merged in. This PR intentionally shows only the changes introduced on top of "4.6, once merged forward" — review #785 first. Once #785 merges for real and is merged forward into5.0, this PR's base will be updated to point at the real5.0branch.Warning
This is the 5.0 follow-up to #785 (4.6). Review that PR first — this carries the same migration forward onto this branch.
Related PRs:
🔄 = this branch adds an upgrade migration (renames/FK-retargets existing schema), not just a fresh baseline.
Description:
Fixes a gap in #785's Doctrine Migrations installer path. On the
5.0and6.0branches,InstallSchemaMigration/ImportDataMigrationhad been independently rewritten to create the already-renamed (ibexa_*) schema from scratch — instead of replaying the real 4.6→5.0 rename. This meant a project already running 4.6 (with legacyez*table names) had no actual migration path to 5.0/6.0 via this mechanism, only a fresh-install script.This PR:
RenameSchemaTo5_0Migration(and carried through in the restoredInstallSchemaMigration/ImportDataMigrationbaseline); verified against all three, not just one engine.InstallSchemaMigration/ImportDataMigrationto the 4.6-shaped baseline (still tagged4.6.0, legacyez*table names — unchanged from the4.6branch).RenameSchemaTo5_0Migration(tagged5.0.0) with the real rename diff, sourced fromibexa/installer's own shipped upgrade SQL (upgrade/db/{mysql,postgresql}/ibexa-4.6.latest-to-5.0.0.sql), filtered to core-owned tables, plus a hand-derived SQLite equivalent (SQLite has noRENAME INDEX, so those becomeDROP INDEX+CREATE INDEX; FK constraint-name-only renames are skipped as cosmetic, since SQLite auto-updates FK/column references across the schema onRENAME TABLE/RENAME COLUMN).ez_lock→ibexa_lockobject-state identifier;ezstring→ibexa_stringfield-type identifier, in two tables).IbexaMigrationComparatororders migrations bygetTargetVersion()thengetCreationDate(), soTaggedMigrationsRunnerreplays these correctly both for a fresh 6.0 install (both migrations run) and for an existing 4.6→6.0 upgrade (only the5.0.0rename runs; already-applied migrations are skipped via the standard Doctrine Migrations metadata table).Verification caught two real gaps in the installer's own shipped SQL, both fixed here:
ezcontentclass_attribute_ml_lang_fk) needed renaming, not just the FK constraint itself.ibexa_content_type_field_definition_ct_id, but the liveschema.yamlactually expects..._ctid(no underscore) — used the live schema as ground truth.For QA:
Ran both migrations end-to-end against a real SQLite connection and diffed the resulting table/index names against a fresh
bin/console ibexa:doctrine:schema:dump-sql src/bundle/Core/Resources/config/storage/legacy/schema.yaml --force-platform=sqlite— they match exactly.To verify manually: set
ibexa.installer.schema_builder_event.enabled: false, runibexa:install, and confirm the resulting schema matches an install with the flag left at its defaulttrue.Documentation:
N/A — internal installer implementation detail.