Когда я удаляю строку, используя этот синтаксис:
$user->delete();
Есть ли способ привязать обратный вызов сортировок, чтобы он, например, сделайте это автоматически:
$this->photo()->delete();
Предпочтительно внутри класса модели.
Когда я удаляю строку, используя этот синтаксис:
$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();
});
}
Я даже предполагаю переместить их в отдельные классы вместо закрытия для лучшей структуры приложения.
Есть 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');
В моем случае это было довольно просто, потому что мои таблицы базы данных - InnoDB с внешними ключами с Cascade on Delete.
Итак, в этом случае, если ваша таблица фотографий содержит ссылку на внешний ключ для пользователя, чем все, что вам нужно сделать, это удалить гостиницу, и очистка будет осуществляться базой данных, база данных удалит все записи фотографий из базы данных.
Я бы перебрал всю коллекцию, отключив все перед удалением самого объекта.
вот пример:
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();
Для этого лучше переопределить метод delete
. Таким образом, вы можете включить транзакции БД в сам метод delete
. Если вы используете способ события, вам придется покрывать вызов метода delete
транзакцией БД каждый раз, когда вы ее вызываете.
В вашей модели User
.
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
Чтобы уточнить выбранный ответ, если ваши отношения также имеют дочерние отношения, которые необходимо удалить, вы должны сначала извлечь все записи дочерних отношений, а затем вызвать метод 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();
});
}
}
Вы можете использовать этот метод в качестве альтернативы.
Что произойдет, так это то, что мы возьмем все таблицы, связанные с таблицей пользователей, и удалим связанные данные, используя цикл
$tables = DB::select("
SELECT
TABLE_NAME,
COLUMN_NAME,
CONSTRAINT_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME = 'users'
");
foreach($tables as $table){
$table_name = $table->TABLE_NAME;
$column_name = $table->COLUMN_NAME;
DB::delete("delete from $table_name where $column_name = ?", [$id]);
}
Или вы можете сделать это, если хотите, еще один вариант:
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();
да, но, как @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 даже не загружается)