欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

How to delete multiple records using Laravel Eloquent 删除集合里的对象


Now this, from what I can see, should have been simple.

I want to be able to delete multiple records from the database. I have theid's of all the records I wish to delete. I call theresource.destroyroute using comma separated list of ids (idis of postgres typeuuid), like so:

Request URL:http://foo.app/products/62100dd6-7ecf-4870-aa79-4b132e60c904,c4b369f1-d1ef-4aa2-b4df-b9bc300a4ff5
Request Method:DELETE

On the other end, my controller action looks like so:

public function destroy($id)
{
    try {
        $ids = explode(",", $id);
        $org->products()->find($ids)->delete();
    }
    catch(...) {
    }
}

This gives me the following error:

BadMethodCallException in Macroable.php line 81:
Method delete does not exist.

in Macroable.php line 81
at Collection->__call('delete', array()) in ProductsController.php line 251
at Collection->delete() in ProductsController.php line 251
at ProductsController->destroy('62100dd6-7ecf-4870-aa79-4b132e60c904,c4b369f1-d1ef-4aa2-b4df-b9bc300a4ff5')

I have verified thatfind()is returning a collection ofproductsmatching the specified ids.

What am I missing?

PS: 1. The modelProducthas severalbelongsTorelationships with other models.2. Theproduct.destroycode works fine if I pass it a singleid

EDITI guess, I'm also trying to understand what the difference between:

$org->products()->find($ids)->delete()

and

$org->products()->whereIn('id', $ids)->get()->delete()

is? From what I see, bothfindandgetare returningCollections

shareimprove this question
 
  
Was there something unclear aboutthe documentation here? Specifically thedestroy()method?maiorano84Jan 27 '16 at 19:25
  
I was using thisthreadas a reference. I've seen the documentation you refer to. I feel a little nervous callingModel::destroywith the product ids because a malicious user may delete products belonging to otherorgs(orgshas manyproducts). I would prefer to first find the records (based onorganizationa user belongs to) and then delete them. I could use a for loop (nqueries). I could also use adeletequery withinclause. Just wondering if there's something more convenient/elegant.Code PoetJan 27 '16 at 20:18
  
@maytham-ɯɐɥıλɐɯ I haven't solved it yet. See my comment above for workarounds I have in mind.Code PoetJan 27 '16 at 20:20
  
The issue is, like someone mentioned below, that you are calling delete() on a collection as opposed to the actual object itself. using your example you can do the following:$org->products()->find($ids)->each(function($product){ $product->delete(); });edc598Jan 27 '16 at 21:40

3 Answers 正确答案

The issue is that you're callingdelete()on a Collection, which does not have that method.

You have a couple options here.

Model Events

If you have event listeners for thedeleting/deletedmodel events, you will need to make sure the deletion happens in a way that each model is loaded and then deleted.

In this case, you can use thedestroymethod on the model that takes a list of ids. It will load a new model for each id, and then calldelete()on it. As you mention in a comment, it won't restrict the deletion to only those products in the organization, so you would need to filter out those ids before passing the list into thedestroy()method.

public function destroy($id)
{
    try {
        $ids = explode(",", $id);
        // intersect the product ids for the org with those passed in
        $orgIds = array_intersect($org->products()->lists('id'), $ids);
        // now this will only destroy ids associated with the org
        \App\Product::destroy($orgIds);
    }
    catch(...) {
    }
}

If you don't particularly like that approach, you will need to iterate your collection of organization products and calldelete()on them individually. You can use a standardforeach, or you can use theeachmethod on the collection:

public function destroy($id)
{
    try {
        $ids = explode(",", $id);
        $org->products()->find($ids)->each(function ($product, $key) {
            $product->delete();
        });
    }
    catch(...) {
    }
}

No Model Events

Now, if you don't have any model events that you need to listen for, things are a little easier. In this case, you can just calldelete()on the query builder, and it will go straight to deleting the records without loading any model objects. So, you get cleaner code with better performance:

public function destroy($id)
{
    try {
        $ids = explode(",", $id);
        // call delete on the query builder (no get())
        $org->products()->whereIn('id', $ids)->delete();
    }
    catch(...) {
    }
}
shareimprove this answer
 
2 
Each one of your solutions, taught be something. Thanks for you lucid answer, I'm sticking with the third option, as I have no model events.Code PoetJan 27 '16 at 21:55
2 
Thanks for this solution. I've also learned here :)Andrés SmerkinJan 28 '16 at 0:18
2 
  
in addition, if there is only one id, use whereConanMar 1 at 2:13

I also faced this problem. Let$orgscontains some records as a collection. Now you can easily delete these records using a loop like this-

foreach($orgs as $org) 
{
    $org->delete();
}
shareimprove this answer
 

When you use the find method, it will find only a single ID. You should use a whereIn to match multiple ids

public function destroy($id)
{
    try {
        $ids = explode(",", $id);
        $org->products()->whereIn('id', $ids)->get()->delete(); 
    }
    catch(...) {
    }
}

This way you will find all the products with the given IDs and delete them all.

shareimprove this answer
 
  
Actually,find()supports an array of ids. This is not documented, but you can take a look at the sourcegithub.com/laravel/framework/blob/5.1/src/Illuminate/Databas‌​e/…Code PoetJan 27 '16 at 20:09
  
Yes, you are right. And it does the same... a whereIn.Andrés SmerkinJan 27 '16 at 20:38
  
find() method sometimes doesn't support multiple id's, instead you can use findMany($id's). And one more thing, findMany() as well as find() method doesnt have delete() method.Rubanraj RavichandranMar 9 at 14:19
  
You couldn't use get() and delete() together.Rubanraj RavichandranMar 9 at 14:20

来自  https://stackoverflow.com/questions/35046082/how-to-delete-multiple-records-using-laravel-eloquent

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

普通分类: