Каждый, кто хоть раз делал сайт с формой обратной связи или интернет-магазин, сталкивался с ситуацией: пользователь что-то сделал, а вы об этом не знаете. Или знаете, но только когда заглянули в админку. А если у вас 10 000 пользователей? Тогда без системы уведомлений — как без рук. В этой статье я покажу, как построить такую систему на PHP, начиная с простого и заканчивая современными подходами. Не переживайте, если вы новичок — объясню всё на пальцах, с примерами и лайфхаками.

Digital-студия WNDER

Что такое уведомления и зачем они нужны

Уведомление — это способ сообщить пользователю или администратору о каком-то событии. Например, кто-то оставил комментарий, заказ, или на сервере произошла ошибка. Без уведомлений вы будете терять клиентов и время.

Уведомления бывают разные:

  • Email-уведомления — классика, но письма могут теряться или попадать в спам.
  • SMS — дорого, но срочно.
  • Push-уведомления в браузере или на телефоне — требуют настройки.
  • Внутренние уведомления (на самом сайте) — когда пользователь заходит, видит колокольчик с цифрой.

В этой статье мы сделаем внутренние уведомления и добавим email-рассылку. А для тех, кто хочет «реального времени», покажу, как подключить WebSockets.

С чего начать: база данных для уведомлений

Прежде чем писать код, нужно спроектировать таблицу для хранения уведомлений. Вот простая схема:

Поле Тип Описание
id INT AUTO_INCREMENT Первичный ключ
user_id INT Кому адресовано
type VARCHAR(50) Тип (например, 'comment', 'order')
message TEXT Текст уведомления
is_read BOOLEAN Прочитано или нет
created_at DATETIME Когда создано

Для начала этого достаточно. Если хотите хранить ссылку на переход, добавьте поле link — VARCHAR(255).

CREATE TABLE notifications (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  type VARCHAR(50) NOT NULL,
  message TEXT NOT NULL,
  is_read BOOLEAN DEFAULT FALSE,
  link VARCHAR(255) DEFAULT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Простой класс для работы с уведомлениями

Теперь напишем PHP-класс, который будет добавлять, получать и отмечать уведомления. Я использую PDO для работы с базой — это безопасно и удобно.

class Notification {
    private $db;

    public function __construct($pdo) {
        $this->db = $pdo;
    }

    // Добавить уведомление
    public function add($userId, $type, $message, $link = null) {
        $stmt = $this->db->prepare("INSERT INTO notifications (user_id, type, message, link) VALUES (?, ?, ?, ?)");
        $stmt->execute([$userId, $type, $message, $link]);
        return $this->db->lastInsertId();
    }

    // Получить непрочитанные для пользователя
    public function getUnread($userId) {
        $stmt = $this->db->prepare("SELECT * FROM notifications WHERE user_id = ? AND is_read = FALSE ORDER BY created_at DESC");
        $stmt->execute([$userId]);
        return $stmt->fetchAll();
    }

    // Отметить как прочитанное
    public function markAsRead($notificationId, $userId) {
        $stmt = $this->db->prepare("UPDATE notifications SET is_read = TRUE WHERE id = ? AND user_id = ?");
        $stmt->execute([$notificationId, $userId]);
    }

    // Получить количество непрочитанных
    public function countUnread($userId) {
        $stmt = $this->db->prepare("SELECT COUNT(*) FROM notifications WHERE user_id = ? AND is_read = FALSE");
        $stmt->execute([$userId]);
        return $stmt->fetchColumn();
    }
}

Использовать просто:

$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$notification = new Notification($pdo);

// Добавить уведомление для пользователя с id=1
$notification->add(1, 'order', 'Ваш заказ отправлен!', '/orders/123');

// Получить непрочитанные
$unread = $notification->getUnread(1);
Правило: всегда используйте подготовленные выражения (prepare/execute) при работе с базой. Это защищает от SQL-инъекций. Никогда не вставляйте данные напрямую в запрос.

Как показывать уведомления на сайте

Теперь сделаем так, чтобы пользователь видел свои уведомления. Обычно это колокольчик в шапке сайта с числом непрочитанных. Когда пользователь кликает — выпадает список.

Для этого понадобится HTML-разметка и немного JavaScript для AJAX-запросов. Сначала создадим PHP-скрипт, который вернёт данные в формате JSON.

// get_notifications.php
session_start();
require 'Notification.php';

// Предположим, пользователь авторизован и его id в сессии
$userId = $_SESSION['user_id'];
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$notification = new Notification($pdo);

$unread = $notification->getUnread($userId);
$count = $notification->countUnread($userId);

echo json_encode(['count' => $count, 'items' => $unread]);

Теперь HTML-часть с кнопкой и списком:

<div class="notification-wrapper">
  <button id="notification-btn" class="notification-btn">
    Уведомления <span id="notification-count">0</span>
  </button>
  <ul id="notification-list" class="notification-list" style="display: none;"></ul>
</div>

И JavaScript, который подгружает уведомления и обновляет счётчик:

document.getElementById('notification-btn').addEventListener('click', function() {
    var list = document.getElementById('notification-list');
    if (list.style.display === 'none') {
        list.style.display = 'block';
        loadNotifications();
    } else {
        list.style.display = 'none';
    }
});

function loadNotifications() {
    fetch('get_notifications.php')
        .then(response => response.json())
        .then(data => {
            document.getElementById('notification-count').textContent = data.count;
            var list = document.getElementById('notification-list');
            list.innerHTML = '';
            data.items.forEach(function(item) {
                var li = document.createElement('li');
                li.textContent = item.message;
                if (item.link) {
                    var a = document.createElement('a');
                    a.href = item.link;
                    a.textContent = ' Перейти';
                    li.appendChild(a);
                }
                list.appendChild(li);
            });
        });
}

Обратите внимание: мы не используем jQuery, а работаем на чистом JS — это современно и быстро.

Отправка email-уведомлений: классика жанра

Иногда внутренних уведомлений мало. Например, пользователь хочет получать письма на почту. Для этого в PHP есть функция mail(), но она часто не работает на хостинге и письма попадают в спам. Лучше использовать библиотеку PHPMailer.

Сначала установите PHPMailer через Composer:

composer require phpmailer/phpmailer

Теперь напишем функцию отправки:

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php';

function sendEmail($to, $subject, $body) {
    $mail = new PHPMailer(true);
    try {
        // Настройки сервера
        $mail->isSMTP();
        $mail->Host = 'smtp.example.com';
        $mail->SMTPAuth = true;
        $mail->Username = 'your@example.com';
        $mail->Password = 'your_password';
        $mail->SMTPSecure = 'tls';
        $mail->Port = 587;

        // Отправитель и получатель
        $mail->setFrom('noreply@example.com', 'Ваш сайт');
        $mail->addAddress($to);

        // Контент
        $mail->isHTML(true);
        $mail->Subject = $subject;
        $mail->Body = $body;
        $mail->AltBody = strip_tags($body);

        $mail->send();
        echo 'Письмо отправлено';
    } catch (Exception $e) {
        echo "Ошибка: {$mail->ErrorInfo}";
    }
}

Теперь при добавлении уведомления можно вызывать и отправку письма. Но не делайте это синхронно, иначе страница будет грузиться долго. Лучше поставить задачу в очередь, например, через RabbitMQ или просто cron.

Реальное время: WebSockets и Pusher

Если вы хотите, чтобы уведомления появлялись мгновенно, без перезагрузки страницы, нужны WebSockets. Но реализовать их на чистом PHP сложно. Есть два пути:

  • Использовать ReactPHP или Ratchet — это библиотеки для асинхронного PHP.
  • Использовать готовые сервисы, например Pusher или Ably. Они предоставляют API и SDK.

Второй путь проще. Вот пример с Pusher:

require 'vendor/autoload.php';

$options = [
    'cluster' => 'eu',
    'useTLS' => true
];
$pusher = new Pusher\Pusher('key', 'secret', 'app_id', $options);

// Отправляем событие на канал user_1
$pusher->trigger('user_1', 'new-notification', ['message' => 'Привет!']);

А на клиенте подписываемся на канал и ловим событие:

// Подключаем библиотеку Pusher (можно через CDN)

Это отличный вариант для MVP. Но учтите, что бесплатный план Pusher имеет ограничения. Если проект серьёзный, лучше поднять свой WebSocket-сервер на Node.js или Go.

Лайфхак: если не хотите заморачиваться с WebSockets, используйте simple polling — просто запрашивайте уведомления каждые 30 секунд через AJAX. Для большинства сайтов этого достаточно.

Оптимизация и лучшие практики

Чтобы система уведомлений работала быстро и не падала, следуйте простым правилам:

  • Индексируйте таблицу по полям user_id и created_at. Это ускорит выборку.
  • Не храните уведомления вечно. Раз в месяц удаляйте старые (например, старше 90 дней).
  • Для массовой рассылки используйте очереди. Не отправляйте 1000 писем в одном цикле.
  • Кэшируйте количество непрочитанных уведомлений, чтобы не нагружать базу.

Пример кэширования через Memcached:

$cache = new Memcached();
$cache->addServer('localhost', 11211);

$userId = 1;
$count = $cache->get('unread_count_' . $userId);
if ($count === false) {
    // Запрос к БД
    $count = $notification->countUnread($userId);
    $cache->set('unread_count_' . $userId, $count, 60); // на 60 секунд
}

И не забывайте про безопасность: всегда проверяйте, что пользователь имеет право на просмотр уведомлений (по user_id).

Что в итоге

Мы разобрали, как создать систему уведомлений на PHP с нуля. Начали с таблицы, написали класс для работы с базой, добавили интерфейс с AJAX, настроили email-рассылку и даже заглянули в реальное время. Теперь вы можете добавить уведомления на свой сайт — это повысит вовлечённость пользователей и упростит вам жизнь.

Главное — не пытайтесь объять необъятное. Начните с простого, а потом улучшайте. И помните: код должен быть чистым и понятным, даже если это внутренний проект.

Студия WNDER