Автоматическое удаление связанных строк в Laravel (Eloquent ORM)
когда я удаляю строку, используя этот синтаксис:
$user->delete();
есть ли способ прикрепить обратный вызов, чтобы он, например, делал это автоматически:
$this->photo()->delete();
предпочтительно внутри модели-класса.
10 ответов:
Я считаю, что это идеальный прецедент для красноречивых событий (http://laravel.com/docs/eloquent#model-events). Вы можете использовать событие "удаление" для выполнения очистки:
class User extends Eloquent { public function photos() { return $this->has_many('Photo'); } // this is a recommended way to declare event handlers public static function boot() { parent::boot(); static::deleting(function($user) { // before delete() method call this $user->photos()->delete(); // do the rest of the cleanup... }); } }
вероятно, вы также должны поместить все это в транзакцию, чтобы обеспечить ссылочную целостность..
вы можете настроить это в своих миграциях:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Источник:http://laravel.com/docs/5.1/migrations#foreign-key-constraints
вы также можете указать желаемое действие для "on delete" и " on обновить" свойства ограничения:
$table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade');
Примечание: этот ответ был написан для Laravel 3. Таким образом, может или не может хорошо работать в более поздней версии Laravel.
вы можете удалить все связанные фотографии перед фактическим удалением пользователя.
<?php class User extends Eloquent { public function photos() { return $this->has_many('Photo'); } public function delete() { // delete all related photos $this->photos()->delete(); // as suggested by Dirk in comment, // it's an uglier alternative, but faster // Photo::where("user_id", $this->id)->delete() // delete the user return parent::delete(); } }
надеюсь, что это помогает.
взаимосвязь в модели User:
public function photos() { return $this->hasMany('Photo'); }
удалить запись и связанные с:
$user = User::find($id); // delete related $user->photos()->delete(); $user->delete();
по состоянию на Laravel 5.2,документации что эти типы обработчиков событий должны быть зарегистрированы в AppServiceProvider:
<?php class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { User::deleting(function ($user) { $user->photos()->delete(); }); }
Я даже предполагаю переместить их в отдельные классы вместо замыканий для лучшей структуры приложения.
в моем случае это было довольно просто, потому что мои таблицы базы данных InnoDB с внешними ключами с каскадом на удаление.
поэтому в этом случае, если ваша таблица фотографий содержит ссылку на внешний ключ для пользователя, чем все, что вам нужно сделать, это удалить отель, и очистка будет выполнена базой данных, база данных удалит все записи фотографий из базы данных.
Я бы перебрал коллекцию, отсоединяя все, прежде чем удалять сам объект.
вот пример:
try { $user = user::findOrFail($id); if ($user->has('photos')) { foreach ($user->photos as $photo) { $user->photos()->detach($photo); } } $user->delete(); return 'User deleted'; } catch (Exception $e) { dd($e); }
Я знаю, что это не автоматически, но это очень просто.
другим простым подходом было бы предоставить модели метод. Вот так:
public function detach(){ try { if ($this->has('photos')) { foreach ($this->photos as $photo) { $this->photos()->detach($photo); } } } catch (Exception $e) { dd($e); } }
тогда вы можете просто назвать это, где вам нужно:
$user->detach(); $user->delete();
есть 3 подхода к решению этой проблемы:
1. Использование Красноречивых Событий На Модели Boot (ref:https://laravel.com/docs/5.7/eloquent#events)
class User extends Eloquent { public static function boot() { parent::boot(); static::deleting(function($user) { $user->photos()->delete(); }); } }
2. Использование Красноречивых Наблюдателей Событий (ref:https://laravel.com/docs/5.7/eloquent#observers)
в вашем AppServiceProvider зарегистрируйте наблюдателя следующим образом:
public function boot() { User::observe(UserObserver::class); }
затем добавьте класс наблюдателя, например Итак:
class UserObserver { public function deleting(User $user) { $user->photos()->delete(); } }
3. Использование Ограничений Внешнего Ключа (ref:https://laravel.com/docs/5.7/migrations#foreign-key-constraints)
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
или вы можете сделать это, если хотите, просто еще один вариант:
try { DB::connection()->pdo->beginTransaction(); $photos = Photo::where('user_id', '=', $user_id)->delete(); // Delete all photos for user $user = Geofence::where('id', '=', $user_id)->delete(); // Delete users DB::connection()->pdo->commit(); }catch(\Laravel\Database\Exception $e) { DB::connection()->pdo->rollBack(); Log::exception($e); }
Примечание Если вы не используете соединение laravel db по умолчанию, то вам нужно сделать следующее:
DB::connection('connection_name')->pdo->beginTransaction(); DB::connection('connection_name')->pdo->commit(); DB::connection('connection_name')->pdo->rollBack();
да, но, как указано выше в комментарии @supersan, если вы удалите() на QueryBuilder, событие модели не будет запущено, потому что мы не загружаем саму модель, а затем вызываем delete() на этой модели.
события запускаются только в том случае, если мы используем функцию delete на экземпляре модели.
Итак, это существо сказало:
if user->hasMany(post) and if post->hasMany(tags)
для того, чтобы удалить теги сообщений при удалении пользователя, мы должны были бы перебирать
$user->posts
и звонок$post->delete()
foreach($user->posts as $post) { $post->delete(); }
-> это вызовет событие удаления на PostVS
$user->posts()->delete()
-> это не приведет к запуску события удаления на post, потому что мы фактически не загружаем модель Post (мы запускаем только SQL, например:DELETE * from posts where user_id = $user->id
и таким образом, модель Post даже не загружается)