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

Versioning Laravel Package

laravel-enso/versioning

Laravel Enso Versioning provides transparent versioning for Eloquent models, automatically recording changes and letting you review or restore previous revisions. It integrates with the Enso ecosystem for permissions and UI-driven version management.

View on GitHub
Deep Wiki
Context7

Getting Started

The package gives you optimistic concurrency control for Eloquent models: each record carries a version counter; an update carrying a stale version is rejected instead of silently overwriting another user's change.

  1. Install and register. composer require laravel-enso/versioning. The package is archived and targets older Laravel/Enso versions, so on Laravel 10+ you may need to pin it in composer.json or vendor the trait by hand. Register Enso\Versioning\VersioningServiceProvider if it is not auto-discovered.
  2. Add the column. Run the package migration or add $table->unsignedInteger('version')->default(1) to the model's table. Check src/Traits/Versionable.php for the exact column name, default, and cast the trait expects before writing your own migration.
  3. Opt a model in. use Versionable; on the model whose edits you care about — an invoice, a CMS page, a settings singleton. Don't blanket-apply it; it earns its keep only where two people plausibly edit the same row.
  4. First use case. Render the record's current version into a hidden form field (<input type="hidden" name="version" value="{{ $invoice->version }}">). When the form posts, the trait compares the submitted version against the stored one. If they differ, you get a conflict instead of a lost update.
  5. Handle the conflict. Catch the package's conflict exception in app/Exceptions/Handler.php (or a FormRequest) and return HTTP 409 with the fresh record so the front end can show "this record changed while you were editing."

Implementation Patterns

Hidden field / Inertia props. The version must travel with the edit payload. With Blade, a hidden input; with Inertia, pass version in the page prop and send it back on submit. Never trust the client to compute it.

Conflict response shape. Return 409 with the current server-side record. The front end then has three options: reload and discard the user's edits, show a diff, or offer a merge. A bare 409 with no body forces a page refresh and loses work, which defeats the point.

API / If-Match mapping. For REST endpoints, map the version to an ETag or If-Match header. The check the trait performs is exactly precondition-failed semantics, so 428 (missing version) and 412/409 (stale version) are reasonable codes.

Guard writes in bulk operations. Only wrap models where every write path goes through an editable form. If a model is also updated by queue jobs, schedulers, or webhooks, those paths either need to opt out of the check or read-then-write with a retry.

Atomicity when it matters. The trait does check-then-write, not compare-and-swap. If concurrent writers are realistic, wrap the save in a transaction with lockForUpdate() and read the row before validating the version. Otherwise two requests can both pass the check and the second still wins.

Testing. Two-request tests are the honest way to verify it: fetch record A, update it, then attempt a second update with A's original version and assert 409. Mirror that in a feature test so a future Laravel upgrade that breaks the trait fails loudly.

Front-end UX. On 409, keep the user's typed values in the form and show a banner with the fresh server values alongside, so they can reconcile. Reloading on conflict is the correct default only if the form is trivial.

Gotchas and Tips

  • Archived and low-traffic. 36 stars and a 2023 last release mean no fixes for new Laravel versions, and Enso-ecosystem coupling can drag in laravel-enso/core. Prefer the built-in updated_at/If-Unmodified-Since or a hand-rolled version column for new code; treat this package as a reference implementation.
  • Mass assignment will eat your version. If version isn't fillable (or is guarded), $model->fill($request->all()) silently drops it and the check compares against the default. Set it explicitly with forceFill or $model->version = $request->integer('version').
  • Mass updates bypass it. Model::where(...)->update(['status' => 'paid']) fires no model events, so the trait never runs. Any code path that uses query-builder updates is unprotected — audit for these before assuming the model is safe.
  • The check is not atomic. As above: two requests can read version 3, both pass, both write. Only a DB-level lock or a WHERE version = ? guard in the UPDATE gives you real protection.
  • JSON vs HTML rendering. The conflict exception needs different rendering for API and web requests. Centralize it in the exception handler rather than sprinkling try/catch in controllers.
  • Don't leak the version blindly. Exposing it in API responses is fine, but if you also expose updated_at and internal counters, clients start relying on them. Treat the version as a contract you version too.
  • Extension points live in the trait. If the default column or increment behavior doesn't fit, override the trait's methods rather than editing vendor code — the package is archived, so your overrides are the maintenance plan.
  • Verify the schema against the source. Before writing migrations, read src/Traits/Versionable.php and any published migration; column name, nullability, and initial value all have to match or every update silently conflicts or silently passes.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core