Уведомления
Система уведомлений Imhotep предоставляет унифицированный интерфейс для отправки сообщений пользователям через различные каналы связи. Компонент спроектирован с учетом расширяемости и гибкости, позволяя легко добавлять новые способы доставки сообщений.
Конфигурирование
Система уведомлений Imhotep уже включена в ядро приложения. Для начала работы необходимо настроить каналы доставки в файле конфигурации config/notifications.php.
Система поддерживает следующие драйверы:
- mail — отправка Email сообщений (документация)
- telegram — отправка уведомлений в Telegram (документация)
- database — сохранение уведомлений в базе данных (документация)
- fcm — отправка push-уведомлений через Firebase Cloud Messaging (документация)
Пример минимальной конфигурации:
return [
'default' => 'mail',
'channels' => [
'mail' => [
'driver' => 'mail',
'server' => 'smtp.example.com',
'port' => 587,
'login' => 'user@example.com',
'password' => 'password',
],
'database' => [
'driver' => 'database',
'table' => 'notifications',
],
'telegram' => [
'driver' => 'telegram',
'token' => 'YOUR_BOT_TOKEN',
],
'fcm' => [
'driver' => 'fcm',
'project_id' => 'your-project-id',
'credentials_path' => '/path/to/credentials.json',
],
],
];
Каждый драйвер поддерживает дополнительные параметры конфигурации, которые можно найти в соответствующей документации.
Создание уведомлений
В Imhotep каждое уведомление представлено отдельным классом, расширяющим базовый класс Imhotep\Notifications\Notification. Для быстрого создания уведомления используйте консольную комманду make:notification, которая поместит новый класс уведомления в каталог app/Notifications:
php imhotep make:notification OrderShipped
Пример класса уведомления
<?php
namespace App\Notifications;
use Imhotep\Notifications\Notification;
use Imhotep\Notifications\Messages\MailMessage;
use Imhotep\Notifications\Messages\DatabaseMessage;
class OrderShipped extends Notification
{
protected $order;
public function __construct($order)
{
$this->order = $order;
}
// Определение каналов доставки
// Уведомление будет отправлено на почту и в базу данных
public function via($recipient): array
{
return ['mail', 'database'];
}
// Формирование почтового уведомления
public function toMail($recipient): MailMessage
{
return (new MailMessage())
->subject('Заказ #'.$this->order->id.' отправлен')
->line('Ваш заказ был успешно отправлен.')
->line('Трек-номер: '.$this->order->tracking_number)
->action('Отследить заказ', url('/orders/'.$this->order->id))
->priority(1); // Высокий приоритет
}
// Формирование уведомления для сохранения в базе данных
public function toDatabase($recipient): DatabaseMessage
{
return new DatabaseMessage([
'order_id' => $this->order->id,
'status' => 'shipped',
'tracking_number' => $this->order->tracking_number,
]);
}
}
Отправка уведомлений
Определение каналов доставки
Каждый класс уведомления содержит метод via(), который возвращает один или несколько каналов доставки. По умолчанию система поддерживает следующие предустановленные каналы: mail, database, telegram и fcm (Firebase Cloud Messaging). При необходимости вы можете добавить собственные каналы доставки уведомлений.
Метод via() принимает первым параметром $recipient — экземпляр класса, которому отправляется уведомление, или строковое значение (например, email-адрес получателя). Используя $recipient, вы можете динамически определять каналы доставки:
public function via(mixed $recipient): array
{
// Всегда сохраняем в базу данных
$channels = ['database'];
if ($recipient instanceof User) {
// Добавляем email, если пользователь включил email-уведомления
if ($recipient->email_notifications) {
$channels[] = 'mail';
}
// Добавляем Telegram, если указан chat_id
if ($recipient->telegram_chat_id) {
$channels[] = 'telegram';
}
}
// Если получатель — email-адрес (строка), отправляем только по почте
if (is_string($recipient)) {
return ['mail'];
}
return $channels;
}
Метод
via()вызывается для каждого получателя отдельно, что позволяет настраивать каналы доставки индивидуально для каждого пользователя.
Подтверждение отправки уведомления
Иногда возникают ситуации, когда уведомление не должно быть доставлено по внешним причинам. Например:
- Отправка SMS только в дневное время
- Уведомление задержалось в очереди, и информация уже устарела
- Пользователь отключил определённый канал получения уведомлений
- И другие подобные ситуации
Для таких случаев вы можете определить метод shouldSend, который вызывается непосредственно перед отправкой уведомления. Если метод возвращает false, уведомление не будет отправлено через указанный канал.
public function shouldSend(User $recipient, string $channel): bool
{
// Если заказ уже был отменён, не отправлять уведомление
if ($this->order->status === 'cancelled') {
return false;
}
// Не отправлять SMS ночью
if ($channel === 'sms' && date('H') < 8) {
return false;
}
// Проверяем настройки пользователя
if ($channel === 'telegram' && !$recipient->telegram_notifications) {
return false;
}
return true;
}
Важно: Метод
shouldSendвызывается для каждого канала отдельно, что позволяет гибко управлять отправкой через разные каналы связи.
Язык уведомлений
Imhotep позволяет отправлять уведомления на языке пользователя, а не системы. Для этого, класс Imhotep\Notifications\Notification содержит метод locale для установки желаемого языка.
// Установка языка уведомления для конкретного пользователя
$user->notify((new OrderShipped($order))->locale('zh'));
// Установка языка уведомлений для нескольких пользвателей через фасад
Notification::locale('zh')->send($users, new OrderShipped($order));
Автоматическое определение языка пользователя:
Если в вашем приложении пользователи могут настраивать предпочитаемый язык индивидуально, тогда уведомляемая модель должна реализовывать интерфейс HasLocalePreference. Это позволит Imhotep автоматически использовать указанный язык при отправке уведомлений без явного вызова метода locale().
use Imhotep\Contracts\Localization\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Получить предпочитаемый пользователем язык.
*/
public function preferredLocale(): string
{
return $this->locale; // Поле 'locale' в таблице пользователей
}
}
Если язык уведомления не был установлен явно, будет использован язык из
app()->getLocale(). Язык, установленный через методlocale(), имеет приоритет над значением изpreferredLocale().
События
Система уведомлений Imhotep предоставляет несколько событий для полного контроля над жизненным циклом уведомлений. Позволяя создавать надежные системы коммуникации с подробным логированием и обработкой ошибок.
Перед отправкой NotificationSending
Событие Imhotep\Notifications\Events\NotificationSending позволяет выполнить проверки или модификации перед фактической отправкой. Уведомление не будет отправлено, если слушатель события вернет false в методе handle():
use Imhotep\Notifications\Events\NotificationSending;
class ValidateNotification
{
public function handle(NotificationSending $event): bool|null
{
$channel = $event->channel; // Канал отправки (mail, database и т.д.)
$recipient = $event->recipient; // Получатель уведомления
$notification = $event->notification; // Экземпляр уведомления
// Пример: проверка времени отправки
if ($channel === 'sms' && date('H') < 8) {
return false; // Отменяем отправку SMS ночью
}
return true; // Разрешаем отправку
}
}
После отправки NotificationSent
После успешной отправки уведомления система запускает событие Imhotep\Notifications\Events\NotificationSent. Это позволяет выполнить логирование, обновление статистики или другие постобработки.
use Imhotep\Notifications\Events\NotificationSent;
class LogNotification
{
public function handle(NotificationSent $event): void
{
$channel = $event->channel; // Канал отправки
$recipient = $event->recipient; // Получатель
$notification = $event->notification; // Уведомление
$response = $event->response; // Ответ сервиса доставки
// Логируем отправку
Log::info("Уведомление отправлено", [
'channel' => $channel,
'recipient' => $recipient->id,
'notification' => get_class($notification),
'response' => $response,
]);
// Обновляем статистику
Cache::increment('notifications_count');
}
}
Пропуск отправки NotificationSkipped
Событие Imhotep\Notifications\Events\NotificationSkipped вызывается, когда уведомление было пропущено (например, метод shouldSend() вернул false или слушатель NotificationSending вернул false).
use Imhotep\Notifications\Events\NotificationSkipped;
class LogSkippedNotifications
{
public function handle(NotificationSkipped $event): void
{
Log::info('Уведомление пропущено', [
'recipient' => $event->recipient->id,
'notification' => get_class($event->notification),
'channel' => $event->channel,
]);
}
}
Неудачная отправка NotificationFailed
Событие Imhotep\Notifications\Events\NotificationFailed вызывается при возникновении ошибки во время отправки уведомления.
use Imhotep\Notifications\Events\NotificationFailed;
class LogNotificationFailure
{
public function handle(NotificationFailed $event): void
{
// Логируем ошибку
Log::error('Ошибка отправки уведомления', [
'recipient' => $event->recipient->id,
'notification' => get_class($event->notification),
'channel' => $event->channel,
'error' => $event->exception->getMessage(),
'trace' => $event->exception->getTraceAsString(),
]);
}
}