gal-digital-gmbh/pimcore-simple-migration
A lightweight migration helper for Pimcore that lets you version and apply database or data changes through simple, readable PHP migration files, with a straightforward CLI workflow for running and rolling back migrations across development and production environments.
This is a small, zero-adoption package, so treat the repository itself as the primary documentation and budget time to read src/ before wiring it into a pipeline. The value proposition is narrow: it removes the boilerplate of setting up Doctrine Migrations inside a Pimcore project (bundle registration, connection wiring, migration directory config) so you can drop a PHP file in a folder and run it.
Minimal steps:
composer require gal-digital-gmbh/pimcore-simple-migration — then immediately open vendor/gal-digital-gmbh/pimcore-simple-migration/composer.json and check the require block. Confirm the Pimcore major version (10 vs 11), the PHP constraint, and whether it expects Doctrine DBAL 2 or 3. A mismatch here is the single most likely reason the package will not work in your project.*Bundle.php class implementing Pimcore\Bundle\... / Symfony\Component\HttpKernel\Bundle\Bundle and register it in config/bundles.php; if it only ships commands, they will be autoconfigured via services.yaml and no registration is needed.bin/console cache:clear
bin/console list | grep -i migration
Do this before reading any blog-style instructions — command names and flags are the part most likely to differ from your expectation.up() / down() vs migrate() / rollback()), how it exposes the DB connection ($this->db, $this->connection, or a Pimcore\Db helper), and where the tracking table lives.First real use case: a change that must exist identically on dev, staging, and prod but which the Pimcore admin UI does not version — adding an index to a custom table, backfilling a field on existing objects, or creating the folder tree a feature assumes. Write one migration, run it against a disposable copy of the dev database, and confirm the tracking table recorded it before you trust it on anything shared.
One migration per intent. Keep schema DDL, class-definition changes, and data backfills in separate files. Pimcore's class-definition cache invalidation is a different failure mode from a failed ALTER TABLE, and separating them makes a half-failed deploy diagnosable.
Import class definitions from JSON rather than mutating them in code. The most reliable way to version a data object class in a migration is to export it from the admin UI, commit the JSON, and import it:
// shape only — verify the service and signature against your Pimcore version
$class = \Pimcore\Model\DataObject\ClassDefinition::getByName('Product');
$json = file_get_contents(__DIR__ . '/../../var/classes/Product.json');
\Pimcore\Model\DataObject\ClassDefinition\Service::importClassDefinitionFromJson($class, $json, true);
$class->save();
The same pattern exists for field collections and object bricks. Doing it this way means the migration is a diff of a committed artifact, not a chain of imperative calls that silently no-ops when a field already exists.
Guard every operation. Migrations get re-run on restored databases and partially-applied environments. Check before you act:
$schema = $this->connection->createSchemaManager();
if (!$schema->tablesExist(['plugin_queue'])) { /* create */ }
Idempotent migrations are the difference between "rerun it" and "restore from backup."
Keep migrations environment-agnostic and non-interactive. They run in CI and in deploy hooks with no TTY. No prompts, no readline, no hardcoded hostnames. If a migration seeds demo content, gate it on an env var rather than on APP_ENV inside the migration file.
Sequence data backfills in batches. A migration that loads 50k objects and calls save() on each will time out and hold locks. Chunk by ID range, and log progress so a killed run is resumable.
Wire it into deploy, not into a human's memory. The package only pays off if bin/console <your-migration-command> is a line in your deployment script (or a CI job with an explicit approve step), running after code deploy and before the cache warmup. Pair it with bin/console pimcore:cache:clear — class-definition and object changes are not visible to the application until the cache is rebuilt.
Rollback is a courtesy, not a strategy. Write down() when it is genuinely reversible (dropping an index you added). For destructive migrations, either leave rollback unimplemented with a comment saying so, or make it a restore instruction. A down() that deletes a column is worse than no down().
composer.json ("gal-digital-gmbh/pimcore-simple-migration": "1.0.0", not ^1.0), and consider reading the source once rather than debugging it in production. There is no community knowledge base to fall back on.migration_versions or similar), running doctrine:migrations:migrate over the same directory will either re-execute applied migrations or fail on duplicates. Pick one runner per migration directory and note the decision in the README of your project.fetchAll() became fetchAllAssociative() and getSchemaManager() moved to createSchemaManager()). If the package predates that, expect to patch a fork — check whether the maintainer accepts PRs or whether you should vendor it.ClassDefinition::save(), run bin/console pimcore:cache:clear (and, in some setups, restart PHP-FPM) before declaring victory.ALTER TABLEs and fails on the third leaves you in a partial state. Keep DDL migrations to one statement where possible, and always test against a production-shaped copy.SELECT * FROM <tracking_table> ORDER BY ... to see what the package thinks has run. "It says applied but nothing changed" and "it keeps re-running" are both diagnosed here in seconds.-vvv, and temporarily add fwrite(STDERR, ...) at the top of up() to confirm the method is even reached. If the file is not being picked up, the issue is directory config or filename convention, not your SQL.How can I help you explore Laravel packages today?