sulu / phpcr-migration-bundle
The bundle provides command to migrate the Sulu database from phpCr to the SuluContentBundle.
Package info
github.com/sulu/SuluPHPCRMigrationBundle
Type:sulu-bundle
pkg:composer/sulu/phpcr-migration-bundle
Requires
- php: ^8.2
- doctrine/orm: ^2.17.3 || ^3.3
- jackalope/jackalope-doctrine-dbal: ^2.0
- jackalope/jackalope-jackrabbit: ^2.0
- sulu/sulu: ^3.0
- symfony/config: ^6.1 || ^7.0 || ^8.0
- symfony/console: ^6.1 || ^7.0 || ^8.0
- symfony/dependency-injection: ^6.1 || ^7.0 || ^8.0
- symfony/framework-bundle: ^6.1 || ^7.0 || ^8.0
- symfony/property-access: ^6.1 || ^7.0 || ^8.0
Requires (Dev)
- deptrac/deptrac: ^3.0
- jangregor/phpstan-prophecy: ^1.0
- php-cs-fixer/shim: ^3.62
- phpspec/prophecy-phpunit: ^2.1
- phpstan/extension-installer: ^1.3
- phpstan/phpstan: ^1.10
- phpstan/phpstan-doctrine: ^1.3
- phpstan/phpstan-phpunit: ^1.3
- phpstan/phpstan-symfony: ^1.3
- phpstan/phpstan-webmozart-assert: ^1.2
- phpunit/phpunit: ^10.0
- rector/rector: ^1.0
- symfony/dotenv: ^6.1 || ^7.0 || ^8.0
- thecodingmachine/phpstan-strict-rules: ^1.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-20 10:13:22 UTC
README
Upgrading Data from Sulu 2.6 to Sulu 3.0
![Note] The upgrade from Sulu 2.6 to Sulu 3.0 is a major upgrade and will require some migration steps.
You can find the full upgrade guide here: sulu/sulu UPGRADE.md
How to install
Install the package:
composer require sulu/phpcr-migration-bundle
Add the new migration bundle to your config/bundles.php:
// config/bundles.php
return [
// ...
+ Sulu\Bundle\PhpcrMigrationBundle\SuluPhpcrMigrationBundle::class => ['all' => true],
Configure the SuluPhpcrMigrationBundle in config/packages/sulu_phpcr_migration.yaml:
If you are currently using Jackrabbit, use the "jackrabbit://" based DSN string. After the upgrade, Apache Jackrabbit is no longer used by Sulu’s new content storage and can be removed from your projects in most situations.
# config/packages/sulu_phpcr_migration.yaml sulu_phpcr_migration: # dbal://<dbalConnection>?workspace=<workspaceName> # jackrabbit://<user>:<password>@<host>:<port>/server?workspace=<workspaceName> # DSN: "dbal://default?workspace=%env(PHPCR_WORKSPACE)%" # DSN: "jackrabbit://admin:admin@127.0.0.1:8080/server?workspace=%env(PHPCR_WORKSPACE)%" DSN: "dbal://default?workspace=%env(PHPCR_WORKSPACE)%" target: dbal: connection: default
How to use
# Update the database to the latest phpcr migration to prepare the structure php bin/adminconsole phpcr:migrations:migrate # Run the command to migrate for the data php bin/adminconsole sulu:phpcr-migration:migrate
In case of some errors on customized code, you can try to fix it and rerun the command. The migration command can be rerun, the existing already migrated content will be overwritten and not duplicated.
If you only want to migrate certain document types pass them as an argument (or comma separated if it's a list)
php bin/adminconsole sulu:phpcr-migration:migrate snippet
Allowed types are: snippet, page, article
Dry-run mode
Preview a migration without writing anything to the target database:
php bin/adminconsole sulu:phpcr-migration:migrate --dry-run
This executes the full parse and persist pipeline against your real PHPCR content and the rows already in the target database. Every database write is kept in memory instead, and later lookups see it, so the dry run reports what the real run would do: skipped routes with the page that owns the slug, and unique-key violations. The command collects every exception (one per document or document/locale pair), continues past failures, and at the end prints a grouped summary plus a JSON report you can hand off for data cleanup.
Default report path: var/phpcr-migration/dry-run-YYYYMMDD-HHMMSS.json.
Override it with --report=/path/to/report.json.
Limitations. Dry-run catches PHP-level validation errors (title/slug length, missing webspace or parent, missing shadow template, etc.) and unique-key collisions, but does not catch other database-level issues such as foreign-key violations, column truncation, or NOT NULL violations on columns populated only by database defaults. A failed document keeps the writes it made before the failure. Post-migration queries (permission contexts, access controls, automation tasks) are skipped in dry-run mode.
A page whose routes fail never gets its row, so every child of it reports
Parent node … not found as well. Fix the first error of such a branch and
rerun before reading the errors below it.
The dry run keeps every written row in memory until the command ends, roughly
1.6 KB per row. A site with many documents may need a higher memory_limit
(php -d memory_limit=2G bin/adminconsole …).
On MySQL the dry run compares unique keys case-insensitively only, while
utf8mb4_unicode_ci also ignores accents and trailing spaces. Values like
Café and Cafe in one run pass the dry run and fail in the real run.
🛠️ Development
Quick Start
# Install dependencies composer install # Run tests composer test # Run code quality checks composer lint # Auto-fix code style composer fix
Testing
This bundle uses functional tests with JSON baseline comparison to validate migration correctness.
See docs/TESTING.md for comprehensive documentation:
- Test infrastructure and architecture
- How baseline comparison works
- Adding new test content to Sulu 2.6
- Updating Sulu 3.0 target schema
- Regenerating baselines
❤️ Support and Contributions
The Sulu content management system is a community-driven open source project backed by various partner companies. We are committed to a fully transparent development process and highly appreciate any contributions.
In case you have questions, we are happy to welcome you in our official Slack channel. If you found a bug or miss a specific feature, feel free to file a new issue with a respective title and description on the sulu/SuluPHPCRMigrationBundle repository.
📘 License
The Sulu content management system is released under the under terms of the MIT License.