49

I have the following code in my controller:

for($i=0; $i<$number_of_tourists; $i++) {
    $tourist = Tourist::updateOrCreate([
        'doc_number' => $request['doc_number'][$I]
    ],
    $tourist_to_update);
}

Each time updateOrCreate runs, it does 1 of 3 things:

  1. Updates the model instance; OR

  2. Creates and saves a new one; OR

  3. Leaves everything unchanged (if model with such values already exists)

I need to check if updateOrCreate has done the first one (updated) and then execute some code.

How can I do it?

3
  • Can you use the updated model event (only fires when the model is actually updated), or should this extra code only be run when this specific set of code updates the model?
    – patricus
    Commented Aug 15, 2017 at 22:43
  • @patricus, thanx! I've used updated model event, but I managed it only to do some simple things like public function handle(TouristUpdated $event) { dump($event); } (it's code from my listener). I can't figure out how can I instruct listener to add some logic into my controller. The logic actually should be creating an array of all $tourist_to_update 's which have triggered the "updated' event. Hope I explained clearly. Commented Aug 15, 2017 at 22:57
  • You use getChanges() detect changed stackoverflow.com/a/51029069 Commented Jun 9, 2022 at 8:14

5 Answers 5

118

You can figure it out like this:

$tourist = Tourist::updateOrCreate([...]);

if(!$tourist->wasRecentlyCreated && $tourist->wasChanged()){
    // updateOrCreate performed an update
}

if(!$tourist->wasRecentlyCreated && !$tourist->wasChanged()){
    // updateOrCreate performed nothing, row did not change
}

if($tourist->wasRecentlyCreated){
   // updateOrCreate performed create
}

Remarks

From Laravel 5.5 upwards you can check if updates have actually taken place with the wasChanged and isDirty method.

  • isDirty() is true if model attribute has been changed and not saved.
  • wasChanged() is true if model attribute has been changed and saved.

There is also a property (not method!) wasRecentlyCreated to check if user was created or not.

$user = factory(\App\User::class)->create();

$user->wasRecentlyCreated; // true
$user->wasChanged(); // false
$user->isDirty(); // false

$user = \App\User::find($user->id);

$user->wasRecentlyCreated; // false
$user->wasChanged(); // false
$user->isDirty(); // false

$user->firstname = 'Max';

$user->wasChanged(); // false
$user->isDirty(); // true

$user->save();

$user->wasChanged(); // true
$user->isDirty(); // false

//You can also check if a specific attribute was changed:
$user->wasChanged('firstname');
$user->isDirty('firstname');

You can checkout the link to the laravel's documentation for wasChanged and isDirty methods.

https://laravel.com/docs/8.x/eloquent#examining-attribute-changes or https://laravel.com/docs/9.x/eloquent#examining-attribute-changes

5

It is pretty easy to determine if the function resulted in an update or an insert (check the wasRecentlyCreated property). However, when using that function, it is less easy to determine if the update actually happened (if the model exists but is not dirty, no update will be performed). I would suggest not using that function, and splitting out the functionality yourself.

This is the function definition:

public function updateOrCreate(array $attributes, array $values = [])
{
    $instance = $this->firstOrNew($attributes);

    $instance->fill($values)->save();

    return $instance;
}

To integrate this into your code, I'd suggest something like:

for ($i=0; $i<$number_of_tourists; $i++) {
    $tourist = Tourist::firstOrNew(['doc_number' => $request['doc_number'][$i]]);

    $tourist->fill($tourist_to_update);

    // if the record exists and the fill changed data, update will be performed
    $updated = $tourist->exists && $tourist->isDirty();

    // save the tourist (insert or update)
    $tourist->save();

    if ($updated) {
        // extra code
    }
}
5
  • thnx! I've already thought about it, but you expalained it in great details. I still hope there is a chance to use 'updated' model event. But if not - I'll choose this answer as 'right'. Commented Aug 15, 2017 at 23:04
  • Could you explain your first sentance It is pretty easy to determine if the function resulted in an update or an insert.? Because thats all I want and I dont know how to get that.
    – Adam
    Commented Feb 8, 2018 at 17:48
  • NVM I already found it: stackoverflow.com/questions/44238257/…
    – Adam
    Commented Feb 8, 2018 at 17:52
  • @Adam Glad you found it. I went ahead and updated my answer to mention it, just in case anyone else comes here wondering as well. Thanks.
    – patricus
    Commented Feb 8, 2018 at 18:22
  • There is now a function called wasChanged() which is doing this job.
    – Adam
    Commented Mar 18, 2018 at 17:10
1

Okay so I couldn't find a good answer for my scenario.

I was using: $this->created_at == $this->updated_at however I would sometimes update the record later in the request, which meant that 20% of the time the created_at and updated_at were about 1ms out.

To combat this I created something a little more relaxed which allows an extra second between creation and modification.

public function getRecentlyCreatedAttribute()
{
    return $this->wasRecentlyCreated || $this->created_at == $this->updated_at || $this->created_at->diffInSeconds($this->updated_at) <= 1;
}

I can now call $this->recentlyCreated which will return true if there is a small difference in time (1 second).

Tbh this is the second time I've needed this in a project, I'm posting as I just ended up googling it and coming back to this thread looking for the same answer.

If someone has a more elegant solution, hmu.

0

@patricus below presented a working way to solve the problem. though @TheFallen here gave a solution which uses Eloquent Events and seems more elegant: Laravel Eloquent Events - implement to save model if Updated

0

The model attribute 'wasRecentlyCreated' would only be 'true' if it has just been created.

There is property named 'changes' in model (it is an array), that determines whether the model has been updated with new values or it has been saved as is without making any changes to its attribute.

Check the following code snippet:

       // Case 1 :  Model Created
        if ($model->wasRecentlyCreated) {

        } else { // Case 2 :  Model Updated

            if (count($model->changes)) { // model has been assigned new values to one of its attributes and saved successfully

            } else { // model has NOT been assigned new values to one of its attributes and saved as is

            }

        }

Not the answer you're looking for? Browse other questions tagged or ask your own question.