Когда я удаляю строку, используя этот синтаксис:
$user->delete();
Есть ли способ прикрепить своего рода обратный вызов, чтобы он, например, делал это автоматически:
$this->photo()->delete();
Желательно внутри модельного класса.
Когда я удаляю строку, используя этот синтаксис:
$user->delete();
Есть ли способ прикрепить своего рода обратный вызов, чтобы он, например, делал это автоматически:
$this->photo()->delete();
Желательно внутри модельного класса.
Ответы:
Я считаю, что это идеальный вариант использования событий Eloquent ( 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...
});
}
}
Вы, вероятно, должны также поместить все это в транзакцию, чтобы обеспечить ссылочную целостность.
foreach($user->photos as $photo)
, $photo->delete()
чтобы убедиться, что у каждого ребенка были удалены его дети на всех уровнях, а не только на одном, как это происходило по какой-то причине.
Photos
имеет tags
и вы делаете то же самое в Photos
модели (т.е. наdeleting
методе:), $photo->tags()->delete();
он никогда не получает триггер. Но если я сделаю это for
циклом и сделаю что-то вроде for($user->photos as $photo) { $photo->delete(); }
этого, то tags
также будет удален! просто к сведению
Вы можете настроить это в своих миграциях:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Источник: http://laravel.com/docs/5.1/migrations#foreign-key-constraints
Вы также можете указать желаемое действие для свойств «при удалении» и «при обновлении» ограничения:
$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();
}
}
Надеюсь, поможет.
$this->photos()->delete()
. photos()
Возвращает объект конструктор запросов.
Отношение в модели пользователя:
public function photos()
{
return $this->hasMany('Photo');
}
Удалить запись и связанные:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->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');
Начиная с 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()
метод на них как на моделях, чтобы инициировать события, связанные с удалением.
Для этого лучше переопределить delete
метод. Таким образом, вы можете включить транзакции БД в сам delete
метод. Если вы используете способ события, вам придется delete
при каждом вызове покрывать вызов метода транзакцией БД.
В твоей User
модели.
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
В моем случае это было довольно просто, потому что мои таблицы базы данных - это InnoDB с внешними ключами с Cascade на 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();
Или вы можете сделать это, если хотите, просто другой вариант:
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();
Чтобы уточнить выбранный ответ, если ваши отношения также имеют дочерние отношения, которые необходимо удалить, вы должны сначала извлечь все записи дочерних отношений, а затем вызвать 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
и вызвать$post->delete()
foreach($user->posts as $post) { $post->delete(); }
-> это вызовет событие удаления на пост
В.С.
$user->posts()->delete()
-> это не сработает при удалении события post, потому что мы на самом деле не загружаем модель Post (мы запускаем только SQL-код: DELETE * from posts where user_id = $user->id
таким образом, модель Post даже не загружается)
Вы можете использовать этот метод в качестве альтернативы.
Что произойдет, это то, что мы возьмем все таблицы, связанные с таблицей пользователей, и удалим связанные данные, используя цикл
$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]);
}
first()
в запрос, чтобы я мог получить доступ к модели-события, например,User::where('id', '=', $id)->first()->delete();
Источник