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/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%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) 

 [ ![MySQL EXPLAIN and Query Profiling in Laravel: Finding Slow Queries Before They Hit Production](https://cdn.msaied.com/563/f2d4a7fb0ab45706cf9330746f7b2588.png) laravel mysql performance 

### MySQL EXPLAIN and Query Profiling in Laravel: Finding Slow Queries Before They Hit Production

Learn how to read MySQL EXPLAIN output, use query profiling tools, and integrate them into a Laravel workflow...

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

 18 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/mysql-explain-and-query-profiling-in-laravel-finding-slow-queries-before-they-hit-production) [ ![How Two Non-Developers Built laracon.us/photos with Claude, Laravel, and Laravel Cloud](https://cdn.msaied.com/561/aa231bfcc6b487d352abf7be487875d3.png) Laravel Cloud Claude AI AI-assisted development 

### How Two Non-Developers Built laracon.us/photos with Claude, Laravel, and Laravel Cloud

Laravel's field marketers built a real, production photo-sharing app for Laracon US 2026 using Claude AI, the...

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

 17 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/how-two-non-developers-built-laraconusphotos-with-claude-laravel-and-laravel-cloud) [ ![Job Batching with Laravel Concurrency: Parallel Work Without the Chaos](https://cdn.msaied.com/559/d55e86a2ecbe32de5e2196f5be63511d.png) laravel concurrency queues 

### Job Batching with Laravel Concurrency: Parallel Work Without the Chaos

Learn how to combine Laravel's Concurrency facade with job batching to run parallel workloads safely, avoid sh...

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

 17 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/job-batching-with-laravel-concurrency-parallel-work-without-the-chaos) 

   [  ![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)
