2016-01-27 2 views
3

Теперь, из того, что я вижу, должно было быть просто.Как удалить несколько записей с помощью Laravel Eloquent

Я хочу, чтобы иметь возможность удалять несколько записей из базы данных. У меня есть 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() возвращается коллекция products, соответствующих указанным идентификаторам.

Что мне не хватает?

PS: 1. У модели Product есть несколько belongsTo отношений с другими моделями. 2. product.destroy код работает отлично, если я передать ему один id

EDIT Я думаю, я также пытаюсь понять, в чем разница между:

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

и

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

есть? Из того, что я вижу, возвращаются find и getCollections

+0

Было ли что-то неясно [документации здесь] (https://laravel.com/docs/5.1/eloquent#deleting-models)? В частности, метод 'destroy()'? – maiorano84

+0

Я использовал эту [нить] (https://laracasts.com/discuss/channels/laravel/delete-multiple-records) в качестве ссылки. Я видел документацию, на которую вы ссылаетесь. Я немного нервничаю, вызывающ «Model :: destroy» с идентификаторами продукта, потому что злоумышленник может удалять продукты, принадлежащие другим «orgs» («orgs' имеет много« продуктов »). Я бы предпочел сначала найти записи (основанные на 'organization', принадлежащие пользователю), а затем удалить их. Я мог бы использовать цикл for ('n' query). Я также мог бы использовать запрос 'delete' с предложением' in'. Просто интересно, есть ли что-то более удобное/элегантное. –

+0

@ maytham-ɯɐɥıλɐɯ Я еще не решил. См. Мой комментарий выше для обходных решений, которые я имею в виду. –

ответ

9

Проблема в том, что вы вызываете delete() в сборнике, у которого нет этого метода.

У вас есть пара вариантов здесь.

Модель События

Если у вас есть обработчики событий для модели событий deleting/deleted, вам нужно будет убедиться, что удаление происходит таким образом, что загружается каждая модель, а затем удаляется.

В этом случае вы можете использовать метод destroy для модели, которая принимает список идентификаторов. Он будет загружать новую модель для каждого идентификатора, а затем называть delete(). Как вы отметили в комментарии, он не будет ограничивать удаление только теми продуктами в организации, поэтому вам нужно будет отфильтровать эти идентификаторы перед передачей списка в метод destroy().

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

Каждое из ваших решений, преподаваемых, должно быть чем-то. Спасибо за ясный ответ, я придерживаюсь третьего варианта, так как у меня нет модельных событий. –

+2

Спасибо за это решение. Я также узнал здесь :) –

+2

Thump up, полезный ответ. –

-2

Когда вы используете метод find, он найдет только один идентификатор. Вы должны использовать, где, чтобы соответствовать несколько идентификаторов

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

Таким образом, вы найдете все продукты с заданными идентификаторами и удалить их все.

+0

На самом деле' find() 'поддерживает массив идентификаторов. Это не документировано, но вы можете взглянуть на источник https://github.com/laravel/framework/blob/5.1/src/Illuminate/Database/Eloquent/Builder.php#L79 –

+0

Да, вы правы. И он делает то же самое ... a whereIn. –

+0

Метод find() иногда не поддерживает несколько идентификаторов, вместо этого вы можете использовать findMany ($ id). И еще одно: findMany(), а также метод find() не имеет метода delete(). –

0

Я также столкнулся с этой проблемой. Пусть $orgs содержит некоторые записи в виде коллекции. Теперь вы можете легко удалить эти записи с помощью цикла, как this-

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

Это плохая идея, потому что для каждой записи новый запрос отправляется в базу данных. – Marco

Смежные вопросы