45

I am using an Observer to watch if a user was updated.

Whenever a user is updated I would like to check if his email has been changed.

Is something like this possible?

class UserObserver
{


    /**
     * Listen to the User created event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function updating(User $user)
    {
      // if($user->hasChangedEmailInThisUpdate()) ?
    }

}
Adam
  • 25,960
  • 22
  • 158
  • 247
  • 9
    You mean the [`isDirty`](https://laravel.com/api/master/Illuminate/Database/Eloquent/Model.html#method_isDirty) method? There's also `wasChanged`. – tadman Feb 14 '18 at 17:45
  • @tadman yes! Thats what I was looking for. And is that compatible with the observer? I think updating is called after assigning changes to `user` but before storing it, right? – Adam Feb 14 '18 at 17:47
  • I'm not 100% sure of the details so you may need to experiment here. Dirty tracking is a common feature of many ORMs, Eloquent included. – tadman Feb 14 '18 at 17:48

4 Answers4

83

Edit: Credits to https://stackoverflow.com/a/54307753/2311074 for getOriginal

As tadman already said in the comments, the method isDirty does the trick:

class UserObserver
{


    /**
     * Listen to the User updating event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function updating(User $user)
    {
      if($user->isDirty('email')){
        // email has changed
        $new_email = $user->email; 
        $old_email = $user->getOriginal('email');
      }
    }

}

If you want to know the difference between isDirty and wasChanged, see https://stackoverflow.com/a/49350664/2311074

Davide Casiraghi
  • 15,591
  • 9
  • 34
  • 56
Adam
  • 25,960
  • 22
  • 158
  • 247
20

You don't have to get the user again from the database. The following should work:

public function updating(User $user)
{
  if($user->isDirty('email')){
    // email has changed
    $new_email = $user->email; 
    $old_email = $user->getOriginal('email'); 
  }
}
user1415066
  • 855
  • 7
  • 11
  • I upvoted your comment on the accepted answer but downvoted this since it's now a duplicate – aowie1 Feb 28 '19 at 19:59
  • 2
    @aowie1 That answer was updated after I commented on it :’( – user1415066 Feb 28 '19 at 20:01
  • 1
    I upvoted your answer because I don't think it deserves downvotes. However, I think its common practice on SO to edit minor improvements to an answer from comments. I would guess using `getOriginal` instead of `find` is just a minor improvement, because both work, and the main part about the answer is how to find if a model was changed: And that is the `isDirty` method. Maybe in this case it would be better if you had made an request by editing my question and changed the line. That would also give you credits. – Adam Apr 05 '19 at 07:24
2

Instead of using the updating event, You should use the updated model event to compare the column changes against the original. Please have a look at the below code.

AppServiceProvider.php

<?php

namespace App\Providers;

use App\Models\User;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        User::updated(function($user){
            $changes = array_diff($user->getOriginal(), $user->getAttributes());
            if(array_key_exists('email',$changes)){
                /* do your things. */
            }
        });
    }
}

Explanation:

  • Once you get the difference then you need to use array_key_exists to check whether the email field got updated or not.
Soubhagya Kumar Barik
  • 1,979
  • 20
  • 26
  • 2
    There is a shortcut for the same check whether a field has been updated: https://laravel.com/docs/8.x/eloquent#examining-attribute-changes So you could instead use `if($user->wasChanged('email'))` ^^ – D. Petrov Jul 22 '21 at 17:00
  • @D.Petrov correct but if you have to capture multiple fields then you need to write for each column right so this is the efficient way to handle multiple column data. – Soubhagya Kumar Barik Jul 23 '21 at 03:25
2

A little late to the party, but might be helpful for future devs seeking a similar solution.

I recommend using the package Laravel Attribute Observer, as an alternative to polluting your Service Providers or filling your Observers with isDirty() and wasChanged() boilerplate.

So your use case would look like this:

class UserObserver
{


    /**
     * Handle changes to the "email" field of User on "updating" events.
     *
     * @param  \App\User  $user
     * @param string $newValue The current value of the field
     * @param string $oldValue The previous value of the field
     * @return void
     */
    public function onEmailUpdating(User $user, string $newValue, string $oldValue)
    {
      // Your logic goes here...
    }

}

Laravel Attribute Observer is especially useful when you have a lot of attributes to observe on one or more models.

Disclaimer: I am the author of Laravel Attribute Observer. If it saves you some development time, consider buying me a coffee.