Когда я удаляю строку, используя этот синтаксис:
$user->delete();
Есть ли способ привязать обратный вызов сортировок, чтобы он, например, сделайте это автоматически:
$this->photo()->delete();
Предпочтительно внутри класса модели.
Я считаю, что это идеальный вариант использования событий Eloquent (http://laravel.com/docs/eloquent#model-events). Вы можете использовать событие "delete" для очистки:
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 an uglier alternative, but faster
// Photo::where("user_id", $this->id)->delete()
// delete the user
return parent::delete();
}
}
Надеюсь, что это поможет.
Отношение в модели пользователя:
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();
});
}
Я даже предполагаю переместить их в отдельные классы вместо закрытия для лучшей структуры приложения.
Eloquent::observe()
доступен в 5.2 и может использоваться из AppServiceProvider.
photos()
, вам также нужно быть осторожным - этот процесс не удалит внуков, потому что вы не загружаете модели. Вам нужно будет зациклить photos
(обратите внимание, не photos()
) и запустить метод delete()
для них как моделей, чтобы инициировать события, связанные с удалением.
Есть 3 подхода к решению этой проблемы:
1. Использование красноречивых событий при загрузке модели (ссылка: 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. Использование Eloquent Event Observers (ссылка: https://laravel.com/docs/5.7/eloquent#observers)
В вашем AppServiceProvider зарегистрируйте наблюдателя следующим образом:
public function boot()
{
User::observe(UserObserver::class);
}
Затем добавьте класс Observer следующим образом:
class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
3. Использование ограничений внешнего ключа (ссылка: https://laravel.com/docs/5.7/migrations#foreign-key-constraints)
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Я бы перебрал всю коллекцию, отключив все перед удалением самого объекта.
вот пример:
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();
В моем случае это было довольно просто, потому что мои таблицы базы данных - InnoDB с внешними ключами с Cascade on Delete.
Итак, в этом случае, если ваша таблица фотографий содержит ссылку на внешний ключ для пользователя, чем все, что вам нужно сделать, это удалить гостиницу, и очистка будет осуществляться базой данных, база данных удалит все записи фотографий из базы данных.
Чтобы уточнить выбранный ответ, если ваши отношения также имеют дочерние отношения, которые необходимо удалить, вы должны сначала извлечь все записи дочерних отношений, а затем вызвать метод delete()
чтобы их события удаления также запускались правильно.
Вы можете сделать это легко с сообщениями более высокого порядка.
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* @return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get()->each->delete();
});
}
}
Вы также можете повысить производительность, запросив только столбец ID отношений:
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* @return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get(['id'])->each->delete();
});
}
}
да, но, как @supersan указал выше в комментарии, если вы удалите() в QueryBuilder, событие модели не будет запущено, потому что мы не загружаем саму модель, а затем вызываем delete() для этой модели.
События запускаются, только если мы используем функцию удаления в экземпляре модели.
Итак, это существо сказало:
if user->hasMany(post)
and if post->hasMany(tags)
чтобы удалить теги $user->posts
при удалении пользователя, нам нужно будет выполнить итерацию по $user->posts
и вызову $post->delete()
foreach($user->posts as $post) { $post->delete(); }
foreach($user->posts as $post) { $post->delete(); }
→ это вызовет событие удаления в посте
В.С.
$user->posts()->delete()
→ это не вызовет событие удаления сообщения, потому что мы на самом деле не загружаем модель сообщения (мы запускаем только SQL- DELETE * from posts where user_id = $user->id
: DELETE * from posts where user_id = $user->id
и, таким образом, модель Post даже не загружается)
Или вы можете сделать это, если хотите, еще один вариант:
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);
}
Обратите внимание, что если вы не используете соединение по умолчанию larvel db, вам необходимо сделать следующее:
DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();
first()
в запрос, чтобы я мог получить доступ к событию модели, например,User::where('id', '=', $id)->first()->delete();
Источник