Versionia replaces Laravel's "one row per migration file" ledger with a single schema-version pointer. Instead of migrations tracking 40 individual files, you have numbered schema versions (1, 2, 3…) and migrate to a target version or roll back to one. Think Doctrine Migrations or Rails' schema_migrations version integer, not Laravel's per-file batch model.
Before you install, verify it still boots on your stack. The package is archived and targets Laravel 5. Treat it as a legacy-project tool, not something you introduce to a Laravel 8+ codebase.
composer.json for its illuminate/* and PHP requirements. That tells you far more than the README about what it will actually run on.composer require jumilla/laravel-versionia
config/app.php:
'providers' => [
// ...
Jumilla\Versionia\VersioniaServiceProvider::class,
],
Confirm the exact FQCN with composer show jumilla/laravel-versionia or by listing vendor/jumilla/laravel-versionia/src/ — don't trust copy-paste from a five-year-old README. On 5.5+ it may auto-discover via extra.laravel.php artisan vendor:publish
php artisan list | grep -i version
You're looking for a migrate/rollback pair that takes a target version argument (something like versionia:migrate 12 / versionia:rollback 11).Where to look first: the service provider (what it binds and registers), the commands directory (the real API surface), config/versionia.php, and whether it defines its own migration repository class. Those four files are the whole package.
One version = one logical change set. Group related DDL into a single numbered class rather than imitating Laravel's one-file-per-table style. That keeps the version pointer meaningful and rollbacks atomic-ish.
// database/versionia/0003_add_billing_tables.php (path per config)
class AddBillingTables
{
public function up() { /* create invoices, payments, add users.billing_id */ }
public function down() { /* drop in reverse dependency order */ }
}
Always write down(). The entire value proposition is jumping back to a known version. A version without a tested down() is a version you can only move forward from, which defeats the tool. Test up() → down() → up() in staging before every deploy.
Pin the target version in deployment, not in the code path. Keep the "desired version" as a config/env value and have your deploy script migrate to it. That makes deploys idempotent: rerunning a deploy on an already-correct schema is a no-op, which is the biggest operational win over per-file batches.
Separate DDL versions from data-migration versions. Schema changes are cheap to reason about and often reversible; data backfills are neither. Number them distinctly (or keep data moves in a Laravel command/job) so a rollback doesn't drag a lossy UPDATE with it.
Baseline existing databases at v1. For an app with a live schema, dump the current structure into version 1's up(), set the pointer to 1, and never re-run it. This is where version-based systems shine compared to retrofitting 200 Schema::create calls.
Append-only version numbers across branches. Reserve the next number before you branch, or assign ranges per developer. The single-pointer model has no batch table to forgive a merge that lands an out-of-order file — it just compares numbers.
Test bootstrap: migrate to latest in setUp/CI setup, and drop-and-rebuild locally by migrating down to 0 (or wiping and re-migrating to latest). Confirm the package's commands are callable from Artisan::call() before wiring them into PHPUnit.
Don't run two migrators over the same schema. If you adopt Versionia, retire the native migrate/migrate:rollback on those connections, or the two ledgers will disagree about "current state" forever. Pick one per database.
Seeders stay separate. Versionia governs structure; run db:seed (idempotent seeders, updateOrCreate) after the migrate step in your deploy sequence: migrate → seed → clear caches.
doctrine/migrations (which has a real version-pointer model and active maintenance) or native Laravel migrations with explicit --path and --step discipline. Forking Versionia is viable only because the surface area is tiny.migrate, migrate:rollback, and migrate:status. Run php artisan list after install and check route:list-style conflicts — if the package overrides the core migrator, that's a deliberate architectural commitment, not an add-on.UPDATE to the version table, a failed deploy mid-migrate, or a hand-run SQL change all desync it. Make the version table read-only to humans and check it first when "nothing happens."migrate does nothing, compare the pointer value against the highest available version number and verify the configured migration directory actually contains those files. Add -v for verbose output and inspect the query log to see what the package is comparing.down() must be an exact inverse, including indexes, foreign keys, and column order. Dropping a column on the way down destroys data — decide explicitly whether rollbacks are "restore previous schema" or "reverse the change and accept loss," and document it.migrate:fresh, db:wipe, and schema dumps don't know about the pointer. Native reset commands wipe tables but may leave the version table (or vice versa), producing an app that thinks it's at v12 on an empty database. Add a project-specific reset command that both wipes and re-baselines.config:cache enabled, edited config isn't picked up until you re-cache. If migrations target the wrong connection or directory in prod but work locally, that's your culprit.composer.json ("jumilla/laravel-versionia": "1.*" at minimum, ideally the exact tag) and commit your composer.lock. An abandoned package with a floating constraint is a supply-chain and reproducibility risk.How can I help you explore Laravel packages today?