2

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.

2
  • So you want to create a new array $array which contains only the records which were updated? Commented Aug 16, 2017 at 6:19
  • @ TheFallen yes:) exactly! Commented Aug 16, 2017 at 8:06

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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?:)
where can I read more about 'app()->singleton' and '\stdClass'?:)
@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.
@SergejFomin, this is the app container binding 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.
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;
}

2 Comments

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...:(
@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.
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);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.