Web Push сповіщення у Laravel: як налаштувати відправку повідомлень безпосередньо через браузер.

Перекладено ШІ 0 Laravel News 01 травня, 2026

Бажаєте залишатися на зв’язку з користувачами, навіть коли вкладка браузера закрита? Дізнайтеся, як інтегрувати Web Push сповіщення у Laravel без використання сторонніх сервісів.

Web push-сповіщення дозволяють вашому застосунку взаємодіяти з користувачами, навіть якщо вкладка браузера закрита — при цьому нативний застосунок не потрібен. Проєкт Laravel Notification Channels — це спільнота розробників, що створює драйвери сповіщень для Laravel. Їхній Web Push Notifications Channel інтегрується у стандартну систему Laravel, тож ви створюєте класи сповіщень так само, як для пошти чи Slack. Під капотом пакет взаємодіє безпосередньо з браузерним Push API через VAPID-ключі, без сторонніх сервісів. Підтримуються Chrome, Firefox, Edge та Safari.

# Встановлення

Встановіть пакет через Composer, а потім додайте трейт HasPushSubscriptions до вашої моделі User — це додасть методи для керування підписками:

composer require laravel-notification-channels/webpush
use NotificationChannels\WebPush\HasPushSubscriptions;
 
class User extends Model
{
    use HasPushSubscriptions;
}

Для зберігання підписок потрібна таблиця в базі даних. Опублікуйте та запустіть міграцію:

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="migrations"
php artisan migrate

Якщо потрібно змінити параметри, опублікуйте файл конфігурації:

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="config"

Нарешті, згенеруйте пару VAPID-ключів:

php artisan webpush:vapid

Ця команда додасть VAPID_PUBLIC_KEY та VAPID_PRIVATE_KEY у ваш файл .env. Публічний ключ передається браузеру під час підписки, а приватний — підписує вихідні повідомлення для верифікації. Оберігайте ці ключі як будь-які інші секретні дані та не змінюйте їх, оскільки наявні підписки жорстко прив'язані до цієї пари ключів.

Для роботи в Safari або на iOS необхідно також додати VAPID_SUBJECT — URL або mailto: адресу вашого застосунку. Apple вимагає цей ідентифікатор, інакше виникне помилка BadJwtToken.

# Налаштування клієнтської частини

Для роботи Web push у браузері потрібні дві речі: service worker для отримання та відображення сповіщень у фоні, а також виклик PushManager.subscribe() для отримання об'єкта підписки.

Примітка: Push API працює лише в безпечному контексті (HTTPS). Єдиний виняток — http://localhost для локальної розробки.

# Service Worker

Створіть файл public/sw.js. Він працюватиме у фоні та оброблятиме події push:

self.addEventListener('push', function (event) {
    let data = {};
 
    try {
        data = event.data?.json() ?? {};
    } catch (e) {
        data = { title: 'Notification', body: event.data?.text() ?? '' };
    }
 
    event.waitUntil(
        self.registration.showNotification(data.title, {
            body: data.body,
            icon: data.icon,
            badge: data.badge,
            data: data.data,
            actions: data.actions,
        })
    );
});
 
self.addEventListener('notificationclick', function (event) {
    event.notification.close();
 
    const url = event.notification.data?.url ?? '/';
 
    event.waitUntil(
        self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) {
            for (const client of clientList) {
                if (client.url === url & 'focus' in client) {
                    return client.focus();
                }
            }
            return self.clients.openWindow(url);
        })
    );
});

Обробник notificationclick перевіряє, чи вже відкрите вікно з потрібною адресою, і замість дублювання вкладки просто перемикає фокус на неї.

# Підписка користувача

Додайте VAPID публічний ключ у meta-тег вашого макета, щоб він був доступний для JavaScript:

<meta name="vapid-public-key" content="{{ config('webpush.vapid.public_key') }}">

Прив'яжіть процес підписки до кліку на кнопку — браузери блокують запит на дозвіл, якщо він не ініційований дією користувача:

function urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
    const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
    const rawData = atob(base64);
    return Uint8Array.from([...rawData].map((c) => c.charCodeAt(0)));
}
 
async function subscribe() {
    if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
        return;
    }
 
    const permission = await Notification.requestPermission();
    if (permission !== 'granted') {
        return;
    }
 
    await navigator.serviceWorker.register('/sw.js');
    const registration = await navigator.serviceWorker.ready;
 
    const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];
 
    const subscription = await registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
            document.querySelector('meta[name="vapid-public-key"]').content
        ),
    });
 
    const { endpoint, keys: { p256dh, auth } } = subscription.toJSON();
 
    await fetch('/push/subscribe', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
        },
        body: JSON.stringify({ endpoint, key: p256dh, token: auth, encoding: contentEncoding }),
    });
}

Використання navigator.serviceWorker.ready гарантує, що pushManager.subscribe() буде викликано лише тоді, коли worker стане активним.

Ваш серверний маршрут /push/subscribe отримає ці дані та передасть їх методу updatePushSubscription() автентифікованого користувача.

# Керування підписками

Для збереження даних підписки на сервері викликайте updatePushSubscription():

$user->updatePushSubscription($endpoint, $key, $token, $contentEncoding);

Видалити підписку можна через deletePushSubscription():

$user->deletePushSubscription($endpoint);

Пакет автоматично видаляє з бази даних підписки, термін дії яких закінчився, щойно браузер поверне відповідну помилку при спробі надсилання.

# Надсилання сповіщення

Надсилання Web push ідентичне іншим сповіщенням Laravel. Додайте WebPushChannel у метод via() та реалізуйте toWebPush():

use Illuminate\Notifications\Notification;
use NotificationChannels\WebPush\WebPushChannel;
use NotificationChannels\WebPush\WebPushMessage;
 
class GoalScored extends Notification
{
    public function __construct(
        protected string $scorer,
        protected string $team,
        protected int $minute,
    ) {}
 
    public function via($notifiable): array
    {
        return [WebPushChannel::class];
    }
 
    public function toWebPush($notifiable, $notification): WebPushMessage
    {
        return (new WebPushMessage)
            ->title('ГОЛ! ' . $this->team)
            ->icon('/icons/football.png')
            ->body("{$this->scorer} забиває на {$this->minute}' хвилині ⚽")
            ->action('Дивитися матч', 'view_match')
            ->data(['url' => '/matches/live'])
            ->options(['TTL' => 60]);
    }
}

Параметр TTL (time to live) корисний для динамічних подій: сповіщення про гол через годину вже не матиме сенсу. WebPushMessage підтримує всі можливості Push API: від іконок та кнопок до вібрації та обов'язкової взаємодії (requireInteraction).

Надішліть сповіщення стандартним методом:

$user->notify(new GoalScored(scorer: 'Eric Barnes', team: 'Laravel News FC', minute: 29));

# Declarative Web Push

DeclarativeWebPushMessage — це сучасна альтернатива, що базується на специфікації Declarative Web Push. Традиційний Web push потребує активного service worker для обробки події, тоді як декларативний push передає цю логіку безпосередньо браузеру. Це заощаджує заряд батареї та покращує приватність на мобільних пристроях. Хоча підтримка браузерами поки обмежена, API дуже схожий на стандартний:

use NotificationChannels\WebPush\DeclarativeWebPushMessage;
use NotificationChannels\WebPush\WebPushChannel;
 
public function toWebPush($notifiable, $notification): DeclarativeWebPushMessage
{
    return (new DeclarativeWebPushMessage)
        ->title('ГОЛ! ' . $this->team)
        ->icon('/icons/football.png')
        ->body("{$this->scorer} забиває на {$this->minute}' хвилині ⚽")
        ->action('Дивитися матч', 'view_match', 'https://myapp.com/matches/live')
        ->navigate('https://myapp.com/matches/live');
}

Головна відмінність — метод navigate(), який вказує браузеру, куди спрямувати користувача при натисканні, без участі service worker.

Детальніше про Web Push Notifications channel та вихідний код можна дізнатися на GitHub.

Популярні

Інше, що варто прочитати

16 Оновлено 01 травня, 2026

Обробка геопросторових даних за допомогою Laravel Magellan

Ви готові відкрити нові горизонти у роботі з геопросторовими даними в Laravel? Дізнайтеся, як за допомогою PostGIS та пакету Laravel-Magellan можна легко зберігати, запитувати та маніпулювати інформацією про розташування, перетворюючи ваші проекти на вражаючі рішення у сфері картографії та геолокації!

16 Оновлено 01 травня, 2026

Налаштування Xdebug з Docker та PHP 8.4 всього за одну хвилину

Встановлення Xdebug може бути складним завданням, але в цій статті ми розкриємо, як швидко та просто налаштувати його за допомогою Docker на прикладі Laravel. Дочитайте до кінця, щоб дізнатися, як за кілька хвилин зробити Xdebug вашим надійним помічником у розробці

10 Оновлено 01 травня, 2026

Управління доступом у Filament за допомогою плагіна Shield

Дізнайтеся, як пакет Filament Shield забезпечує управління доступом до ваших панелей, ресурсів і віджетів у Laravel. Ця стаття розкриває основні можливості пакету, включаючи просту установку та підтримку багатокористувацьких середовищ — не пропустіть!