mvrhov/phinx-bundle
Symfony bundle that integrates Phinx database migrations into your app, letting you configure Phinx and run migration commands through Symfony’s console. Useful for managing schema changes across environments with a familiar, versioned migration workflow.
This is a Symfony bundle that wires the standalone Phinx migration tool into a Symfony application, so you can drive migrations through bin/console and keep Phinx configuration alongside the rest of your Symfony config instead of a separate phinx.yml.
Minimum steps:
composer require --dev mvrhov/phinx-bundle (pin the Symfony version you need — see Gotchas).app/AppKernel.php. In Symfony 4+: add its class to config/bundles.php. Open vendor/mvrhov/phinx-bundle/ and confirm the bundle class name and namespace before registering — with zero dependents and a 2017 release, do not assume the README matches your Symfony version.phinx: section to config/config.yml (or phinx.yml if the bundle version you pull expects a Phinx-native config). The minimum is a migrations path and one environment with an adapter and connection details.bin/console list phinx to see which commands the bundle actually registers, then run bin/console phinx:status.Where to look first:
vendor/mvrhov/phinx-bundle/DependencyInjection/Configuration.php — the authoritative list of config keys. This is more reliable than the README.Command/ directory — tells you the exact console command names and options.robmorgan/phinx version in composer.lock — the bundle's API surface depends heavily on it (pre-1.0 vs 1.0 changed a lot).First use case: point the bundle at your existing migrations/ directory, generate a migration with phinx:create AddFooTable, write the change() method, run phinx:migrate, and confirm rows appear in the phinxlog table. Once that loop works, you have the whole day-to-day workflow.
Single source of truth for connection config. The main value of the bundle is avoiding a second copy of DB credentials. Feed Phinx the same parameters your app already uses (%database_host%, %database_name%, etc.) via the phinx: config block. If the bundle exposes a Doctrine-DBAL-backed adapter, prefer it so migrations and the ORM can never drift onto different databases.
Environment separation. Configure at least development and production blocks, with default_environment set per kernel environment. Keep destructive operations (fresh drops, seeds) restricted to dev by scoping which commands your deploy script calls, not by trusting the config alone.
Migrations as change() methods. Prefer reversible change() over explicit up()/down() where the operation is supported (create table, add column, add index). It halves the maintenance surface and makes phinx:rollback trustworthy in staging.
Deploy pipeline. The typical production flow is: phinx:status (log output, non-fatal) → phinx:migrate → application boot. Run migrations as a distinct step before new app code serves traffic, and gate it so only one deploy worker runs it. Phinx's phinxlog table is your only lock — it is not a distributed lock.
Seeds for fixtures, not for reference data. Use phinx:seed for local dev fixtures. For reference/lookup data that production needs, use a migration so it is versioned and ordered with the schema.
Testing. Point the test environment at a separate database and run phinx:migrate in your test bootstrap (or a CI step) rather than relying on a schema dump. This catches migration ordering bugs that a doctrine:schema:create path will hide.
Integration tips. If you already use Doctrine Migrations, do not run both against the same schema — pick one. If you must coexist, give Phinx a distinct migration_table name and a separate migrations directory so status output stays readable.
Maintenance risk is the headline. Last release December 2017, six stars, zero dependents. Treat this as an unmaintained dependency: pin the exact version, commit the lock file, and be prepared to vendor a patch. Budget for replacing it with a plain phinx.php config and direct vendor/bin/phinx invocation — that is the fallback if Symfony or PHP upgrades break the bundle.
Symfony and PHP version ceilings. A 2017 bundle predates Symfony 5/6 service definitions, autowiring changes, and PHP 8. Expect friction on modern stacks. Check composer.json require constraints before installing; composer require will tell you quickly whether it can even resolve.
Phinx 0.9 vs 1.0. The bundle may pin an old robmorgan/phinx. If so, migration class signatures, Table API details, and config key names differ from current Phinx docs. Read the docs for the version actually installed, not the latest.
Config precedence confusion. If the bundle merges a native phinx.yml with Symfony config, it is not always obvious which wins. When a setting appears ignored, dump the resolved config (a small compiler-pass debug or debug:container --parameter for exposed parameters) rather than guessing.
Migrations run outside the Symfony container. Migration classes are instantiated by Phinx, not the DI container. You cannot constructor-inject services. If a migration needs app services, fetch them via a global kernel boot or keep migrations to pure SQL/schema work — the latter is strongly preferred.
DDL and transactions. MySQL performs implicit commits on DDL, so a failed multi-statement migration leaves partial state. Keep migrations small and single-purpose so a failure is easy to reason about and re-run.
Debugging checklist. phinx:status -vvv for command-level errors; inspect the phinxlog table directly when a version appears applied but the schema disagrees; check file naming (YYYYMMDDHHMMSS_ClassName.php) and that the class name matches the filename exactly — Phinx silently skips mismatches in some versions.
Extension points. The cleanest escape hatches, in order: (1) override config values in your app config, (2) decorate or extend the bundle's commands via a compiler pass, (3) drop the bundle and use Phinx directly with a phinx.php that reads $_ENV. Option 3 is the realistic long-term path given the package's age.
How can I help you explore Laravel packages today?