framework: Eloquent Model delete/deleting events are not triggered on a where(...)->delete()
Scenario A
Analytic::where('id', 2)->delete();
- this deletes the entry in the database
- this does not trigger the deleting/deleted events on the model
Scenario B
Analytic::where('id', 2)->first()->delete();
- this deletes the entry in the database
- this does trigger the deleting/deleted events on the model
Is this expected behavior?
public static function boot() {
static::deleting(function($obj) {
});
static::deleted(function($obj) {
});
}
referring doc: http://four.laravel.com/docs/eloquent
About this issue
- Original URL
- State: closed
- Created 11 years ago
- Reactions: 21
- Comments: 26 (3 by maintainers)
Links to this issue
Commits related to this issue
- Allows routes to be specified as "http". Issue #2536 fixed an issue where URL::to() couldn't be used to create http URLs from an https page. This is a fix to a similar but different issue where URL... — committed to orrd/framework by deleted user 10 years ago
- Allows routes to be specified as 'http'. Issue #2536 fixed an issue where URL::to() couldn't be used to create http URLs from an https page. This is a fix to a similar but different issue where URL:... — committed to orrd/framework by deleted user 10 years ago
Just to be clear, I wouldn’t say you need a “workaround” since this isn’t a bug, it really does make sense when you understand what’s going on. The solution you need to use instead depends on what you’re trying to do.
First, let’s talk about their first example:
All this actually does is execute SQL, probably something like “DELETE FROM analytic WHERE id=2”. That’s it. It doesn’t load the models into memory, etc. For these kinds of functions, Laravel is just passing control to an underlying Query Builder query that performs the actions in SQL. That’s just something that Eloquent lets you do, it lets you perform Query actions directly on its underlying query object.
In order to trigger events when you delete something, the Eloquent model would have to be loaded into memory, and then delete() has to be called on each model individually. This is an entirely different kind of operation. The question gives you an example of how to do that for one object with the ID “2”:
In this case “first()” loads the model into memory, and then the delete() function is called on it. If you have more than one row/object that you want to delete at a time, you can do something like this (be careful with the where() statements, you don’t want to delete things you don’t want to!):
Here’s another way to do the same thing using Laravel’s each() function that it gives you for working on Collections…
Yes, this is basically expected. In order to fire the events we would have to pull the models first and then call the events with the model instances. Then call the delete query.
The Law of Leaky Abstractions
Btw in 2018 you can replace
Analytic::where('id', '>', 100)->get()->each(function($analytic) { $analytic->delete(); });
with
Analytic::where('id', '>', 100)->get()->each->delete();
@silverdr, yes that is the expected behavior. When you call
$model0->model1()->delete();
you are only executing SQL, so your delete events never get triggered. The way Laravel relations work,$model0->model1()
pretty much just gets you an Query Builder class, so you can use any of the Query Builder functions on it. For example$model0->model1()->where('foo', '=', 'bar')->get()
.That’s different than
$model0->model1
(without parenthesis on model1). That does something completely different. That fetches your model1 objects from the database and gives them to you in a Collection. That’s why when you call delete() on each one, your delete events get triggered.Understanding the difference between
$model0->model1()
and$model0->model1
is the key to understanding Laravel relations (personally I didn’t even notice the difference for the first year or so of using Laravel, but everything made a lot more sense once I understood that).+1 for solving this issue.
Details where I found this useful.
Why it isn’t working is logical, but not obvious. At least this should be documented in Query builder section.
I have something similar and still consider it somewhat confusing to say the least.
I have a typical event hook for soft deleting children:
and
Now when I use:
things work as expected and model2 children of model 1 get (soft) deleted. But when I use:
then all related model1 records get deleted but model2 and all its records remain untouched.
@chrisadipascual whenever you call the
delete
method of a model directly, not when calling thedelete
method of the query builder@NightwalkerYao Yes, first that will load all of the models into memory, and then call delete on each one (
deleting
anddeleted
events will fire), also doing theDELETE from table...
query on each one.For those who wonder how it works,
each
is one of the “proxies” defined in the Collection class. The__get
magic method in Collection class calls the HigherOrderCollectionProxy class which proxies the method call onto the collection items. This feature was added in Laravel 5.4.Thank you… It works… !! made my day… !! 😃