The Differences Between UpdateOrCreate And UpdateOrInsert in Laravel
Arie Visser • November 21, 2023
laravel php database eloquentWhen you want to update a record and create it when it doesn't exist, you can use the updateOrCreate
or updateOrInsert
method in Laravel.
The so-called "upsert" methods.
I've always wondered what the exact differences are, and it turns that quite some aspects of Laravel have to be discussed to make this clear.
I hope this article will give some insight in the anatomy of these methods, and how to use them.
How to call "upsert" methods in Laravel?
Both updateOrCreate
and updateOrInsert
can be called in the same way.
For example, when you want to update or create a user with a certain email
, you can call the methods like this:
User::updateOrInsert(
['email' => 'brennan50@example.org', 'status' => 'DISABLED'],
['name' => 'Christopher Jones', 'status' => 'ACTIVE']
);
User::updateOrCreate(
['email' => 'brennan50@example.org', 'status' => 'DISABLED'],
['name' => 'Christopher Jones', 'status' => 'ACTIVE']
);
In both cases the user with email
"brennan50@example.org" and status
"DISABLED" will be renamed to "Christopher Jones" and get the status
"ACTIVE".
When no user with email
"brennan50@example.org" and status
"DISABLED" exists, it will be created with name
"Christopher Jones" and status "ACTIVE".
Both methods will always only affect a single record.
So far, they seem to be the same. However, there are some important distinctions.
What are the differences between updateOrCreate
and updateOrInsert
?
After reading the documentation of updateOrCreate
and updateOrInsert
, it is helpful to navigate through Laravel's source.
Eloquent Builder or Query Builder
When looking at the function definitions, the first distinction is that updateOrCreate
is part of Illuminate\Database\Eloquent\Builder
and updateOrInsert
is part of Illuminate\Database\Query\Builder
.
In vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php
you can find updateOrCreate
:
**
* Create or update a record matching the attributes, and fill it with values.
*
* @param array $attributes
* @param array $values
* @return \Illuminate\Database\Eloquent\Model|static
*/
public function updateOrCreate(array $attributes, array $values = [])
{
return tap($this->firstOrCreate($attributes, $values), function ($instance) use ($values) {
if (! $instance->wasRecentlyCreated) {
$instance->fill($values)->save();
}
});
}
As you can see, the updateOrCreate
method will just call firstOrCreate
, and return the new model when it is just created.
When it already exists it will call fill
and save
to update the model.
The logic of the implementation in updateOrInsert
is a bit different, but it will lead to the same result.
You can find the definition in vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php
:
/**
* Insert or update a record matching the attributes, and fill it with values.
*
* @param array $attributes
* @param array $values
* @return bool
*/
public function updateOrInsert(array $attributes, array $values = [])
{
if (! $this->where($attributes)->exists()) {
return $this->insert(array_merge($attributes, $values));
}
if (empty($values)) {
return true;
}
return (bool) $this->limit(1)->update($values);
}
First it checks if a record with the given attributes exists. When it doesn't, it will create one with insert
.
When it does exist and $values
are provided in the second parameter, it will update the record.
Since updateOrInsert
is part of the query builder, it can also be called like this:
use Illuminate\Support\Facades\DB;
DB::table('users')->updateOrInsert(
['email' => 'brennan50@example.org', 'status' => 'DISABLED'],
['name' => 'Christopher Jones', 'status' => 'ACTIVE']
);
However, as seen in the examples above, you can also call updateOrInsert
directly from the model, in this case User::updateOrInsert
.
How is this possible?
This needs a short explanation about the __call
and __callStatic
magic methods in PHP.
As you can see in Illuminate\Eloquent\Model
, the updateOrInsert
method is not defined.
But, when a class method is inaccessible in a static context, the __callStatic
method will be triggered.
As you can see in the __callStatic
method definition in the Model
class, this will create a new instance of the Model
(in this case User
), and try to call the updateOrInsert
method again from an object context:
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
When a method is inaccessible in an object context, the __call
method will be triggered.
Now, when we look at this method in Illuminate\Eloquent\Model
, we will see that it forwards the call like this :
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
This means the call will be forwarded to Illuminate\Database\Eloquent\Builder
, since newQuery
(at last a method that is defined in the Model
class!) will return an Eloquent Builder instance.
To make things harder, even on the Illuminate\Database\Eloquent\Builder
class, the updateOrInsert
method is not defined.
Again, we turn to the __call
method, where we see the call is forwarded:
$this->forwardCallTo($this->query, $method, $parameters);
Since $this->query
is an instance of Illuminate\Database\Query\Builder
, it will call the updateOrInsert
method that is defined in this class.
Return types
Another difference is the return type.
As seen in the function definition, updateOrInsert
will return a boolean when called from the DB
facade.
However, when the method is called from the model, like in our case User::updateOrInsert(...)
, it will return Illuminate\Database\Eloquent\Builder
.
As explained earlier, it forwards the call first to the Illuminate\Database\Eloquent\Builder
.
Since the method doesn't exist there, the __call
method in this class will be triggered.
This method will return the Illuminate\Database\Eloquent\Builder
object itself, and not the return value of the method it is forwarded to, i.e. the updateOrInsert
method.
The updateOrCreate
method on the other hand, will return the updated or created model.
Timestamps
Something else you will notice, is that updateOrInsert
will not affect the created_at
and updated_at
timestamps.
Even when called from the model like User::updateOrInsert(...)
, they will not be updated.
This can result in a record with a created_at
and updated_at
value that is null
.
The updateOrCreate
method will always update the timestamps.
This is because the method will call the save
method, that is defined in the Model
class, when the model is updated or created.
As you can see in the definition of the save
method, this will trigger model events that can change the timestamps.
Performance
This may lead to the conclusion that is better to use updateOrCreate
in most situations.
The updateOrCreate
method will update the timestamps and return the updated or created model.
One latest distinction that I will mention here however, is that the updateOrInsert
method can be faster.
I don't want to dive into this too far, since it depends on many things such as the environment and the amount of records and fields that are affected.
But when you need to "upsert" a large amount of records, performance can be a reason to prefer updateOrInsert
.