Lockable Models
Description
Lockable Models provides short-lived edit locks for Eloquent models.
It is meant for collaborative backoffice flows where a record should be protected from concurrent edits while a user is working on it. The package offers a base lockable model, a base lock model, middleware that prevents actions on already locked models, and a terminate middleware that releases successful edit locks automatically.
Locks are user-aware, expire automatically after a configurable number of minutes, and allow the locking user to continue working on the same record while blocking everyone else.
The package depends on laravel-enso/users for lock ownership and display identity. It does not depend on people directly; the lock message uses the user model's public display API.
Installation
Install the package:
composer require laravel-enso/lockable-models
If needed, publish the config:
php artisan vendor:publish --tag=lockable-models-config
Default configuration:
return [
'lock_duration' => 5,
];
To use the package, your implementation typically needs:
- a model extending
LockableModel - a related lock model extending
ModelLock - a one-to-one table for storing the current lock
- the request middleware pair on routes that edit the model
Features
- Creates user-specific temporary locks for editable models.
- Prevents concurrent actions when another user holds a non-expired lock.
- Allows the same user to continue editing an already locked model.
- Automatically expires stale locks after the configured duration.
- Releases locks automatically after successful requests.
- Provides reusable abstract models for both the lockable record and the lock record.
Usage
Create a lockable model by extending the base class:
use LaravelEnso\LockableModels\Models\LockableModel;
class Order extends LockableModel
{
}
Create a related lock model:
use LaravelEnso\LockableModels\Models\ModelLock;
class OrderLock extends ModelLock
{
}
Protect editing routes with the middleware pair:
Route::middleware([
PreventActionOnLockedModels::class,
UnlocksModelOnTerminate::class,
])->group(function () {
// edit routes
});
Manually acquire and release a lock:
$order->lockFor($user);
$order->unlockFor($user);
Note
UnlocksModelOnTerminate only releases locks when the response status is 200.
If a request fails or returns a different success status, the lock remains active until it is explicitly released or it expires.
API
Models
LaravelEnso\LockableModels\Models\LockableModel
Public methods:
lock()lockFor(User $user): voidunlockFor(User $user): voidlockForMinutes(): int
LaravelEnso\LockableModels\Models\ModelLock
Public methods:
expired(): boolallowed(User $user): booluser(): BelongsToscopeIsExpired(Builder $query): Builder
Middleware
PreventActionOnLockedModelsUnlocksModelOnTerminate
Behavior:
- detects route parameters that are instances of
LockableModel - blocks the request if a valid lock belongs to another user
- acquires the lock for the current user before handling the request
- releases the lock on terminate for successful
200responses
Exceptions
LaravelEnso\LockableModels\Exceptions\ModelLockException
Current lock failure message:
Locked by: :user
Depends On
Required Enso packages:
Framework dependency:
Contributions
are welcome. Pull requests are great, but issues are good too.
Thank you to all the people who already contributed to Enso!