Обновление уведомлений

Выдалась свободная минутка (точнее, часик) и я организовал-таки нормальные уведомления о комментариях на почту.

Если кто не в курсе, то раньше было вот так:

Чтобы понять о чём речь, нужно было обязательно пройти на сайт и почитать - что не очень удобно, если там только "спасибо за ответ!".

А теперь уведомления выглядят вот так:

Сразу ясно - надо ли заходить на сайт для ответа, или можно просто удалить письмо. Подобные уведомления не являются чем-то новым, я уже делал такое на modx.pro, но здесь решил улучшить пару моментов.

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

Во-вторых, за эти 10 минут получатель уведомлений может и сам зайти на сайт и всё прочитать. Отправлять юзеру уведомления о том, что он уже видел, ну нужно. Подобным грешит, например, Вконтакте - неоднократно замечал, раздражает.

Значит, нам нужна табличка для уведомлений. В отличие от обычной очереди email я решил сделать ссылки на событие:

$this->schema->create(
    'user_notifications',
    function (Blueprint $table) {
        $table->uuid('id');
        // Связи с юзером-получателем уведомления, заметкой и комментом
        $table->foreignId('user_id')
            ->constrained('users')->cascadeOnDelete();
        $table->foreignId('topic_id')
            ->constrained('topics')->cascadeOnDelete();
        $table->foreignId('comment_id')
            ->constrained('comments')->cascadeOnDelete();
        // Тип сообщения: обычный коммент или ответ другому юзеру
        $table->string('type', 50);
        // Уведомление еще актуально
        $table->boolean('active')->default(true)->index();
        // Уведомление было отправлено
        $table->boolean('sent')->default(false)->index();
        // Метки времени: created_at и updated_at
        $table->timestamps();
         // Время отправки
        $table->timestamp('sent_at')->nullable();
    }
);

Флаг active нам нужен для того, чтобы отключить отправку уведомления, если оно уже устарело - то есть юзер увидел комментарий раньше. Это я определяю по времени последнего просмотра заметки пользователем. Если зашёл в заметку, то считаем, что прочитал там все комментрии.

Генерация и отправка писем работает по расписанию каждые n минут:

// Дата создания = текущая дата минус 600 секунд
$created_at = Carbon::now()->subSeconds(600)->toDateTimeString();
// Выбираем активные неотправленные уведомления,
// которые были созданы более 10 минут назад
$notifications = UserNotification::query()
    ->where('created_at', '<=', $created_at)
    ->where([
        'active' => true,
        'sent' => false,
    ]);

foreach ($notifications->cursor() as $notification) {
    $notification->sendEmail();
}

Сами email создаются в момент отправки, так что там всегда будет актуальная информация. Главное, чтобы задержка отправки была больше времени редактирования.

public function sendEmail(): ?string
{
    // Служба работы с почтой
    $mail = new Mail();

    // Заголовок зависит от типа уведомления
    $subject = getenv($this->type === 'comment-new' ? 'LEXICON_COMMENT_NEW' : 'LEXICON_COMMENT_REPLY');
    // Все возможные данные для оформления писем
    $data = [
        'comment' => $this->comment->toArray(),
        'user' => $this->comment->user->toArray(),
        'topic' => $this->topic->toArray(),
        'author' => $this->topic->user->toArray(),
    ];

    // Почта может вернуть ошибку
    if ($err = $mail->send($this->user->email, $subject, $this->type, $data)) {
        return $err;
    }
    // Если отправка прошла без ошибок - отмечаем
    $this->sent = true;
    $this->sent_at = time();
    $this->save();

    return null;
}

Заодно это убирает задержку на отправку писем при комментировании.

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

Оставляем комментарии к заметке, тестируем новый функционал!

← Предыдущая заметка
Загрузка видеофайлов
Следующая заметка →
Курс по Vesp доступен бесплатно
Комментарии (2)
inetloverАлександр Наумов
19.02.2023 12:20

Класс, я даже бы не додумался, что с простыми уведомлениями можно сделать что-то еще и так их прокачать!

С другой стороны такие продвинутые уведомления снизят трафик ))

bezumkinВасилий Наумкин
19.02.2023 16:49

Не такая уж тут активность в комментриях, чтобы что-то снижать - а удобнее будет, в первую очередь мне самому.

bezumkin
Василий Наумкин
15.09.2023 08:27
Никакой разницы, только в количестве строк. Не знаю, почему я так написал - не задумывался -)
bezumkin
Василий Наумкин
14.09.2023 23:59
Очень рад, что тебе понравилось!
NightRider
12.09.2023 12:37
Понял. Спасибо за подробный ответ!
Сергей Лелеко
10.09.2023 06:23
Понял! да , сталкивался с этой проблемкой
Дмитрий П.
07.09.2023 11:19
ну да, что-то на этот момент я не обратил внимание) видимо дублирование в девтулсах происходит из-за...
NightRider
29.08.2023 10:30
Просто так совпало что у меня пока что есть свободное время на изучение. Остальные скорее всего побо...
bezumkin
Василий Наумкин
28.08.2023 01:02
Абсоюлютно верно, у меня даже IDE подсвечивает ошибку - не знаю, как пропустил. Поменял на $me-&gt;g...
bezumkin
Василий Наумкин
25.08.2023 10:24
Очень рад!
inetlover
Александр Наумов
24.08.2023 10:49
Спасибо!!
bezumkin
Василий Наумкин
21.08.2023 12:37
Сорян, что-то я уже второй раз затупил с этим дампом. Держи правильный ms2.sql.zip (2.79 Mb), в заме...