Skip to content

Laravel #[Refreshes]: Refresh Attributes After Writes

How the Laravel #[Refreshes] attribute reloads generated column values after insert and update, what the extra query costs, and when refresh() is better.

Dasun Kalupahana 3 min read 71 views 0 comments
On this page
  1. The problem, reproduced
  2. The fix: #[Refreshes]
  3. What it costs: one extra query per write
  4. Refreshes vs. refresh()
  5. When to use it, and when to skip it
  6. Common questions
  7. Does it work for columns set by triggers or database defaults?
  8. Which Laravel version has it?
  9. References

You create a model, and one of its columns is calculated by the database: a full name built from two fields, a slug, a total. You save it and read the value back. You get null, even though the row in the database is correct. The model only knows what you gave it, and the database never told it what it computed. The Laravel 13 documentation has a small feature for exactly this: the #[Refreshes] attribute. I ran it against a real MariaDB database to see what it does and what it costs.

The problem, reproduced

Here is a table with generated columns. full_name is stored, and slug is computed when read:

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('first_name');
    $table->string('last_name');
    $table->string('full_name')
        ->storedAs("concat(first_name, ' ', last_name)");
    $table->string('slug')
        ->virtualAs("lower(replace(concat(first_name, '-', last_name), ' ', '-'))");
});

With a normal model, this happened:

$user = User::create(['first_name' => 'Ada', 'last_name' => 'Lovelace']);

$user->full_name;   // null  (the database has "Ada Lovelace")
$user->slug;        // null

Eloquent ran one query, the INSERT, and did not read anything back. After an update it was the same: I changed the first name to Grace and saved, the database now said "Grace Lovelace", and the model still held null.

The fix: #[Refreshes]

Tell the model which attributes to reload after a write. This is straight from the documentation:

use Illuminate\Database\Eloquent\Attributes\Refreshes;
use Illuminate\Database\Eloquent\Model;

#[Refreshes('full_name')]
class User extends Model
{
    // ...
}

For more than one, pass an array:

#[Refreshes(['full_name', 'slug'])]
class User extends Model {}

Results with the attribute in place:

  • After User::create(...), full_name was "Ada Lovelace".
  • After changing first_name to Grace and saving, full_name was "Grace Lovelace".
  • With ['full_name', 'slug'], both values came back (Alan Turing and alan-turing).
  • Only the columns you list are loaded. In the single-column example, slug was still null.
  • The refreshed values do not leave the model marked as changed: isDirty() returned false afterwards.

What it costs: one extra query per write

This is the trade-off to know before you use it. I logged the SQL:

insert into `users` (`first_name`, `last_name`) values (?, ?)
select `full_name` from `users` where `id` = ? limit 1

An update behaves the same way: the UPDATE, then a SELECT for just the listed columns. Reading the framework source, that SELECT uses the write connection, which is the safe choice when you have read replicas, because a replica may not have your new row yet. If nothing changed, save() ran no queries at all, so the refresh did not add any either.

The refresh also works with increment(). I used a stored column logins_x2 defined as logins * 2 and listed it in the attribute. After $user->increment('logins', 5), the model showed logins = 5 and logins_x2 = 10 using two queries (the UPDATE and the SELECT).

Refreshes vs. refresh()

You could call $user->refresh() after saving. It reloads every column, not just the ones you care about, and you have to remember to call it at every write. #[Refreshes] does it automatically for the columns you name, wherever the model is saved. Choose refresh() if you only need the value in one place, and the attribute if the model should always be correct after writing.

When to use it, and when to skip it

  • Use it when your code or API response reads a generated column right after a write.
  • Skip it when nobody reads that column straight after saving. You would pay for a SELECT that nothing uses.
  • Be careful on bulk jobs. Creating 10,000 models one by one now means 20,000 queries. Bulk insert() does not create model instances, so it is unaffected, but it also gives you no refreshed values.

Common questions

Does it work for columns set by triggers or database defaults?

The docs describe it for generated columns. The mechanism reloads whatever columns you list, so it should work for any column the database fills in, but I only tested generated columns (stored and virtual).

Which Laravel version has it?

It is in the Laravel 13.x documentation and in the 13.35.0 source I tested. I did not check older releases, so on an older version use refresh() instead.

References

Free resources for this topic

Code snippets and fixes for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 0

Be kind. Comments with links are reviewed before they appear.

No comments yet

Be the first to share your thoughts.

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.