Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Migration Bundle Laravel Package

tilleuls/migration-bundle

A Symfony bundle that integrates Doctrine database migrations into PHP applications, offering configuration and console commands to create, run, and manage schema changes safely across development, staging, and production environments.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle is a thin framework — it gives you a console command plus LoaderInterface / TransformerInterface (and an abstract Transformer). You write the actual loaders and transformers; the bundle wires them into bin/console coop-tilleuls:migration:migrate.

Minimal steps:

  1. composer require tilleuls/migration-bundle. Because it is archived (last release 2020-11-27) and has effectively zero dependents, verify the constraint resolves against your Symfony/PHP version before committing. If it does not, vendor the source instead of relying on Composer solving.
  2. Register CoopTilleuls\MigrationBundle\CoopTilleulsMigrationBundle (or the bundles.php entry if you are on Symfony 4+; check Resources/config/services.xml for the exact service IDs and tags).
  3. Read these three things first, in this order:
    • Command/MigrationCommand.php — this tells you how a transformer is selected, whether a loader is injected or resolved by name, and which options (--offset, --limit, etc.) actually exist.
    • Loader/LoaderInterface.php — the load() contract and return shape (iterable or array).
    • Transformer/TransformerInterface.php and the abstract Transformer — getName(), supports(), transform().
  4. Create one loader (e.g., a DBAL query against the legacy database) and one transformer (e.g., legacy user row → new User entity, persisted with the existing EntityManager).
  5. Register both as services with the tags the bundle expects (or whatever the command's constructor requires — confirm from the command, not from assumption). Then run: bin/console coop-tilleuls:migration:migrate <transformer-name>

First realistic use case: migrate a legacy user table into your new User entity, one transformer per table, running the command per transformer in a deploy or one-off script.

Implementation Patterns

Stream, don't collect. Implement load() as a generator (yield $row) over a DBAL statement or a file handle rather than fetchAll(). This is the single biggest day-to-day decision: it keeps memory bounded and lets you run the same transformer unchanged against small and large datasets.

One transformer per target type, supports() as the router. If a loader emits heterogeneous rows, use supports() to decide which transformer handles a row. Keep getName() stable — it is your CLI identifier and is likely what you pass to the command, so make it something you can type (legacy_user, legacy_order).

Make transform() idempotent. Look up the target by a natural key first (findOneBy(['legacyId' => $row['id']])), then create or update. This lets you re-run a failed migration without truncating and without duplicating rows — essential when the command dies halfway through.

Batch flush and clear manually. The command does not manage your persistence lifecycle for you. Inside transform() (or a small base class you write), accumulate entities and call $em->flush(); $em->clear(); every N rows (100–500 is a reasonable start). Beware: after clear(), any references you held are detached.

Run in prod, non-debug. bin/console --env=prod --no-debug coop-tilleuls:migration:migrate <name>. Debug mode and the profiler inflate memory badly on long migrations.

Test transformers as plain services. Because a transformer is just a class with supports()/transform(), unit-test it with an in-memory or mocked EntityManager. This is much faster than testing through the container and is the main reason to keep transformers small and free of container lookups.

Integration workflow. Chain transformers in a script (migrate legacy_user && migrate legacy_order) after schema/seed is in place. Seed reference data (countries, statuses) with dedicated transformers first, or run them as ordinary fixtures — the migration command should not be your fixture loader.

Composition over inheritance. If the abstract Transformer does not fit, implement TransformerInterface directly; the bundle only cares about the interface contract. Keep a shared abstract base of your own for flush batching, logging, and legacy-ID lookup.

Gotchas and Tips

  • Archived, unmaintained, near-zero adoption. 13 stars and 0 dependents means no issue tracker support and no Symfony-version guarantees. Pin the version in composer.json, and be prepared to fork or inline the ~3 classes this bundle actually provides. Ask whether you need the bundle at all versus copying the command + interfaces into src/.
  • Service tags may not autoconfigure. On Symfony 4+, interfaces from old bundles often aren't in autoconfigure rules. Tag services manually (check Resources/config/services.xml for the exact tag name) and verify with bin/console debug:container --tag=<tag>.
  • Loader ↔ transformer resolution is the first thing to verify. The command must know which loader feeds which transformer — either injected in the constructor, resolved by name, or via tag attributes. Read MigrationCommand before designing your services; this is where most integration time is lost.
  • Silent skips. If supports() returns false, the row is dropped with no error. Log skipped rows in the loader or command wrapper while you are bringing a migration up, otherwise you will discover gaps weeks later.
  • No transaction handling for you. Wrap per-chunk work in $em->wrapInTransaction() or rely on idempotent upserts. Do not assume the command gives you all-or-nothing semantics — it doesn't.
  • Identity map and detach bugs. flush() then clear() every N rows; re-fetch anything you need after clear(). Holding onto entity references across a flush is the classic OOM/crash combo here.
  • No built-in batching/resume. Options like offset/limit are whatever the command implements — confirm rather than assume. If missing, add them in a subclass of the command or a shell loop keyed on a legacy ID watermark stored somewhere.
  • Legacy data quirks land in transform(). Nulls, 0/''/NULL ambiguity, charsets from the old database, timezone-naive datetimes, and enum-like integers. Normalize in the transformer, not in the loader, and write a unit test per quirk.
  • Extension points. Override the command, decorate LoaderInterface, or add a Symfony event/messenger dispatch at the end of transform() if you need post-migration side effects. Because the bundle is small, subclassing is usually cheaper than a PR you cannot merge upstream.
  • Debugging tip. Add a -v log line per transformed row with the legacy ID and target ID during bring-up; on failures you will want to replay exactly that row.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager