Observers в Laravel: чистый способ обработки событий моделей

Когда ваше приложение на Laravel растёт, логика, связанная с моделями (например, отправка писем, обновление связанных данных, уменьшение остатков на складе), легко разбрасывается по контроллерам и самим моделям. Это усложняет поддержку и делает код менее понятным.

Laravel предоставляет удобный инструмент - наблюдатели (Observers). Это специальные классы, которые слушают события моделей и автоматически выполняют нужные действия в ответ на них. Они помогают вынести связанную с событиями логику из моделей и контроллеров в одно централизованное место.

Что такое Observers и зачем они нужны

Наблюдатель - это класс, который реагирует на события жизненного цикла модели (например, когда запись создаётся, обновляется или удаляется) и запускает определённые действия.

Зачем использовать Observers

  • Чистый код: вся логика, привязанная к событиям модели, собрана в одном месте/ Модели и контроллеры при этом остаются "тонкими".

  • Проще отлаживать: легче понять, что и где происходит, когда ответственный код централизован.

  • Производительность: тяжёлые задачи (например, отправка писем) можно выполнять в очередях, чтобы не тормозить ответ пользователю.

Жизненный цикл событий модели

Laravel поддерживает множество "хуков" (точек входа), соответствующих разным моментам работы с моделью. Основные из них:

Событие

Что означает

retrieved

После получения модели из базы

creating

Перед созданием записи

created

После создания записи

updating

Перед обновлением

updated

После обновления

saving

Перед сохранением (и созданием, и обновлением)

saved

После сохранения

deleting

Перед удалением

deleted

После удаления

restoring

Перед восстановлением мягко удалённой записи

restored

После восстановления

Полный список событий модели можно найти в документации Laravel.

Как создать Observer

Рассмотрим пример интернет-магазина, где после подтверждения заказа нужно уменьшить остатки, отправить письма клиенту и т. д.

1. Генерация класса наблюдателя

Запустите команду Artisan, чтобы создать класс:

php artisan make:observer OrderObserver --model=Order

Это создаст файл OrderObserver и привяжет его к модели Order.

2. Реализация логики в Observer

Пример такого класса может выглядеть так:

class OrderObserver
{
    // Срабатывает после создания заказа
    public function created(Order $order)
    {
        // Благодарственное письмо покупателю
        Mail::to($order->user->email)->send(new OrderThanks($order));

        // Уведомление администратору
        Notification::route('slack', env('SLACK_WEBHOOK'))
            ->notify(new OrderNotification($order));
    }

    // Срабатывает при обновлении заказа
    public function updated(Order $order)
    {
        if ($order->status === 'confirmed'
            && $order->getOriginal('status') !== 'confirmed') {

            // Уменьшаем остатки
            foreach ($order->products as $product) {
                $product->decrement('stock', $product->pivot->quantity);
            }

            // Подтверждающее письмо
            Mail::to($order->user->email)
                ->send(new OrderConfirmation($order));

            // Логируем
            Log::info("Order confirmed", [
                'order_id' => $order->id,
                'user_id'  => $order->user_id,
            ]);
        }

        if ($order->status === 'shipped'
            && $order->getOriginal('status') !== 'shipped') {

            // Письмо о отправке
            Mail::to($order->user->email)
                ->send(new OrderShipment($order));
        }
    }
}

Здесь наблюдатель реагирует на создание заказа и его обновление (например, смену статуса). Обратите внимание: внутри наблюдателя нельзя использовать глобальную функцию request(), вместо этого берите данные из самой модели и её связей.

3. Регистрация наблюдателя

Чтобы Laravel начал использовать наблюдателя, его нужно зарегистрировать. Обычно это делают в провайдере:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Models\Order;
use App\Observers\OrderObserver;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Order::observe(OrderObserver::class);
    }
}

Без регистрации Observer просто не будет вызываться.

Пример использования

После подключения наблюдателя вот так выглядит создание и обновление заказа:

// Создаём заказ
$order = Order::create([
    'user_id'      => 1,
    'status'       => 'pending',
    'total_amount' => 100,
]);

$order->products()->attach([
    1 => ['quantity' => 2],
    3 => ['quantity' => 1],
]);

// Подтверждаем заказ — Observer уменьшает остатки и отправляет письмо
$order->update(['status' => 'confirmed']);

// Меняем статус на отправлен — Observer отправляет уведомление
$order->update(['status' => 'shipped']);

При обновлении статуса наблюдатель автоматически выполняет нужные действия.

Observer и очередь

Если действия, выполняемые в наблюдателе, тяжёлые (например, отправка писем, уведомлений), имеет смысл выполнить их в фоне. Достаточно реализовать интерфейс ShouldQueue:

use Illuminate\Contracts\Queue\ShouldQueue;

class OrderObserver implements ShouldQueue
{
    // задачи будут выполняться через очередь
}

Тогда Laravel будет обрабатывать задачи в очереди, если настроены соответствующие workers.

Наблюдатели - это мощный инструмент Laravel, который помогает избавиться от разрозненной логики, сделать код чище, понятнее и проще сопровождать. Они особенно полезны там, где нужно централизованно обрабатывать события моделей, не загромождая контроллеры и сами модели.

Если вы до сих пор писали обработку событий прямо в моделях или контроллерах, попробуйте перенести её в Observers и почувствуйте разницу. Пусть ваш код будет чище и удобнее для разработки.

Похожие статьи

Рекомендательные технологии Подробнее

Как скачивать файлы по URL в Laravel

Подробное руководство по скачиванию файлов из внешних URL в Laravel: от простого стриминга до продакшен-ориентированных подходов с сохранением и раздачей файлов пользователям.

Декодирование JSON из HTTP-ответов в Laravel

Как Laravel обрабатываются JSON-ответы HTTP-клиента и какие возможности дают флаги декодирования JSON. Рассматриваются ошибки декодирования, работа с большими числами и настройка флагов по умолчанию.

Генерация PDF в Laravel: Spatie Laravel PDF vs Laravel DomPDF

Сравнение двух популярных пакетов для генерации PDF в Laravel, современного Spatie Laravel PDF с рендерингом через браузер и классического Laravel DomPDF, который работает в чистом PHP. Обсуждаем преимущества, примеры, ограничения и когда что использовать.