Якщо у вас є події Watcher для оновлення або видалення записів, важливо знати, що вони запускаються лише під час оновлення окремих записів, а не під час їх масового оновлення
.Код спостерігача:Цей спостерігач зареєстрований у постачальника послуг:А тепер уявіть собі три різні промовисті запити:
app/Observers/PostObserver.php
:
class PostObserver{ public function deleted(Post $post) { // Например, удаляет связанные файлы изображений }}
app/Providers/AppServiceProvider.php
:
use App\Models\Post;use App\Observers\PostObserver;class AppServiceProvider extends ServiceProvider{ public function boot() { Post::observe(PostObserver::class); }}
$post = Post::first();$post->delete();// Это БУДЕТ запускать НаблюдателяPost::find(2)->delete();// Это также БУДЕТ запускать НаблюдателяPost::where('id', '>', 3)->delete();// Но это НЕ БУДЕТ запускать Наблюдателя!$user->posts()->delete();// Это также НЕ БУДЕТ запускать Наблюдателя!
Причина в тому, що події Спостерігача походять від Моделі красномовства. У випадку delete()
з конструктором запитів запит виконується безпосередньо до бази даних, минаючи окрему модель та її події.
Навіть якщо результатом запиту буде одна модель Eloquent, Watchers не запуститься.
Post::where('id', 4)->delete();
Це справедливо для будь-якого методу Observer: updated()
або , delete()
або updating()
deleting()
.
Це не менш важливо у випадку з деякими пакетами, які автоматично реєструють спостерігачів.
Наприклад, у випадку Spatie Media Library вона реєструє методdeleted()
, який не видалятиме пов'язані медіафайли, якщо ви видаляєте записи моделі масово, а не одну модель.
laravel-medialibrary/src/InteractsWithMedia.php
:
trait InteractsWithMedia{ public static function bootInteractsWithMedia() { static::deleting(function (HasMedia $model) { if ($model->shouldDeletePreservingMedia()) { return; } if (in_array(SoftDeletes::class, class_uses_recursive($model))) { if (! $model->forceDeleting) { return; } } $model->media()->cursor()->each(fn (Media $media) => $media->delete()); }); }