How to delete multiple records using Laravel Eloquent

71,502

Solution 1

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

You have a couple options here.

Model Events

If you have event listeners for the deleting/deleted model 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 the destroy method on the model that takes a list of ids. It will load a new model for each id, and then call delete() 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 the destroy() 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 call delete() on them individually. You can use a standard foreach, or you can use the each method 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 call delete() 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(...) {
    }
}

Solution 2

If you create a model of your products, it will help you with these types of operations.

For example:

the model Products.php

<?php

namespace App\Http\Models;

use Illuminate\Database\Eloquent\Model;

class Products extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'products';
    protected $primaryKey = 'id';

    protected $fillable = ['name', 'price', 'description'];


}

The controller Products.php

You can use the destroy method and pass one or more primary keys to it as arguments.

<?php

    namespace App\Http\Controllers;

    use App\Http\Models\Products;

    class Products 
    {
        
        public function destroy($id)
        {
            try {
                
                $ids = explode(",", $id);
                //$ids is a Array with the primary keys
                Products::destroy($ids);
            }
            catch(...) {
            }
        }

    }

You can also use this option to remove query results with a custom parameter

$deletedRows = Products::where('name', 'phones')->delete();

You can check the Laravel documentation https://laravel.com/docs/8.x/eloquent#soft-deleting

Solution 3

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.

Share:
71,502
Code Poet
Author by

Code Poet

Updated on July 28, 2022

Comments

  • Code Poet
    Code Poet over 1 year

    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 the id's of all the records I wish to delete. I call the resource.destroy route using comma separated list of ids (id is of postgres type uuid), 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 that find() is returning a collection of products matching the specified ids.

    What am I missing?

    PS: 1. The model Product has several belongsTo relationships with other models. 2. The product.destroy code works fine if I pass it a single id

    EDIT I 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, both find and get are returning Collections