46

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?

Kirk Beard
  • 9,569
  • 12
  • 43
  • 47
Sergej Fomin
  • 1,822
  • 3
  • 24
  • 42
  • 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 Aug 15 '17 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. – Sergej Fomin Aug 15 '17 at 22:57
  • You use getChanges() detect changed https://stackoverflow.com/a/51029069 – Thanhansoft Jun 09 '22 at 08:14

5 Answers5

108

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

Ernest Elikem
  • 316
  • 2
  • 12
Adam
  • 25,960
  • 22
  • 158
  • 247
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
    }
}
patricus
  • 59,488
  • 15
  • 143
  • 145
  • 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'. – Sergej Fomin Aug 15 '17 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 Feb 08 '18 at 17:48
  • NVM I already found it: https://stackoverflow.com/questions/44238257/laravel-5-4-updateorcreate-method-check-if-record-was-updated-or-created/44238805 – Adam Feb 08 '18 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 Feb 08 '18 at 18:22
  • There is now a function called `wasChanged()` which is doing this job. – Adam Mar 18 '18 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.

Jera
  • 183
  • 1
  • 5
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

Sergej Fomin
  • 1,822
  • 3
  • 24
  • 42
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

            }

        }
Naveed Ali
  • 361
  • 3
  • 12