Laravel Lock: Distributed Locks for Models &amp; Routes | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Laravel Lock: Distributed Locks for Models and Routes        On this page       1. [  What Is Laravel Lock? ](#what-is-laravel-lock)
2. [  Acquiring and Releasing Locks ](#acquiring-and-releasing-locks)
3. [  Model-Scoped Locks with HasLocks ](#model-scoped-locks-with-haslocks)
4. [  Route Middleware ](#route-middleware)
5. [  Cache vs. Database Storage ](#cache-vs-database-storage)
6. [  Installation ](#installation)
7. [  Key Takeaways ](#key-takeaways)

  ![Laravel Lock: Distributed Locks for Models and Routes](https://cdn.msaied.com/562/7649de72113e99332a9f7e25015f9397.png)

 [  Laravel ](https://www.msaied.com/articles?category=laravel) [  Composer Pacakge ](https://www.msaied.com/articles?category=composer-pacakge)  #Laravel   #Distributed Locks   #Composer Package   #Race Conditions   #Queue Workers  

 Laravel Lock: Distributed Locks for Models and Routes 
=======================================================

     17 Aug 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   What Is Laravel Lock?  ](#what-is-laravel-lock)
2. [  02   Acquiring and Releasing Locks  ](#acquiring-and-releasing-locks)
3. [  03   Model-Scoped Locks with HasLocks  ](#model-scoped-locks-with-haslocks)
4. [  04   Route Middleware  ](#route-middleware)
5. [  05   Cache vs. Database Storage  ](#cache-vs-database-storage)
6. [  06   Installation  ](#installation)
7. [  07   Key Takeaways  ](#key-takeaways)

 What Is Laravel Lock?
---------------------

Race conditions in queue workers are easy to overlook until a customer receives two identical shipments. Laravel's built-in `Cache::lock()` handles the primitive, but you still have to format the key, manage the owner token, and remember the `finally` block every time. [Laravel Lock](https://github.com/zaber-dev/laravel-lock), by Md Mahedi Zaman Zaber, wraps all of that behind a fluent builder, a model trait, and route middleware.

Acquiring and Releasing Locks
-----------------------------

The `Lock` facade accepts an action name and an optional target, then returns a pending lock you can configure before acquiring:

```php
use ZaberDev\Lock\Facades\Lock;

$lock = Lock::for('shipment_dispatch', $shipment)->ttl(120);

if ($lock->acquire()) {
    try {
        $carrier->dispatch($shipment);
    } finally {
        $lock->release();
    }
}

```

Each builder generates its own UUID owner token. A second `Lock::for(...)` instance carries a different token, so calling `release()` on it does nothing — preventing accidental cross-process releases. When the acquire and release happen in separate processes, set the token explicitly with `->owner('worker-7')`.

For a cleaner one-liner, `block()` acquires, runs the callback, and releases automatically:

```php
$manifest = Lock::for('shipment_dispatch', $shipment)
    ->block(function () use ($shipment, $carrier) {
        return $carrier->dispatch($shipment);
    });

```

If the lock is already held, `block()` throws `LockAcquisitionException` — which carries the `LockInfo` of the blocking lock — rather than silently skipping the work. Both `acquire()` and `block()` accept a wait duration so they retry before giving up:

```php
$lock->acquire(blockSeconds: 5);
Lock::for('stock_allocation', $warehouse)->block($callback, 60, 5);

```

Model-Scoped Locks with HasLocks
--------------------------------

Add the `HasLocks` trait to any Eloquent model and the lock target is derived automatically from the morph class and primary key:

```php
use ZaberDev\Lock\HasLocks;

class Shipment extends Model
{
    use HasLocks;
}

$shipment->lock('dispatch')->ttl(120)->acquire();
$shipment->isLocked('dispatch');
$shipment->forceReleaseLock('dispatch');

```

The generated key looks like `dispatch:App_Models_Shipment:42`. Register a morph map and you get the shorter alias. Non-model targets can implement the `Lockable` interface and return a custom identifier string.

Route Middleware
----------------

The package registers a `lock` middleware alias. Pass it an action name and a TTL in seconds:

```php
Route::post('/warehouse/reconcile', [ReconcileController::class, 'store'])
    ->middleware('lock:warehouse_reconcile,300');

```

To scope the lock to a specific route model binding, embed the parameter in the action name:

```php
Route::post('/shipments/{shipment}/dispatch', [ShipmentController::class, 'dispatch'])
    ->middleware('lock:shipment_dispatch:{shipment},60');

```

When the lock is already held, the middleware throws `LockAcquisitionException` before the controller runs. The exception code is 423, but you need to handle it explicitly to return the right HTTP status:

```php
$exceptions->render(function (LockAcquisitionException $e) {
    return response()->json([
        'message' => 'Already processing. Try again in a moment.',
        'retry_after' => $e->lockInfo?->remainingSeconds(),
    ], 429);
});

```

Cache vs. Database Storage
--------------------------

The default driver is set via `LOCK_DRIVER`. Switch per lock with `->using('database')`:

- **Cache driver** — uses `Cache::add()` for atomicity; fast and suitable for Redis or Memcached.
- **Database driver** — writes to a `locks` table with `lockForUpdate()` inside a transaction; survives cache flushes and supports Eloquent queries.

Expired rows are pruned via Laravel's `Prunable` trait. Schedule it daily:

```php
Schedule::command('model:prune', ['--model' => LockModel::class])->daily();

```

Three events fire when enabled: `LockAcquired`, `LockFailed`, and `LockReleased`. Listening for `LockFailed` surfaces which actions actually contend in production.

Installation
------------

Requires PHP 8.2+ and Laravel 11, 12, or 13:

```bash
composer require zaber-dev/laravel-lock
php artisan vendor:publish --tag=locks-config
php artisan vendor:publish --tag=locks-migrations
php artisan migrate

```

Key Takeaways
-------------

- Fluent builder with `acquire()`, `block()`, `refresh()`, and inspection helpers like `remainingSeconds()`.
- `HasLocks` trait auto-generates model-scoped lock keys using morph class and primary key.
- Route middleware protects endpoints or individual model routes without touching controller code.
- Two drivers: fast cache-backed locks or durable database locks that survive restarts.
- `LockAcquisitionException` carries `LockInfo` so callers know how long to wait before retrying.

---

Source: [Laravel Lock: Distributed Locks for Models and Routes — Laravel News](https://laravel-news.com/laravel-lock)

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-lock-distributed-locks-for-models-and-routes&text=Laravel+Lock%3A+Distributed+Locks+for+Models+and+Routes) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-lock-distributed-locks-for-models-and-routes) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  What is the difference between the cache and database drivers in Laravel Lock?        The cache driver uses `Cache::add()` for atomicity and is faster, making it suitable for short-lived locks on Redis or Memcached. The database driver writes rows to a `locks` table using `lockForUpdate()` inside a transaction, so locks survive a cache flush or Redis restart and can be queried with Eloquent. 

      Q02  How does the route middleware handle a request when a lock is already held?        The `lock` middleware throws a `LockAcquisitionException` before the controller runs. The exception code is 423, but you must handle it explicitly in your exception handler to return the appropriate HTTP status to the client — for example, a 429 response with a `retry_after` value from `$e-&gt;lockInfo-&gt;remainingSeconds()`. 

      Q03  Can I use Laravel Lock with non-Eloquent targets?        Yes. For targets that are not Eloquent models, implement the `Lockable` interface on your value object and return a custom identifier string from `getLockTargetIdentifier()`. That string becomes the second segment of the lock key. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![Streaming AI Responses in Laravel: Token Budgets, Structured Output, and Agent Contracts](https://cdn.msaied.com/584/3ffe135721c65ab3f9b40401dc3c41de.png) laravel ai llm 

### Streaming AI Responses in Laravel: Token Budgets, Structured Output, and Agent Contracts

Learn how to stream LLM responses in Laravel, enforce token budgets, and lock structured output to typed PHP c...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 23 Aug 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/streaming-ai-responses-in-laravel-token-budgets-structured-output-and-agent-contracts) [ ![Typed PHP 8.3 Enums as Eloquent Casts, Route Parameters, and Validation Rules](https://cdn.msaied.com/583/7301900aac0f2ec5d1347df11ce92188.png) laravel php8.3 enums 

### Typed PHP 8.3 Enums as Eloquent Casts, Route Parameters, and Validation Rules

Go beyond basic enum casting. Learn how to wire PHP 8.3 backed enums into Eloquent, route model binding, and c...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 23 Aug 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/typed-php-83-enums-as-eloquent-casts-route-parameters-and-validation-rules) [ ![Livewire v3 Lazy Components, Islands, and Deferred Loading in Practice](https://cdn.msaied.com/582/564b2d098d2b94b53d4f5319ac77d58b.png) livewire laravel performance 

### Livewire v3 Lazy Components, Islands, and Deferred Loading in Practice

Lazy components, islands, and deferred loading in Livewire v3 let you ship fast initial pages without sacrific...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 23 Aug 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/livewire-v3-lazy-components-islands-and-deferred-loading-in-practice-1) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
