I am facing an error that I would like to see explained. I have an UserObserver and every time a user gets updated and the active
field updates to true a new password is generated and a welcome email is sent.
The function looks like this.
public function updated (User $user)
{
if ($user->active && $user->isDirty('active')) {
$password = generatePassword();
$user->password = bcrypt($password);
$user->save();
$user->notify(
new UserWelcomeNotification(
$user->email,
$password,
new MailResource(Email::getMailInfo(23))
)
);
}
}
As you can see in the if statement there is a check to see if the user is active and if the database field has been changed (isDirty()
). If this is true a new password is being generated, hashed with bcrypt and then send to the user via notifications. (mail)
As expected the password update triggers the method again, but now the isDirty('active)
should return false. This does not happen, it returns true through all the iterations. After the PHP max execution time has been reached I get the following error:
[Fri Jan 11 09:13:13 2019] PHP Fatal error: Maximum execution time of 60 seconds exceeded in app/src/vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php
After enabling xdebug the following exception is thrown. (as expected)
PHP Error: Maximum function nesting level of '256' reached, aborting! in /home/ilyas/script/clockwork/app/src/vendor/laravel/framework/src/Illuminate/Support/Collection.php on line 1971
From this issue, which can be solved easily, I have 2 questions.
Why does bcrypt thow an error after the max execution time has been reached?
Why does $user->isDirty('active')
return true throughout after every update in this loop although the last update in the observer did not update the active
field?
As asked by Mozammil $user->getDirty()
returns this the first time the updated method is triggered.
array(2) {
'active' =>
bool(true)
'updated_at' =>
string(19) "2019-01-11 11:27:13"
}
From the second time it returns until timeout is reached:
array(3) {
'password' =>
string(60) "$2y$10$rlAbpelKnT/yp5zFhXcjwelEKkDEx5SfNJWqL1LiDltRnHYBLINmK"
'active' =>
bool(true)
'updated_at' =>
string(19) "2019-01-11 11:27:13"
}
Thanks to Dries Vints and Jonas Staudenmeir for providing an answer on GitHub.
From DriesVints:
Well, think about it. The "updated" event happens after your model was updated. So any changes you made on your model are bound to still be picked up by the isDirty call. The fact that $user->active returns true is indeed because it was changed to true from the original update. The original changes aren't cleared or anything. Since you are constantly referencing the same object being passed to the updated method this is the expected behaviour.
From Jonas Staudenmeir
This is happening because the updated event is fired before syncOriginal() is called.
https://github.com/laravel/framework/issues/27138
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With