1

I have such code in the controller:

for($i=0; $i<$number_of_tourists; $i++) {

$tourist = Tourist::updateOrCreate(['doc_number' => $request['doc_number'][$i]],
$tourist_to_update);

}

so, the updateOrCreate method can 1) Update record, 2) Create a new one 3) Leave record untouched if $tourist_to_update equals to what's already in the DB.

I want to save $tourist_to_update into $array[] only when a record in 'tourist" table is being updated (not when a new record created or nothing changes).

As I know, there are Eloquent events https://laravel.com/docs/5.4/eloquent#events which have updating and updated events, which seem to match my needs.

The problem is I watched&read several tutorials and still don't understand how to use them.

I know how-to-register a Eloquent event in Tourist model:

protected static function boot()
{
    parent::boot();

    static::updating(function ($model) {

    });
}

Could anyone explain how can I achive my goal with Eloquent events?

Thank you in advance.

Sergej Fomin
  • 1,822
  • 3
  • 24
  • 42

3 Answers3

1

This is a bit more complex approach, but if you want to get only updated records which have their data changed this is working. In your controller method add:

app()->singleton('touristsCollector', function ($app) {
    $collector = new \stdClass;
    $collector->updated = [];

    return $collector;
});

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

$collector = resolve('touristsCollector');
var_dump( $collector->updated ); // Only updated tourists which have data changed

This will basically add to the app container a standard PHP class which will collect all the updated tourists from the model event. In your model add this:

protected static function boot()
{
    parent::boot();

    static::updated(function ($model) {
        $collector = resolve('touristsCollector');
        $collector->updated[] = $model;
    });
}

This will basically get the PHP class from the app container add the tourist model only if some properties of it have been updated.

thefallen
  • 9,496
  • 2
  • 34
  • 49
  • wow, that's what I was looking for - using Eloquent Models to get this thing done!:) You are really The MASTER!:) I have some questions though: I see no difference if i change 'static:updated' to 'static:updating'. So what's the difference?:) – Sergej Fomin Aug 16 '17 at 08:55
  • where can I read more about 'app()->singleton' and '\stdClass'?:) – Sergej Fomin Aug 16 '17 at 08:56
  • 1
    @SergejFomin, `updating` event will fire even though properties are the same, `updated` will fire only if there are some changes of the data, so you need the `updated` event. – thefallen Aug 16 '17 at 08:56
  • 1
    @SergejFomin, this is the app container binding https://laravel.com/docs/5.4/container#binding and `stdClass` is an anonymous PHP library standard class, you could use a plain array instead of this class. – thefallen Aug 16 '17 at 08:59
0

If the model has been created the property wasRecentlyCreated will be set to true, so this is how you can check if the model is new or updated:

$updated = [];

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

    if ( $tourist->wasRecentlyCreated ) {
        continue; // Go to next loop if this tourist was created now
    }

    $updated[] = $tourist;
}
thefallen
  • 9,496
  • 2
  • 34
  • 49
  • thank you, TheFallen. But I think there is a problem. 'updateOrCreate', besides updating and creating, has a third behaviour: 3) Leave record untouched if $tourist_to_update equals to what's already in the DB. I think that you code will add a tourist which is already in db and doesn't need to be updated or created, to the $updated[ ] array. I've just implemented your code. yes, it does add unchanged tourists to array $updated...:( – Sergej Fomin Aug 16 '17 at 08:40
  • 1
    @SergejFomin, ye, you are correct, to get only the updated ones you might compare the `updated_at` timestamp to the current time or check my other answer. – thefallen Aug 16 '17 at 08:42
0

If you always want to execute a specific code when a row in tourist table was updated, I would recommend to create an observer to listen for updated or create calls.

<?php

namespace App\Observers;

use App\Tourist;

class TouristObserver
{
    /**
     * Listen to the User created event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(Tourist $user)
    {
        // this will always trigger when you created a new entry
    }

    /**
     * Listen to the User deleting event.
     *
     * @param  \App\Tourist $user
     * @return void
     */
    public function updated(Tourist $user)
    {
        // this will always trigger when actually a row was updated
    }
}

All you have to do is to register the observer in AppServiceProvider like this:

public function boot()
{
    Tourist::observe(\App\Observer\TouristObserver::class);
}
Adam
  • 25,960
  • 22
  • 158
  • 247