Override updated_at, or "hidden" save() parameters

Another interesting feature in Laravel Eloquent mechanism. When updating existing entry, we just use update() or save() and then updated_at field is changed automatically. But what if we want to stick our own updated_at instead of automatic one?

How timestamps work

If in your Model you don't have $timestamps = false setting, you should have created_at and updated_at in your DB table, which are changed automatically in the background, you don't need to take care of anything. At the moment of creating new entry with create() or save(), created_at and updated_at are filled with current timestamp. And when something is updated, then only updated_at field is changing, created_at remains the same, well, forever. Notice: interestingly, updated_at is changed only when at least one column in table was *actually* changed. If you just call update() with old values, updated_at would remain the same.

Why we might need to override updated_at?

Let's say we have some external data source - that we need to sync something with another database or API. And you need to update entry in your database, but with updated_at timestamp from another database. So you would think we can do that:
$product = Product::find($id);
$product->updated_at = '2015-01-01 10:00:00';
$product->save();
But the problem is that updated_at is overridden after that by automatic Laravel magic - so whatever you put in this value it will be updated to current timestamp.

Solution: save() with parameters

Sometimes it's interesting to dig deeper in Laravel functions and find out about some additional optional parameters. So this is the example - when calling save() method, we can specify timestamps to be ignored and not updated automatically. Like this:
$product = Product::find($id);
$product->updated_at = '2015-01-01 10:00:00';
$product->save(['timestamps' => false]);
If we look at the actual save() method, it looks like this:
/**
 * Save the model to the database.
 *
 * @param  array  $options
 * @return bool
 */
public function save(array $options = array())
Which then is actually leads to method performUpdate() and used only for this instance:
protected function performUpdate(Builder $query, array $options = [])
{
// .... some code
// ....
// First we need to create a fresh query instance and touch the creation and
// update timestamp on the model which are maintained by us for developer
// convenience. Then we will just continue saving the model instances.
if ($this->timestamps && array_get($options, 'timestamps', true))
{
    $this->updateTimestamps();
}
So far I cannot find any more usages of the $options, except for 'timestamps', but I guess it's possibly to add some parameters in the future, so pretty flexible code. Taylor strikes again.
avatar

This is an old post - but for people that stumble on this while googling. In laravel9 no need to set this $options parameter to manually set the updated_at. If the updated_at property is dirty it will use your value when saving.

avatar

I couldn't find a version dating back to v6 that supported this as the options paramater doesn't exist. Here is the method in old v8 https://github.com/laravel/framework/blob/8.x/src/Illuminate/Database/Eloquent/Model.php#L1050

Here is how you can do this today:

$inspection->parent = $newId;
$inspection->timestamps = false;
$inspection->save();

In this case timestamps do not update when record is saved.

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 59 courses (1056 lessons, total 44 h 09 min)
  • 78 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials