【问题标题】:How to delete multiple records using Laravel Eloquent如何使用 Laravel Eloquent 删除多条记录
【发布时间】:2016-05-04 22:03:46
【问题描述】:

现在,据我所知,这应该很简单。

我希望能够从数据库中删除多条记录。我有我想删除的所有记录的id。我使用逗号分隔的 id 列表调用 resource.destroy 路由(id 是 postgres 类型 uuid),如下所示:

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

另一方面,我的控制器操作如下所示:

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

这给了我以下错误:

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')

我已验证 find() 正在返回与指定 ID 匹配的 products 集合。

我错过了什么?

PS: 1.模型Product与其他模型有几个belongsTo关系。 2. product.destroy 代码如果我传递一个 id 就可以正常工作

编辑 我想,我也想了解两者之间的区别:

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

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

是吗?据我所知,findget 都在返回 Collections

【问题讨论】:

  • the documentation here 有什么不清楚的地方吗?具体是destroy() 方法?
  • 我用这个thread 作为参考。我看过你提到的文档。使用产品 ID 调用 Model::destroy 时我感到有点紧张,因为恶意用户可能会删除属于其他 orgs 的产品(orgs 有很多 products)。我宁愿先找到记录(基于用户所属的organization)然后删除它们。我可以使用 for 循环(n 查询)。我还可以使用带有in 子句的delete 查询。只是想知道是否有更方便/优雅的东西。
  • @maytham-ɯɐɥıλɐɯ 我还没解决。有关我想到的解决方法,请参阅上面的评论。
  • 问题是,就像下面提到的那样,您在集合上调用 delete() 而不是实际对象本身。使用您的示例,您可以执行以下操作:$org->products()->find($ids)->each(function($product){ $product->delete(); });

标签: php laravel laravel-5 eloquent laravel-5.1


【解决方案1】:

问题是您在一个没有该方法的集合上调用delete()

这里有几个选项。

模型事件

如果您有 deleting/deleted 模型事件的事件侦听器,则需要确保删除以加载每个模型然后删除的方式发生。

在这种情况下,您可以在接受 id 列表的模型上使用 destroy 方法。它将为每个 id 加载一个新模型,然后在其上调用 delete()。正如您在评论中提到的,它不会将删除仅限于组织中的那些产品,因此您需要在将列表传递给 destroy() 方法之前过滤掉这些 id。

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(...) {
    }
}

如果您不是特别喜欢这种方法,则需要迭代您的组织产品集合并单独调用delete()。您可以使用标准的foreach,也可以在集合上使用each 方法:

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

无模型事件

现在,如果您没有任何需要监听的模型事件,事情就会变得容易一些。在这种情况下,您只需在查询构建器上调用delete(),它将直接删除记录而不加载任何模型对象。因此,您可以获得更简洁的代码和更好的性能:

public function destroy($id)
{
    try {
        $ids = explode(",", $id);
        // call delete on the query builder (no get())
        $org->products()->whereIn('id', $ids)->delete();
    }
    catch(...) {
    }
}

【讨论】:

  • 另外,如果只有一个id,使用where
【解决方案2】:

如果您创建产品模型,它将帮助您进行这些类型的操作。

例如:

型号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'];


}

控制器Products.php

您可以使用destroy 方法并将一个或多个主键作为参数传递给它。

<?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(...) {
            }
        }

    }

您还可以使用此选项通过自定义参数删除查询结果

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

你可以查看 Laravel 文档https://laravel.com/docs/8.x/eloquent#soft-deleting

【讨论】:

    【解决方案3】:

    当你使用 find 方法时,它只会找到一个 ID。您应该使用 whereIn 来匹配多个 id

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

    这样您将找到所有具有给定 ID 的产品并将其全部删除。

    【讨论】:

    • 其实find()支持id数组。这没有记录,但您可以查看源代码github.com/laravel/framework/blob/5.1/src/Illuminate/Database/…
    • 是的,你是对的。它做同样的事情...... whereIn。
    • find() 方法有时不支持多个 id,您可以使用 findMany($id's)。还有一件事,findMany() 和 find() 方法没有 delete() 方法。
    • 你不能同时使用 get() 和 delete()。
    【解决方案4】:

    我也遇到过这个问题。让$orgs 包含一些记录作为集合。现在您可以使用这样的循环轻松删除这些记录-

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

    【讨论】:

    • 这是个坏主意,因为每个条目都会向数据库发送一个新查询。
    猜你喜欢
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 2018-10-14
    • 2021-06-13
    相关资源
    最近更新 更多