Laravel refreshForUpdate() Pessimistic Locking | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. [Laravel](https://www.msaied.com/public/articles?category=laravel)
6. /
7. Pessimistic Locking in Laravel Eloquent with refreshForUpdate()

   [Laravel](https://www.msaied.com/public/articles?category=laravel) [Tips &amp; Tricks](https://www.msaied.com/public/articles?category=tips-tricks) 

 Pessimistic Locking in Laravel Eloquent with refreshForUpdate()
================================================================

 Laravel 13.27 adds refreshForUpdate() to Eloquent, letting you reload a model from the database and acquire a FOR UPDATE row lock in a single method call — no more manual query rebuilding inside transactions.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 28 Aug 2026 · Updated 29 Aug 2026 · 3 min read

ShareCopy linkCopied

 ![Pessimistic Locking in Laravel Eloquent with refreshForUpdate()](https://cdn.msaied.com/605/c67ac0ef4197be20a45afdbc4f26f670.png) 

  On this page +1. [What Is refreshForUpdate()?](#what-is-coderefreshforupdatecode)
2. [How It Works Under the Hood](#how-it-works-under-the-hood)
3. [Before and After](#before-and-after)
4. [Before Laravel 13.27](#before-laravel-1327)
5. [After Laravel 13.27](#after-laravel-1327)
6. [Key Takeaways](#key-takeaways)

 What Is `refreshForUpdate()`?
-----------------------------

Laravel has long provided two separate tools for working with database rows inside transactions:

- `refresh()` — reloads a model's attributes from the database.
- `lockForUpdate()` — appends `FOR UPDATE` to a query, preventing other transactions from reading or modifying the row until the lock is released.

What was missing was a way to do **both on an existing model instance** without rebuilding the query manually. Laravel 13.27 fills that gap with `refreshForUpdate()`, contributed by [@stevebauman](https://github.com/stevebauman) in [\#61247](https://github.com/laravel/framework/pull/61247).

---

How It Works Under the Hood
---------------------------

The implementation is concise:

```php
public function refreshForUpdate()
{
    if (! $this->exists) {
        return $this;
    }

    return $this->refreshUsingQuery(
        $this->newQueryWithoutScopes()->lockForUpdate()
    );
}

```

It delegates to `refreshUsingQuery()` — the same internal helper that powers `refresh()` — but passes a query that already has `lockForUpdate()` applied. That helper:

1. Scopes the query to the model's primary key.
2. Routes the query through `useWritePdo()` so a read replica cannot serve a lock you are about to depend on.
3. Calls `firstOrFail()` to fetch the row.
4. Replaces the model's raw attributes in place.
5. Reloads any relations that were already eager-loaded.
6. Syncs the original attribute state.

The only addition `refreshForUpdate()` makes over a plain `refresh()` is the `FOR UPDATE` lock.

---

Before and After
----------------

### Before Laravel 13.27

To safely decrement stock while preventing concurrent overselling, you had to re-query the model explicitly inside the transaction:

```php
public function purchase(Product $product): Response
{
    DB::transaction(function () use ($product) {
        $product = Product::query()
            ->lockForUpdate()
            ->findOrFail($product->getKey());

        if ($product->stock === 0) {
            throw new RuntimeException('The product is out of stock.');
        }

        $product->decrement('stock');
    });

    // ...
}

```

This works, but it requires you to reassign `$product` and remember to call `lockForUpdate()` every time.

### After Laravel 13.27

With `refreshForUpdate()` you call the method directly on the model instance:

```php
public function purchase(Product $product): Response
{
    DB::transaction(function () use ($product) {
        $product->refreshForUpdate();

        if ($product->stock === 0) {
            throw new RuntimeException('The product is out of stock.');
        }

        $product->decrement('stock');
    });

    // ...
}

```

The model is refreshed with the latest database values **and** locked for the duration of the transaction — in one readable line.

---

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

- `refreshForUpdate()` is available from **Laravel 13.27** onward.
- It combines `refresh()` and `lockForUpdate()` into a single Eloquent method call.
- The query is always routed to the **write PDO connection**, preventing stale reads from a replica.
- If the model does not exist (`$this->exists === false`), the method returns early without querying.
- It must be called **inside a `DB::transaction()` block**; a `FOR UPDATE` lock has no effect outside a transaction.
- Already-loaded Eloquent relations are refreshed automatically alongside the model's own attributes.

---

*Source: [Pessimistic Locking in Laravel Eloquent with refreshForUpdate() — Laravel News](https://laravel-news.com/laravel-refresh-for-update)*

- [Laravel](https://www.msaied.com/public/articles?search=Laravel)
- [Eloquent](https://www.msaied.com/public/articles?search=Eloquent)
- [Pessimistic Locking](https://www.msaied.com/public/articles?search=Pessimistic%20Locking)
- [Database Transactions](https://www.msaied.com/public/articles?search=Database%20Transactions)
- [Laravel 13](https://www.msaied.com/public/articles?search=Laravel%2013)

 Frequently asked questions 
---------------------------

  Does refreshForUpdate() work outside a database transaction?No. The FOR UPDATE lock it applies is a database-level construct that only holds for the duration of an open transaction. Calling refreshForUpdate() outside a DB::transaction() block will still execute the query, but the lock will be released immediately and provide no concurrency protection.

   Will refreshForUpdate() reload eager-loaded relationships?Yes. It delegates to the same refreshUsingQuery() helper used by refresh(), which reloads any relations that were already loaded on the model instance before the call.

   What happens if the model does not exist in the database?If $this-&gt;exists is false, refreshForUpdate() returns the model instance immediately without issuing any query, so it is safe to call even on unsaved models.

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

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

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleMask Query Bindings in Laravel Exception Messages](https://www.msaied.com/public/articles/mask-query-bindings-in-laravel-exception-messages) [Next articleLaravel Queues at Scale: Backpressure, Dead-Letter Queues, and Graceful Degradation](https://www.msaied.com/public/articles/laravel-queues-at-scale-backpressure-dead-letter-queues-and-graceful-degradation)  

   On this page
-------------

1. [What Is refreshForUpdate()?](#what-is-coderefreshforupdatecode)
2. [How It Works Under the Hood](#how-it-works-under-the-hood)
3. [Before and After](#before-and-after)
4. [Before Laravel 13.27](#before-laravel-1327)
5. [After Laravel 13.27](#after-laravel-1327)
6. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
