48

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()) ?
    }

}
3
  • 9
    You mean the isDirty method? There's also wasChanged.
    – tadman
    Commented Feb 14, 2018 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
    Commented Feb 14, 2018 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
    Commented Feb 14, 2018 at 17:48

4 Answers 4

90

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

3
  • 1
    Unnecessary to transfer the model from the database again, see my answer for an updated version Commented Jan 22, 2019 at 11:54
  • relevant docs
    – Stetzon
    Commented Sep 7, 2022 at 19:51
  • Shouldn't the function now be called updated?
    – Riza Khan
    Commented Feb 17, 2023 at 21:34
22

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'); 
  }
}
3
  • I upvoted your comment on the accepted answer but downvoted this since it's now a duplicate
    – aowie1
    Commented Feb 28, 2019 at 19:59
  • 2
    @aowie1 That answer was updated after I commented on it :’( Commented Feb 28, 2019 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
    Commented Apr 5, 2019 at 7:24
3

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.
2
  • 2
    There is a shortcut for the same check whether a field has been updated: laravel.com/docs/8.x/eloquent#examining-attribute-changes So you could instead use if($user->wasChanged('email')) ^^
    – D. Petrov
    Commented Jul 22, 2021 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. Commented Jul 23, 2021 at 3:25
3

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.

1
  • Nice idea and well made, this should be included in Laravel by default!
    – mrvnklm
    Commented Jan 4, 2023 at 13:00

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