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

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

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

  [ home ](https://msaied.com)    [ articles ](https://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://msaied.com/articles?category=laravel) [  Composer Pacakge ](https://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/01M22N44A70A5MC2S599JP0MPH.webp)  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%2Fmsaied.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%2Fmsaied.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://msaied.com/articles) 

 [ ![Laravel September 2026 Product Updates: AI Keys, Semantic Search, and Cloud Improvements](https://cdn.msaied.com/731/759b68914c20a5403430fd886d488a80.png) Laravel Laravel Cloud Laravel Scout 

### Laravel September 2026 Product Updates: AI Keys, Semantic Search, and Cloud Improvements

Laravel's September 2026 updates bring per-tenant AI provider keys, semantic and hybrid search for Typesense a...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 2 Oct 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-september-2026-product-updates-ai-keys-semantic-search-and-cloud-improvements) [ ![Eloquent Custom Relations: Polymorphic Pivots, Has-One-Of-Many, and Raw Join Relations](https://cdn.msaied.com/728/b96439f5ef5f084b44d736951875f964.png) laravel eloquent database 

### Eloquent Custom Relations: Polymorphic Pivots, Has-One-Of-Many, and Raw Join Relations

Beyond belongsTo and hasMany lies a set of underused Eloquent relation types that eliminate ugly query hacks....

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 2 Oct 2026     3 min read  

  Read    

 ](https://msaied.com/articles/eloquent-custom-relations-polymorphic-pivots-has-one-of-many-and-raw-join-relations) [ ![Laravel Caching Strategies: Tags, Stampede Prevention, and Cache-Aside at Scale](https://cdn.msaied.com/727/7873d7495db459e7a6e3bfbb8835852d.png) laravel caching redis 

### Laravel Caching Strategies: Tags, Stampede Prevention, and Cache-Aside at Scale

Beyond simple remember() calls: how to use cache tags for granular invalidation, prevent thundering-herd stamp...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 2 Oct 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-caching-strategies-tags-stampede-prevention-and-cache-aside-at-scale-2) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://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://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://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)
