Живі props в Inertia з Laravel Reverb: реальний час для сторінки замовлення

У матеріалі розбирається підхід автора Paul Redmond до того, як зробити сторінку замовлення в Laravel + Inertia такою, що оновлюється в реальному часі через Laravel Reverb. Ключова ідея — використати Inertia live props і події трансляції, щоб усі відкриті вкладки з конкретним замовленням автоматично бачили зміну статусу.

1. Коли потрібні live props і що вони роблять

З виходом Inertia.js 3.11 зʼявилися live props — спеціальні props, які Inertia оновлює при надходженні подій від сервера. Сторінка сама підписується на канал трансляції, а коли приходить подія — робить частковий reload тільки тих props, які позначені як live.

Автор демонструє це на кейсі статусу замовлення: як тільки замовлення переходить у стан «shipped», усі користувачі, що дивляться сторінку цього замовлення, бачать новий статус без перезавантаження сторінки.

Для прикладу потрібні такі версії пакетів:

  • inertia-laravel від 3.7
  • @inertiajs/vue3 від 3.11 (або відповідні пакети для React/Svelte)
composer require inertiajs/inertia-laravel:^3.7
npm install @inertiajs/vue3@^3.11

Якщо ви використовуєте React або Svelte, замініть Vue-пакет на @inertiajs/react або @inertiajs/svelte.

2. Налаштування трансляцій: Reverb і Echo

Щоб live props отримували оновлення, потрібна система трансляцій Laravel. Автор використовує вбудований сервер Laravel Reverb разом з Echo.

Laravel має інсталятор для broadcasting, який одразу готує Reverb:

php artisan install:broadcasting --reverb

Ця команда:

  • встановлює Reverb
  • додає змінні REVERB_* і VITE_REVERB_* в .env
  • створює routes/channels.php для каналів трансляції
  • для Vue/React проектів — встановлює відповідний Echo-пакет (@laravel/echo-vue або @laravel/echo-react) і вставляє виклик configureEcho() в resources/js/app.ts або app.tsx
import { configureEcho } from '@laravel/echo-vue'
configureEcho({
  broadcaster: 'reverb',
})

Для Svelte інсталятор Echo не налаштовує, тому там знадобиться вручну додати @laravel/echo-svelte і виклик configureEcho() з такими ж опціями.

3. Подія трансляції для оновлення замовлення

Щоб Inertia знала, з якого каналу слухати live prop, потрібна подія, яка реалізує ShouldBroadcast. Автор створює подію OrderUpdated:

php artisan make:event OrderUpdated

У тілі події важливі два моменти:

  • вона приймає модель Order
  • визначає приватний канал, на який транслюється оновлення конкретного замовлення

Приклад коду в оригіналі

Таким чином, кожне замовлення слухається на своєму каналі orders.{id}. Оскільки це PrivateChannel, потрібне правило авторизації в routes/channels.php, щоб сторонні користувачі не могли підписатись на чужі замовлення. Автор обмежує доступ власником замовлення:

Приклад коду в оригіналі

Ця умова перевіряє, що $user->id збігається з $order->user_id. Без такої перевірки приватні канали не будуть працювати, або, в разі занадто слабких правил, виникне витік даних.

4. Маркуємо prop як live у контролері

Далі автор показує, як перетворити звичайний Inertia-prop order на live prop. Це робиться в контролері через Inertia::live():

Приклад коду в оригіналі

Ключові деталі цієї конфігурації:

  • Значення через замикання. Функція fn () => $order->fresh() повертає завжди актуальний стан замовлення. Inertia викликає цю функцію при першому рендері сторінки і щоразу, коли потрібно перезавантажити live prop.
  • Параметр on: отримує екземпляр події трансляції new OrderUpdated($order). Створення події тут нічого не транслює — Inertia лише викликає в неї broadcastOn(), щоб дізнатись, на який канал підписуватися.

В результаті, кожна сторінка, яка рендерить Orders/Show з таким live prop, буде:

  • підписана на orders.{order-id}
  • отримувати події OrderUpdated
  • робити частковий reload тільки для props, позначених як live

5. Підключення клієнта Inertia до Echo

Щоб live props реально реагували на події, Inertia на фронтенді має отримати транспорт echo(). У Vue-проєкті автор підключає його в resources/js/app.ts після виклику configureEcho():

Приклад коду в оригіналі

Пара моментів, на які варто звернути увагу:

  • пакет @inertiajs/vue3/echo надає функцію echo(), яку і потрібно передати в опцію live при створенні Inertia-додатку
  • додаткового коду у сторінковому компоненті для props не потрібно — достатньо того, що prop позначений як live у контролері, а клієнтське live: echo() налаштоване

При завантаженні сторінки Inertia сама:

  • зʼєднується з Reverb через Echo
  • підписується на канали, описані у live props
  • оновлює дані при надходженні подій

6. Де й як транслювати подію оновлення

Коли користувач або бекграунд-процес змінює статус замовлення, потрібно відправити подію OrderUpdated. Автор наводить базовий приклад:

Приклад коду в оригіналі

У цьому випадку всі вкладки, де відкрита сторінка поточного замовлення, отримають подію, і Inertia зробить partial reload prop order. Але вкладка користувача, який зробив зміну, зазвичай уже бачить нові дані у відповіді на свій запит. Щоб не дублювати оновлення для цього клієнта, можна скористатися toOthers():

Приклад коду в оригіналі

Це викликає трансляцію на Reverb всім підписникам каналу, крім клієнта з відповідним socket ID. Inertia автоматично передає socket ID у заголовку X-Socket-Id з кожним запитом до того ж домену, і Laravel Broadcasting використовує його, щоб визначити, кого вважати «тим самим» клієнтом.

7. Запуск Reverb і черги подій

Події з інтерфейсом ShouldBroadcast за замовчуванням відправляються в чергу. Тому, окрім налаштованого Reverb-сервера, потрібен ще queue worker.

Для запуску Reverb автор використовує:

Приклад коду в оригіналі

У нових додатках Laravel composer run dev зазвичай уже запускає queue:work. Якщо ж ви не користуєтеся цим скриптом, worker треба запустити самостійно:

Приклад коду в оригіналі

Якщо є потреба виконувати трансляцію синхронно, в ході HTTP-запиту, а не через чергу, можна реалізувати ShouldBroadcastNow замість ShouldBroadcast. Це корисно у середовищах без налаштованих черг, але варто памʼятати про потенційне збільшення часу відповіді.

8. Індикація перезавантаження props на фронтенді

Щоб користувач бачив, що live prop зараз оновлюється, Inertia пропонує хук useProp(). Автор ілюструє це на прикладі Vue-компонента сторінки замовлення:

Приклад коду в оригіналі

Тут:

  • defineProps(['order']) оголошує звичайний prop з даними замовлення
  • const { loading } = useProp('order') повертає реактивний прапорець loading саме для цього prop
  • у шаблоні умовно виводиться повідомлення «Updating...», коли loading істинний

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

9. Зменшення навантаження: передача значення в події

За замовчуванням кожна подія, повʼязана з live prop, спричиняє окремий HTTP-запит до сервера для кожної відкритої сторінки. Якщо сторінок багато, це стає відчутним навантаженням. Автор пропонує оптимізацію: приєднати до події готові дані для props через broadcastWith().

У прикладі дані замовлення готуються через OrderResource, а потім пропускаються через Inertia::broadcastProps():

Приклад коду в оригіналі

Особливості такого підходу:

  • дані для props прилітають разом з подією Reverb, Inertia просто оновлює стан на клієнті без додаткового запиту до серверного контролера
  • Inertia::broadcastProps() формує payload у тому ж форматі, в якому props приходять при звичайному рендері
  • оновлюються тільки ті props, які явно слухають дану подію — подія не може зробити «приховане» оновлення якихось сторонніх даних на сторінці

Обмеження цього методу важливе: усі користувачі, які слухають канал, отримують однаковий payload. Тому автор застерігає — не застосовувати таке оновлення для значень, які залежать від конкретного користувача (наприклад, індивідуальних прав або приватних даних, відмінних для кожного слухача).

10. Контроль частоти оновлень live props

Ще одна потенційна проблема — надто часті оновлення. Inertia за замовчуванням застосовує ліміт: кожен live prop перезавантажується не частіше ніж раз на секунду. Для джерел з інтенсивними подіями (лог активності, показники в реальному часі) автор показує, як явно задати більший інтервал.

У прикладі prop activity оновлюється не частіше, ніж раз на 5 секунд (5000 мс):

Приклад коду в оригіналі

Тут:

  • throttle: 5000 задає мінімальний інтервал між перезавантаженнями для цього конкретного live prop
  • значення береться з $order->activity, але також слухає подію ActivityLogged

Також Inertia ставить перезавантаження на паузу, коли вкладка браузера прихована, і застосовує їх, коли користувач повертається. Водночас значення, які приходять безпосередньо в payload через broadcastProps(), оновлюються незалежно від того, активна вкладка чи ні.

11. Підсумкова схема для сторінки замовлення

У підході, який описує автор, сторінка замовлення з live props працює так:

  • на бекенді налаштовані Reverb, Echo і черги
  • подія OrderUpdated транслюється в приватний канал orders.{id}, доступний лише власнику замовлення
  • контролер повертає сторінку з live prop order, який знає про цей канал
  • на фронтенді Inertia ініціалізується з live: echo(), автоматично підписується на канал і оновлює prop при надходженні події
  • опційно: подія містить готові дані для props через broadcastWith(), а частота оновлень обмежується параметром throttle

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


Джерело: Paul Redmond — Laravel News

Пошук

Закрити