Каждый, кто хоть раз писал на Yii2, рано или поздно сталкивается с задачей: как сообщить пользователю, что его действие выполнено, или напомнить ему о чём-то. Можно, конечно, просто вывести текст на экран, но это скучно и непрофессионально. Сегодня я покажу, как построить полноценную систему уведомлений в Yii2 — от простых flash-сообщений до email-рассылок и даже Telegram-уведомлений. Будет интересно, обещаю.

Digital-студия WNDER

С чего начать: flash-сообщения — быстрый способ сказать "Привет"

Flash-сообщения — это как записка на холодильнике: появилась, показала информацию и исчезла после перезагрузки страницы. В Yii2 они реализованы через компонент session и используются для коротких уведомлений: "Сохранено!", "Ошибка при загрузке файла" и тому подобное.

Как это работает? В контроллере вы добавляете сообщение в сессию, а во вьюхе выводите его. Всё просто, но есть нюанс: flash-сообщения живут ровно до следующего запроса. Если пользователь обновит страницу — сообщение исчезнет. Это удобно для одноразовых уведомлений.

Пример добавления flash-сообщения в контроллере:

<?php

namespace app\controllers;

use Yii;
use yii\web\Controller;

class SiteController extends Controller
{
    public function actionSave()
    {
        // Какая-то логика сохранения
        $success = true;

        if ($success) {
            Yii::$app->session->setFlash('success', 'Данные успешно сохранены!');
        } else {
            Yii::$app->session->setFlash('error', 'Произошла ошибка при сохранении.');
        }

        return $this->redirect(['index']);
    }
}

А во вьюхе выводим:

<?php if (Yii::$app->session->hasFlash('success')): ?>
    <div class="alert alert-success">
        <?= Yii::$app->session->getFlash('success') ?>
    </div>
<?php endif; ?>

Ну и куда без стилей? Можно добавить CSS-классы, чтобы сообщения выглядели аккуратно. Но если нужно больше контроля — переходим к следующему шагу.

Создаём свою систему: компонент уведомлений

Flash-сообщения хороши, но для серьёзных проектов хочется чего-то более структурированного. Например, чтобы уведомления сохранялись в базе данных и пользователь мог просмотреть их позже. Или чтобы отправлялись на email. Для этого создадим свой компонент.

Компонент — это класс, который можно вызывать из любого места приложения. В Yii2 компоненты регистрируются в конфигурации, а затем доступны через Yii::$app->notification. Это удобно: вся логика в одном месте, и её можно переиспользовать.

Создаём файл components/NotificationComponent.php:

<?php

namespace app\components;

use Yii;
use yii\base\Component;
use yii\db\Exception;

class NotificationComponent extends Component
{
    public $fromEmail = 'no-reply@example.com';

    /**
     * Отправляет уведомление пользователю
     * @param int $userId ID пользователя
     * @param string $type Тип уведомления (info, warning, error)
     * @param string $message Текст сообщения
     * @param bool $sendEmail Отправлять ли email
     * @return bool
     * @throws Exception
     */
    public function send($userId, $type, $message, $sendEmail = false)
    {
        // Сохраняем в базу данных
        $notification = new Notification();
        $notification->user_id = $userId;
        $notification->type = $type;
        $notification->message = $message;
        $notification->is_read = 0;
        $notification->created_at = date('Y-m-d H:i:s');
        if (!$notification->save()) {
            throw new Exception('Не удалось сохранить уведомление');
        }

        // Если нужно, отправляем email
        if ($sendEmail) {
            $user = User::findOne($userId);
            if ($user && $user->email) {
                Yii::$app->mailer->compose()
                    ->setFrom($this->fromEmail)
                    ->setTo($user->email)
                    ->setSubject('Новое уведомление')
                    ->setTextBody($message)
                    ->send();
            }
        }

        return true;
    }

    /**
     * Получает непрочитанные уведомления пользователя
     * @param int $userId
     * @return Notification[]
     */
    public function getUnread($userId)
    {
        return Notification::find()
            ->where(['user_id' => $userId, 'is_read' => 0])
            ->orderBy(['created_at' => SORT_DESC])
            ->all();
    }
}

Регистрируем компонент в конфигурации (файл config/web.php):

'components' => [
    // ...
    'notification' => [
        'class' => 'app\components\NotificationComponent',
        'fromEmail' => 'noreply@myproject.com',
    ],
    // ...
],

Теперь в любом месте приложения можно вызвать:

Yii::$app->notification->send(Yii::$app->user->id, 'info', 'Ваш заказ принят!', true);

Это уже похоже на систему. Но давайте копнём глубже.

Расширяем возможности: уведомления в реальном времени

Иногда хочется, чтобы уведомления приходили мгновенно, без перезагрузки страницы. Например, когда кто-то оставляет комментарий или ставит лайк. Тут на помощь приходят WebSocket или просто периодический опрос сервера через AJAX.

Первый вариант — WebSocket — требует настройки сервера (например, Ratchet или Node.js). Это мощно, но сложно. Второй вариант — простой опрос каждые 30 секунд: клиент спрашивает сервер, есть ли новые уведомления. Это проще и часто достаточно.

Реализуем опрос. Создаём действие в контроллере, которое возвращает непрочитанные уведомления в формате JSON:

public function actionUnreadNotifications()
{
    Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    $notifications = Yii::$app->notification->getUnread(Yii::$app->user->id);

    $data = [];
    foreach ($notifications as $n) {
        $data[] = [
            'id' => $n->id,
            'type' => $n->type,
            'message' => $n->message,
            'created_at' => $n->created_at,
        ];
    }

    return $data;
}

На клиенте используем JavaScript для периодического запроса:

// Функция для получения уведомлений
function fetchNotifications() {
    fetch('/site/unread-notifications')
        .then(response => response.json())
        .then(data => {
            if (data.length > 0) {
                // Показываем уведомления, например, в попапе
                showNotificationPopup(data);
                // Отмечаем как прочитанные (можно дополнительно)
                markAsRead(data.map(n => n.id));
            }
        })
        .catch(error => console.error('Ошибка при загрузке уведомлений:', error));
}

// Запускаем опрос каждые 30 секунд
setInterval(fetchNotifications, 30000);

Этот подход легко масштабировать: можно добавить звуковой сигнал, иконку с бейджем и т.д.

Интеграция с Telegram: уведомления, которые всегда с тобой

Если твои пользователи сидят в Telegram, почему бы не отправлять уведомления прямо туда? Это удобно для администраторов или для сервисов, где нужна мгновенная реакция. Для этого понадобится создать бота через BotFather и получить токен.

В Yii2 можно использовать стороннюю библиотеку, например, telegram-bot/api или просто отправлять HTTP-запросы через curl. Я покажу вариант с использованием официального API.

Добавляем метод в наш компонент:

public function sendTelegram($chatId, $message)
{
    $token = 'ВАШ_ТОКЕН_БОТА';
    $url = "https://api.telegram.org/bot{$token}/sendMessage";

    $data = [
        'chat_id' => $chatId,
        'text' => $message,
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true)['ok'] ?? false;
}

Теперь можно отправлять уведомления в Telegram:

Yii::$app->notification->sendTelegram('@mychannel', 'Сервер упал!');

Не забудьте получить chat_id — это может быть ID пользователя или канала. Для канала нужно сначала добавить бота в канал и отправить туда сообщение, чтобы узнать ID.

Советы по организации и безопасности

Когда уведомлений становится много, важно правильно организовать их хранение и обработку. Вот несколько лайфхаков, которые я использую в своих проектах.

Параметр Описание Рекомендация
Хранение База данных или файлы? База данных — надёжнее, файлы — быстрее для маленьких проектов
Формат Текст или HTML? Лучше хранить простой текст, а форматировать при выводе
Очереди Много уведомлений? Используйте очередь (например, RabbitMQ) для асинхронной отправки
Безопасность Проверка прав Всегда проверяйте, что пользователь имеет доступ к своим уведомлениям
Правило: Не отправляйте уведомления на все каналы сразу без необходимости. Дайте пользователю выбрать, куда ему удобнее получать сообщения. Это повысит лояльность и снизит нагрузку на сервер.

Ещё один момент — не забывайте про очистку старых уведомлений. Если их накапливается слишком много, база раздувается. Можно поставить cron-задачу, которая удаляет записи старше, например, 30 дней.

Итог: что мы построили

Мы разобрали три уровня уведомлений: простые flash-сообщения для мгновенной обратной связи, собственную систему с хранением в базе и отправкой email, а также интеграцию с Telegram. Всё это можно комбинировать и расширять под свои нужды.

Главное — не перегружайте пользователя. Уведомления должны быть полезными и своевременными. И помните: код — это инструмент, а не самоцель. Стройте систему так, чтобы её было легко поддерживать и развивать.

Если остались вопросы — пишите в комментариях, с радостью помогу.

Студия WNDER