ucan-lab/laravel-dacapo is the inverse of migrate: it reads the live schema of a database and writes Laravel migration files that reproduce it. It is a dev-only tool for DB-first projects, legacy codebases, or any situation where the database is the source of truth and the database/migrations folder is behind.
Minimal steps:
composer require --dev ucan-lab/laravel-dacapo
php artisan vendor:publish --tag=dacapo # creates config/dacapo.php
php artisan dacapo
Generated files land in your migrations directory (configurable). Look at config/dacapo.php first — it controls the output path, which connection is inspected, force-overwrite behaviour, and which tables to skip. Almost every "why did it do that?" question is answered by that file.
First use case: point it at a copy of a legacy/staging database, run php artisan dacapo, then verify the output by wiping a scratch database and running php artisan migrate against it. If the schema round-trips, you have a trustworthy migration baseline to commit.
5.2.0 notes: adds PHP 8.2 support, a refreshed Artisan console UI (progress/spinner output instead of plain text), and fixes a crash (array_intersect_key(): Argument #2 must be of type array, null given) that hit users with incomplete/older published config. If you are on PHP 8.2 or have been seeing that error, upgrade and re-publish the config.
Baseline generation for an existing app. Wipe/ignore the existing migrations, generate a single snapshot from the current schema, commit it as 0001_01_01_000000_baseline.php (or the full generated set), and from then on write migrations by hand. This is the most common day-to-day use: stop hand-writing history for a database that already exists.
Schema-drift detection in CI. Run dacapo into a temporary directory, normalise the output, and git diff --exit-code against committed migrations. Any diff means someone changed the database directly instead of via a migration. Keep the generation step on a throwaway SQLite/MySQL service container so it never touches real data.
Dedicated inspection connection. Rather than letting it read your default connection, configure a schema-only connection (read replica, or a dumped copy) in config/dacapo.php. This makes it safe to run locally against realistic data volumes and avoids accidental writes on the wrong environment.
Iterating on a local schema. While prototyping with a GUI client, enable force-overwrite so re-running regenerates the files in place. Keep that flag off on shared branches — see gotchas.
Custom stubs for house style. The 5.2.0 refactor of MigrationFile, DatabaseDriver and the stub classes makes published stubs the clean extension point: adjust the template to match your team's migration conventions (typed properties, up/down ordering, comment headers) so generated files don't need mass reformatting in review.
Review-then-edit workflow. Treat generated migrations as a first draft for a PR, not as committed artefacts of an automated process. The valuable part is the column/type/index inventory; the parts you usually fix by hand are ordering, naming, and anything DB-specific.
migrate:fresh on a scratch connection), otherwise you get "table already exists" on first run.array_intersect_key null crash fixed in this release came from missing/legacy config arrays. After upgrading, re-publish config/dacapo.php and run php artisan config:clear — a stale cached config is the usual cause of "it sees no tables" or "it ignores my exclusions."migrations, failed_jobs, telescope_*, job_batches, session/cache tables and pivot tables will otherwise show up as migrations you don't want. The exclusion list in config is the first thing to curate.migrate to catch this, and reorder or split the constraint into a later migration.dacapo in a script or CI job that parses output, prefer --no-interaction and quiet flags, and don't assert on the human-readable progress lines introduced in 5.2.0.-v to see which connection and directory were resolved. Nine times out of ten the answer is "wrong connection" or "cached config," not the package.How can I help you explore Laravel packages today?