В Ларавеле вложенные (каскадные) отношения моделей — обычное дело. У автора есть записи, у записей есть комментарии, и так далее. Не менее обычное дело — необходимость удалять дочерние модели при удалении родителя. Самое первое, что приходит на ум — каскадное удаление моделей, которое включается при определении foreign-ключей и будет выполняться автоматически базой данных. Но у такого способа есть достаточно весомый недостаток: события deleting
и deleted
для удаляемых моделей не произойдут и их обработчики, соответственно, не будут вызваны. Ну, потому что удаление в БД происходит.
Поэтому, если существование обработчиков событий предполагается, лучше навесить удаление дочерних моделей на событие deleting
модели родительской. Вот есть допустим модель Company
и у нее множество моделей Contract
, определенное вот так:
public function contracts() {
return $this->hasMany('App\Contract');
}
Реализуем обработчик события deleting
:
protected static function boot() {
parent::boot();
static::deleting(function (Company $company) {
// здесь мы можем удалять контракты
});
}
Но тут есть еще одна маленькая хитрость. Если мы напишем $company->contracts()->delete()
, то контракты удалятся, но их события deleting
и deleted
тоже не произойдут. Дело в том, что contracts()
возвращает нам не что иное, как Query Builder, и когда мы вызовем delete()
— это будет обычный delete-запрос к БД. На эту тему даже иссуй есть.
Вызвать $company->contracts->delete()
мы тоже не можем, потому что $company->contracts
— это просто коллекция, нет такого метода у коллекции. Чтобы события сработали (например, чтобы удалить дочерние модели контрактов), нужно загружать каждую модель и вызывать delete()
на ней. Примерно так:
static::deleting(function (Company $company) {
$company->contracts->each(function (Contract $contract) {
$contract->delete();
});
});
Также подойдет метод Contract::destroy()
, в который нужно передать массив с айдишниками контрактов — он сделает то же самое, что и приведенный выше код.