Skip to content

Upgrade Roadiz to Doctrine ORM 3.x #394

@ambroisemaupate

Description

@ambroisemaupate

🚀 Migration Plan: Upgrade Roadiz to Doctrine ORM 3.x

Context

Roadiz currently relies on Doctrine ORM 2.x, which has reached end-of-life. Doctrine ORM 3.x introduces significant internal refactoring, removal of deprecated APIs, and ecosystem alignment (DoctrineBundle 3, DBAL 3+, etc.).

This issue tracks the full migration of Roadiz to Doctrine ORM 3.6.x (stable minor target), ensuring:

  • Forward compatibility
  • Removal of deprecated APIs
  • Improved performance and maintainability
  • Alignment with the current Symfony/Doctrine ecosystem

🎯 Target Stack

Package Target Version
doctrine/orm ^3.6
doctrine/dbal ^3
doctrine/doctrine-bundle ^3
doctrine/persistence latest compatible
doctrine/migrations latest compatible

🧭 Migration Strategy (Phased Approach)

We will not attempt a big-bang upgrade. Migration will be done in controlled phases.


Phase 1 — Stabilize on Latest ORM 2.x (Pre-Upgrade Cleanup)

Goal

Eliminate all deprecations under ORM 2.21 before moving to 3.x.

Tasks

  • Upgrade to doctrine/orm:^2.21
  • Upgrade to doctrine/dbal:^3
  • Enable deprecation reporting in CI
  • Fix all deprecations
  • Ensure full test suite passes
  • Validate schema: bin/console doctrine:schema:validate

Why?

Doctrine officially recommends upgrading to the latest 2.x and clearing deprecations before jumping to 3.x.


Phase 2 — DoctrineBundle 3 Upgrade

DoctrineBundle 3 removed legacy support (annotations, YAML mapping, legacy config options).

Tasks

  • Upgrade to doctrine/doctrine-bundle:^3
  • Update Symfony config to remove deprecated options
  • Ensure cache pools are properly configured
  • Remove any legacy Doctrine\ORM\Tools\Setup usage
  • Verify event listeners and subscribers still register properly
  • Ensure compatibility with current Symfony version

Phase 3 — Mapping Migration (Critical)

DoctrineBundle 3 removes support for:

  • ❌ Annotations
  • ❌ YAML mapping

If Roadiz uses annotations:

@ORM\Entity

They must be converted to PHP Attributes:

#[ORM\Entity]

If YAML mappings exist:

They must be converted to:

  • PHP Attributes (preferred)
  • OR XML mapping

Tasks

  • Scan repository for @ORM\
  • Scan for .orm.yml files
  • Convert annotations to PHP attributes
  • Remove YAML mapping definitions
  • Validate metadata mapping with doctrine:schema:validate
  • Clear metadata and proxy cache

Phase 4 — Removed APIs & Breaking Changes (ORM 3)

This is the most critical phase.

1️⃣ EntityManager::merge() Removed

Find

->merge(

Required Refactor

Replace merge usage with explicit load + patch pattern:

$entity = $repository->find($id);
$entity->setFoo($dto->foo);
$em->flush();

No automatic graph reattachment exists in ORM 3.


2️⃣ Cascade "merge" Removed

Find

cascade={"merge"}

Action

Remove merge from cascade definitions.

Allowed cascade options:

  • persist
  • remove
  • refresh
  • detach

3️⃣ Flush Behavior Review

Audit usage of:

$em->flush($entity);

Ensure no reliance on legacy partial flush semantics.

Prefer full flush unless explicitly safe.


4️⃣ Proxy & Lazy Loading Changes

ORM 3 introduces internal improvements and optional Native Lazy Objects.

Tasks

  • Verify lazy loading works in Twig templates
  • Verify serialization (API responses)
  • Test backoffice entity forms
  • Benchmark homepage and node listing performance
  • Detect potential N+1 regressions

Phase 5 — Query & Hydration Review

Audit advanced ORM usage:

  • ResultSetMapping
  • Custom hydrators
  • iterate()
  • toIterable()
  • Scalar hydration
  • DQL PARTIAL objects
  • Custom query hints

Tasks

  • Identify custom hydrators
  • Run regression tests on heavy queries
  • Validate pagination logic
  • Validate DQL compatibility

Phase 6 — Migrations & Tooling

  • Verify doctrine/migrations compatibility
  • Test schema diff generation
  • Test database migrations execution
  • Validate fixtures loading
  • Validate custom Roadiz CLI commands
  • Ensure test database setup works correctly

🔎 Repository Audit Commands

Run these before starting Phase 4:

rg -n "->merge\s*\(" .
rg -n "cascade\s*=\s*\{[^}]*merge|cascade:\s*\[[^\]]*merge|cascade:\s*\{[^}]*merge" .
rg -n "@ORM\\\\" .
rg -n "type:\s*yml|\\.orm\\.yml" .
rg -n "Doctrine\\\\Common\\\\Persistence" .
rg -n "Doctrine\\\\ORM\\\\Tools\\\\Setup" .

Attach results to this issue for tracking.


🧪 Testing Requirements

Minimum validation checklist before merge:

  • All PHPUnit tests pass
  • No deprecations remain
  • Admin CRUD fully functional
  • Node tree manipulation works
  • Translations work
  • Media handling works
  • API responses unchanged
  • No N+1 regressions detected
  • Performance regression < +5% on critical pages

⚠️ Risk Areas

Area Risk Level
Entity graphs High
Backoffice forms High
Serialization Medium
Custom DQL Medium
Event subscribers Medium
Performance Medium

📦 Delivery Strategy

Recommended workflow:

  • Create branch feature/orm3-migration
  • Maintain ORM2-compatible branch for hotfixes
  • Enable strict CI rules
  • Merge only when fully green
  • Squash commits per migration phase

✅ Definition of Done

  • Roadiz runs fully on Doctrine ORM 3.6+
  • No deprecated APIs remain
  • No runtime regressions
  • Documentation updated
  • CI enforces Doctrine 3 compatibility
  • All mapping uses PHP Attributes or XML

This issue serves as the canonical migration blueprint.

All related PRs must reference it.

Metadata

Metadata

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions