Skip to content

IBX-11939: [Doctrine Migrations] Added Doctrine Migrations-based schema and data install path (inlined SQL) - #788

Open
Steveb-p wants to merge 43 commits into
base/ibx-11939-4.6-5.0-merged-6.0from
feature/fix-schema-rename-migration-6.0-postgres
Open

IBX-11939: [Doctrine Migrations] Added Doctrine Migrations-based schema and data install path (inlined SQL)#788
Steveb-p wants to merge 43 commits into
base/ibx-11939-4.6-5.0-merged-6.0from
feature/fix-schema-rename-migration-6.0-postgres

Conversation

@Steveb-p

@Steveb-p Steveb-p commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Warning

This PR's base branch (base/ibx-11939-4.6-5.0-merged-6.0) is not the real 6.0 branch — it's 6.0 with the 4.6 (#785) and 5.0 (#787) feature branches merged in. This PR intentionally shows only the changes introduced on top of "4.6 and 5.0, once merged forward" — review #785 and #787 first. Once those merge for real and are merged forward into 6.0, this PR's base will be updated to point at the real 6.0 branch.

Warning

This is the 6.0 follow-up to #787 (5.0). Review that PR first — this carries the same migration forward onto this branch.

🎫 Issue IBX-11939

Related PRs:

Package 4.6 5.0 6.0
ibexa/activity-log #166 #167 #168
ibexa/cart #172 #173 🔄 #174
ibexa/collaboration #115 #116
ibexa/connector-ai #198 #199
ibexa/connector-payum ⚠️ #39 #40 #41
ibexa/core #785 🩹 #787 🔄 🩹 #788this PR 🩹
ibexa/corporate-account #369 #370 🔄 #371
ibexa/discounts #341 🩹 #342 🩹
ibexa/discounts-codes #50 #51
ibexa/doctrine-migrations #1 #2 #3
ibexa/doctrine-schema #41 #42 #43
ibexa/fieldtype-page #208 🩹 #209 🔄 🩹 #210 🩹
ibexa/form-builder #240 🩹 #241 🔄 🩹 #242 🩹
ibexa/measurement #133 #134 🔄 #135
ibexa/messenger #22 #23
ibexa/migrations #438 #439 #440
ibexa/oauth2-server 🔄 #48 #49 #50
ibexa/order-management #188 #189 🔄 #190
ibexa/payment #204 #205 🔄 #206
ibexa/product-catalog #1543 #1544 🔄 #1545
ibexa/product-catalog-date-time-attribute #53 #54
ibexa/product-catalog-symbol-attribute #17 #18
ibexa/scheduler #168 #169 🔄 #170
ibexa/segmentation #186 #187 🔄 #188
ibexa/share #190 #191
ibexa/shipping #154 #155 🔄 #156
ibexa/shopping-list #66 #67
ibexa/site-context #121
ibexa/site-factory #172 #173 🔄 🩹 #174 🩹
ibexa/taxonomy ⚠️ #431 #432 #433
ibexa/translations-management #174 #175
ibexa/user #128 #129 🔄 #130
ibexa/workflow #191 🩹 #192 🔄 🩹 #193 🩹

🔄 = this branch adds an upgrade migration (renames/FK-retargets existing schema), not just a fresh baseline.
🩹 = this branch also received a backported delta migration, converted from a legacy ibexa/installer upgrade/db/*.sql script (4.6.0 or later) that the original baseline-only migration didn't cover. See IBX-11939 upgrade-scripts backport report or that package's own PR description for details.
⚠️ = fixes a different, related problem (missing SchemaBuilderEvent support for a plain Doctrine ORM entity/table) — not a Doctrine Migrations baseline/upgrade migration like the rest of this table. See that package's own PR description for details.

Description:

Fixes a gap in #785's Doctrine Migrations installer path. On the 5.0 and 6.0 branches, InstallSchemaMigration/ImportDataMigration had 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 legacy ez* table names) had no actual migration path to 5.0/6.0 via this mechanism, only a fresh-install script.

This PR:

  • Is a follow-up PR to IBX-11939: [Doctrine Migrations] Added Doctrine Migrations-based schema and data install path (inlined SQL) #785, applying the same fix pattern on this branch. All three platform branches — MySQL, PostgreSQL, and SQLite — are implemented in RenameSchemaTo5_0Migration (and carried through in the restored InstallSchemaMigration/ImportDataMigration baseline); verified against all three, not just one engine.
  • Restores InstallSchemaMigration/ImportDataMigration to the 4.6-shaped baseline (still tagged 4.6.0, legacy ez* table names — unchanged from the 4.6 branch).
  • Adds RenameSchemaTo5_0Migration (tagged 5.0.0) with the real rename diff, sourced from ibexa/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 no RENAME INDEX, so those become DROP INDEX+CREATE INDEX; FK constraint-name-only renames are skipped as cosmetic, since SQLite auto-updates FK/column references across the schema on RENAME TABLE/RENAME COLUMN).
  • Includes 3 genuine data-value renames found alongside the schema rename in the installer's script (ez_lockibexa_lock object-state identifier; ezstringibexa_string field-type identifier, in two tables).

IbexaMigrationComparator orders migrations by getTargetVersion() then getCreationDate(), so TaggedMigrationsRunner replays these correctly both for a fresh 6.0 install (both migrations run) and for an existing 4.6→6.0 upgrade (only the 5.0.0 rename 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:

  • A stray FK-backing index (ezcontentclass_attribute_ml_lang_fk) needed renaming, not just the FK constraint itself.
  • An index-name mismatch: the installer script renames to ibexa_content_type_field_definition_ct_id, but the live schema.yaml actually 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, run ibexa:install, and confirm the resulting schema matches an install with the flag left at its default true.

Documentation:

N/A — internal installer implementation detail.

@Steveb-p Steveb-p changed the title IBX-11939: [Doctrine Migrations] Fixed schema rename migration to replay 4.6→5.0 history IBX-11939: [Doctrine Migrations] Added Doctrine Migrations-based schema and data install path (inlined SQL) Jul 21, 2026
Steveb-p and others added 11 commits July 24, 2026 13:12
… install path

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 new 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 the existing schema.yaml; ImportDataMigration's
from the existing per-DBMS cleandata.sql files (with the mysql statements
reused verbatim for sqlite, which previously had no cleandata support at
all).
Symfony\Component\DependencyInjection\ServiceLocator is not a generic
class, so PHPStan rejects the ServiceLocator<Installer> PHPDoc type used
for InstallPlatformCommand::$installers. Removed the invalid generic
parameter; the type-hint itself (plain ServiceLocator) is unaffected.
Converts InstallSchemaMigration and ImportDataMigration from
Ibexa\Contracts\DoctrineMigrations\Migrations\AbstractVersion (which loads
its SQL from a YAML manifest + one-statement-per-file pairs at runtime) to
Doctrine\Migrations\AbstractMigration directly, with every statement
inlined as an addSql() call in up(), branching on $this->platform
(MySqlPlatform/PostgreSqlPlatform/SqlitePlatform).

Both still implement IbexaMigrationInterface and are tagged with
IbexaMigrationTag::TAG in services.yml, so they remain discoverable by
TaggedMigrationsRunner/ServiceMigrationsRepository/IbexaOnlyDependencyFactory
exactly as before - no other part of the installer changes.

Removes the entire Resources/migrations/ YAML+SQL-file tree (509 files),
generated from the same schema.yaml/cleandata.sql sources as before.
CREATE TABLE statements are now pretty-printed one column per line, and
other long or naturally multi-line statements (multi-row INSERT, long
ALTER TABLE ... ADD CONSTRAINT) use NOWDOC instead of a single escaped
string literal, for readability. SQL content is unchanged.
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").
…name

Live end-to-end testing (fresh legacy install -> doctrine:migrations:migrate
on 4.6) surfaced that these baseline guards checked the FINAL, post-5.0-
rename table name (e.g. "ibexa_content") -- correct on 5.0/6.0, but wrong
on 4.6, which has no rename at all: there, the table this migration
creates already IS the branch's permanent, current name (e.g.
"ezcontentobject"), so the guard never fired and the baseline collided
with an already-installed legacy 4.6 schema. Checks the branch's own
table name instead, exactly like every other baseline guard that has no
later rename to worry about.

For core's ImportDataMigration specifically, this required a proper data
check rather than a schema-shape check: "ezcontentobject" is 4.6's real,
permanent name, so it always exists once the baseline has run there,
regardless of whether this migration's own bootstrap INSERTs ran yet.
Checks for the actual seed row (root content, id = 1) instead, combined
with the table's outright absence (the 5.0/6.0 legacy signal) via OR.
…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
Steveb-p force-pushed the feature/fix-schema-rename-migration-6.0-postgres branch from ae983a1 to 25b186d Compare July 24, 2026 11:13
Steveb-p and others added 4 commits July 24, 2026 13:26
…for PR #787)

# Conflicts:
#	composer.json
#	phpstan-baseline-7.4.neon
#	phpstan-baseline-lte-8.1.neon
#	phpstan-baseline.neon
#	src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php
#	src/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPass.php
#	src/bundle/RepositoryInstaller/Installer/CoreInstaller.php
#	tests/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPassTest.php
#	tests/bundle/RepositoryInstaller/DependencyInjection/IbexaInstallerExtensionTest.php
#	tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php
#	tests/lib/Repository/Service/Mock/ContentTest.php
… 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).
Steveb-p and others added 8 commits July 24, 2026 13:36
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.
… install path

Same as the 4.6/5.0 branches: 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).
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.
…onflict resolution

Restores the ez_lock->ibexa_lock and ezstring->ibexa_string (x2) UPDATE
statements that were present in the original commit but got lost while
resolving rebase conflicts against 6.0.
@Steveb-p
Steveb-p force-pushed the feature/fix-schema-rename-migration-6.0-postgres branch from 25b186d to a7b7542 Compare July 24, 2026 11:45
@Steveb-p
Steveb-p changed the base branch from 6.0 to base/ibx-11939-4.6-5.0-merged-6.0 July 24, 2026 11:45
@Steveb-p
Steveb-p marked this pull request as ready for review July 24, 2026 12:14
Steveb-p and others added 9 commits July 27, 2026 12:40
…nt importData() call)

Propagated from #785 (4.6):
- CoreInstaller::getQueriesFromSchemaBuilderEvent() now builds its Query list via an
  arrow function instead of a static closure, per review suggestion.
- CoreInstaller::importData() no longer re-invokes TaggedMigrationsRunner::run() in the
  migrations-runner branch; importSchema() already runs every tagged migration
  (including ImportDataMigration) to the latest version, so the second call was a
  pure no-op with no real caller relying on it standalone.
- getDropSqlStatementsForExistingSchema()'s docblock tightened to list<string>, which
  it always genuinely returns.
- TaggedMigrationsRunner's constructor documents why $dependencyFactory is nullable
  (optional service, wired via "@?..." in services.yml; run() throws a clear exception
  if called while null).
ibexa/doctrine-migrations is now published on Packagist (which mirrors all
of its branches, not just tags), so the explicit VCS repository pointing
composer directly at GitHub is no longer needed to resolve the dev-branch
require constraint.
… mandatory

Instead of TaggedMigrationsRunner tolerating a null DependencyFactory at
runtime, RemoveTaggedMigrationsRunnerPass now removes its service
definition entirely when "ibexa/doctrine-migrations" isn't
installed/enabled, and CoreInstaller depends on it as an optional
(nullable) service instead, throwing a clear configuration exception if
"ibexa.installer.schema_builder_event.enabled" is disabled without it.
…erEvent()

Passing an empty array as array_map()'s third argument re-indexes its
result, so PHPStan can type it as list<Query> instead of Query[].
… branch for PR #788)

# Conflicts:
#	composer.json
#	src/bundle/RepositoryInstaller/Installer/CoreInstaller.php
Converts installer's upgrade/db/ibexa-4.5.1-to-4.5.2.sql and
upgrade/db/ibexa-4.6.20-to-4.6.21.sql into guarded Doctrine migrations
targeting the pre-rename (4.6.0) table names. Same content as the 5.0
branch.
$schema->getTable() throws TableDoesNotExist rather than returning null,
so a guard that calls it directly (without checking hasTable() first)
crashes -- rather than skipping -- on any install that never had the
table under this name at all. Caught via live testing against a real
database. Guard now checks hasTable() first. Same content as the 4.6
branch.
…into baseline

Same rationale as ibexa/product-catalog: doctrine migrations only exist
starting at 4.6, so InstallSchemaMigration's baseline already includes
these indexes (originally shipped via installer's
upgrade/db/ibexa-4.5.0-to-4.5.1.sql). A standalone migration for pre-4.6
content can never have real work to do on a genuine 4.6+ install.
…ion-6.0-postgres' into feature/fix-schema-rename-migration-6.0-postgres

# Conflicts:
#	src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php
#	src/bundle/RepositoryInstaller/Installer/CoreInstaller.php
#	src/bundle/RepositoryInstaller/Resources/config/services.yml
Steveb-p added a commit that referenced this pull request Jul 29, 2026
… branch for PR #788)

Refreshes the base branch to current origin/6.0 (6 new upstream commits,
including the DBAL 3.10-deprecated-API cleanup) plus feature-5.0's current
content (the product-catalog pre-4.6-origin-migration removal).

Restored src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php's
InstallerTagPass registration and its test file: both exist unchanged on
current 6.0 (added before feature-5.0 branched) but feature-5.0's own tree
dropped them at some point, so the auto-merge silently took feature-5.0's
version wholesale since git saw no conflicting HEAD-side change. Combined
both instead of losing InstallerTagPass entirely.
@Steveb-p
Steveb-p force-pushed the base/ibx-11939-4.6-5.0-merged-6.0 branch from 9e7df09 to 1b517a5 Compare July 29, 2026 10:32
Steveb-p added 5 commits July 29, 2026 12:33
…0' into feature/fix-schema-rename-migration-6.0-postgres
The feat/doctrine-migrations-6.0 branch it previously required has
been merged into and deleted from the primary 6.0 branch upstream,
so the old constraint no longer resolves.
$migration->up(new Schema()) gave every migration's hasTable()/
getTable() guard checks an always-empty schema to inspect, regardless
of what earlier migrations in the same run (or a prior run) actually
created against the real database -- guards written the correct way
(checking hasTable() before trusting a table exists) would incorrectly
conclude the table is missing and either skip work that still needs
doing, or crash outright on getTable() for a table that does exist.
Introspect the live database instead, the same way the standalone
'doctrine:migrations:migrate' command already does via
DBALSchemaDiffProvider::createFromSchema().
InstallSchemaMigration, AddUrlAliasMlLinkIndexMigration, RenameSchemaTo5_0Migration,
ImportDataMigration, and FixLegacyIdentifiersMigration only depend on the persistence
connection -- they don't need anything from IbexaRepositoryInstallerBundle
(CoreInstaller, BuildSchemaSubscriber), which is scoped to installer-time concerns
and additionally requires DoctrineSchemaBundle to boot at all. Declaring them in
IbexaCoreBundle instead matches how every other package in this effort declares
its own InstallSchemaMigration in its own bundle, and lets any consumer discover
core's schema migrations via ibexa:doctrine:migrations:migrate without needing the
installer bundle registered -- useful for integration test kernels that don't
otherwise need CoreInstaller.
… SQL

install-schema-sqlite.sql and import-data-sqlite.sql were generated from
the MySQL source without adapting to SQLite's stricter SQL semantics:

- import-data-sqlite.sql used MySQL-style backslash escapes (\" and \n)
  inside string literals. SQLite (unlike MySQL) does not interpret
  backslash as an escape character at all, so these were stored as
  literal two-byte sequences, corrupting PHP-serialized content-type
  names/descriptions (unserialize() failures) and embedded XML field
  data (DOMDocument parse failures, e.g. the admin user's own ezimage
  field).

- install-schema-sqlite.sql collapsed the composite primary keys on
  ezcontentclass, ezcontentclass_attribute and ezcontentobject_attribute
  (id, version) down to a single-column `id INTEGER PRIMARY KEY
  AUTOINCREMENT`, dropping the `version` component entirely -- unlike
  the mysql/postgresql variants, which both correctly declare
  `PRIMARY KEY(id, version)`. This made SQLite reject the second row
  (e.g. publishing a content-type draft, which legitimately reuses the
  same id with a different version/status) as a duplicate primary key.

Both only affect the SQLite platform; MySQL and PostgreSQL were
already correct.
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
29.7% Duplication on New Code (required ≤ 3%)
B Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant