web-dev-qa-db-ja.com

Laravel Eloquentを使用して複数のレコードを削除する方法

今、これは、私が見ることができるものから、単純なはずでした。

データベースから複数のレコードを削除できるようにしたい。削除するすべてのレコードの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. idを1つ渡した場合、_product.destroy_コードは正常に機能します

[〜#〜] edit [〜#〜]おそらく、私は次の違いを理解しようとしています:

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

そして

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

ですか?私が見るところから、findgetの両方がCollectionsを返しています

11
Code Poet

問題は、そのメソッドを持たないコレクションで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(...) {
    }
}
_
16
patricus

私もこの問題に直面しました。 $orgsには、いくつかのレコードがコレクションとして含まれています。これで、次のようなループを使用してこれらのレコードを簡単に削除できます-

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

Findメソッドを使用すると、単一のIDのみが検索されます。複数のIDに一致させるにはwhereInを使用する必要があります

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

このようにして、指定されたIDを持つすべての製品を見つけ、それらをすべて削除します。

0
Andrés Smerkin