How to delete multiple records using Laravel Eloquent
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.
Code Poet
Updated on July 28, 2022Comments
-
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 theresource.destroy
route using comma separated list of ids (id
is 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 that
find()
is returning a collection ofproducts
matching the specified ids.What am I missing?
PS: 1. The model
Product
has severalbelongsTo
relationships with other models. 2. Theproduct.destroy
code works fine if I pass it a singleid
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
andget
are returningCollections