Я думаю, что проблема в сводной таблице. В сводной таблице есть два внешних ключа. Ваша таблица ничего не сделает, кроме как выдаст ошибку при удалении связанной записи, поскольку вы не дали ей «ссылочное действие», чтобы что-то сделать, когда это произойдет.
Добавляя onDelete()
, вы можете указать эти ссылочные действия. В зависимости от того, что вы хотите, вы можете отдавать такие заказы, как: cascade
, set null
, restrict
, no action
и set default
. Подробнее об этом здесь .
В вашем случае вы хотите использовать cascade
, что в этом синтаксисе в основном означает: «удалить запись, если удален внешний ключ».
Ваша миграция будет выглядеть так:
public function up()
{
Schema::create('ingredient_images', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('ingredient_id');
$table->foreign('ingredient_id')->references('id')->on('ingredients')->onDelete('cascade');
$table->unsignedInteger('image_id');
$table->foreign('image_id')->references('id')->on('images')->onDelete('cascade');
$table->timestamps();
});
}
Первое обновление после комментария.
Поскольку $ingredient->images()->delete()
будет использовать Eloquent Builder для удаления нескольких записей одновременно, Image@delete
никогда не будет вызываться.
Простое решение может быть:
// Ingredient
public function delete() {
foreach ($this->images as $image) {
$image->delete();
}
return parent::delete();
}
Это, конечно, приведет к отдельным запросам на удаление каждого изображения. В зависимости от того, хотите ли вы использовать сверхмегаперспективную скорость, это не будет вашим выбором.
Рекомендуется использовать наблюдатель (для поддержания чистоты ваших моделей) и событие в сочетании с ожидающим (необязательным) слушателем .
Наблюдатель:
class IngredientObserver
{
public function deleting(Ingredient $ingredient) {
// Loop here
foreach ($ingredient->images as $image) {
Storage::disk('s3')->delete($image->path); // Or this can also be done in a seperate observer for Image to ensure the image is always deleted on AWS when deleting an image, that would be my choice.
$image->delete();
}
// Or use an event
$paths = $ingredient->images()->lists('path');
$ingredient->images()->delete();
event(new RemoveAwsImages($paths));
}
}
Event:
class RemoveAwsImages
{
public $paths;
public __construct($paths) {
$this->paths = $paths;
}
}
Слушатель:
use Illuminate\Contracts\Queue\ShouldQueue;
class RemoveAwsImagesListener implements ShouldQueue // Remember ShouldQueue is optional
{
public function handle(RemoveAwsImages $event)
{
foreach ($event->paths as $paths) {
Storage::disk('s3')->delete($path);
}
}
}
Таким образом, вам не нужно добавлять методы удаления внутри вашей модели, и в сочетании с onDelete('cascade')
вам не нужно их отсоединять.
Я не проверял этот код, поэтому могут быть небольшие ошибки.