Шаблоны топиков¶
Топик¶
Топик — имя события из сегментов через точку. Аналог URL для событий: путь /order/42/paid ↔ топик order.42.paid.
shop.order.42.paid
│ │ │ └─ что произошло
│ │ └──── какой именно заказ
│ └────────── о какой сущности
└─────────────── какая система
Правила для имени события: непустые сегменты, без пробелов, без символов *, #, {, }, регистр важен. Нарушение — InvalidEventName.
Сегменты шаблона¶
В маршруте пишется не точное имя, а шаблон — он описывает целую группу событий.
| Сегмент | Что совпадает | Пример |
|---|---|---|
order |
ровно этот текст, с учётом регистра | order |
{order_id} |
любой один сегмент, значение попадает в параметр | 42 → order_id = '42' |
{order_id:\d+} |
один сегмент, целиком подходящий под регулярное выражение | 42, но не abc и не 42abc |
* |
любой один сегмент | 42, abc |
# |
ноль или больше любых сегментов | ничего, 42, 42.payment.failed |
use Selyusize\EventsRouter\Topic\TopicPattern;
$cases = [
['order.{order_id}.paid', 'order.42.paid'],
['order.{order_id:\d+}.paid', 'order.abc.paid'],
['order.*.cancelled', 'order.42.cancelled'],
['order.*.cancelled', 'order.42.items.cancelled'],
['order.#', 'order'],
['order.#', 'order.42.payment.failed'],
['order.{order_id}.payment.#', 'order.42.payment.failed'],
];
foreach ($cases as [$pattern, $topic]) {
$params = TopicPattern::fromString($pattern)->match($topic);
echo str_pad($pattern, 28), str_pad($topic, 26), match (true) {
$params === null => 'нет',
$params === [] => 'да',
default => 'да, ' . json_encode($params),
}, PHP_EOL;
}
order.{order_id}.paid order.42.paid да, {"order_id":"42"}
order.{order_id:\d+}.paid order.abc.paid нет
order.*.cancelled order.42.cancelled да
order.*.cancelled order.42.items.cancelled нет
order.# order да
order.# order.42.payment.failed да
order.{order_id}.payment.# order.42.payment.failed да, {"order_id":"42"}
Параметры¶
| Правило | Пример |
|---|---|
имя — snake_case: строчные латинские буквы, цифры и _, начинается с буквы |
{order_id}, {v2}; не {orderId}, не {_id} |
ограничение — regex после двоеточия, совпадает с сегментом целиком, ^ и $ не нужны |
{order_id:\d+}, {code:\d{3}} |
| Unicode в ограничении поддерживается | {name:\p{L}+} |
| один параметр — один раз на шаблон | user.{id}.order.{id} — ошибка |
| пустой сегмент не совпадает | order..paid не подходит под order.{order_id}.paid |
Альтернатива в ограничении тоже работает: {status:paid|cancelled}.
Значение параметра — строка. Слушатель получает её через $event->getAttribute('order_id').
Литеральные сегменты библиотека не ограничивает: события Bitrix называются OnAfterUserAdd. Для своих событий пишите snake_case: order.unpaid_reminder.
* и #¶
| Шаблон | Совпадает | Не совпадает |
|---|---|---|
order.*.cancelled |
order.42.cancelled |
order.cancelled, order.42.items.cancelled |
order.# |
order, order.created, order.42.payment.failed |
shop.order |
order.#.failed |
order.failed, order.42.payment.failed |
order.42.paid |
# |
любое событие | — |
# работает как в AMQP: может стоять в любом месте и совпадать с нулём сегментов.
Ограничения¶
Параметр, * и # занимают весь сегмент. order-{order_id} и ord* писать нельзя. Сегмент, который нужно разобрать, ловится параметром с regex: {order_ref:order-\d+}.
Ошибка в шаблоне видна сразу при объявлении маршрута, а не когда придёт событие:
use Selyusize\EventsRouter\Exception\InvalidTopicPattern;
use Selyusize\EventsRouter\Topic\TopicPattern;
try {
TopicPattern::fromString('order-{order_id}.paid');
} catch (InvalidTopicPattern $error) {
echo $error->getMessage(), PHP_EOL;
}
Некорректный шаблон топика "order-{order_id}.paid": параметр в сегменте "order-{order_id}" должен занимать весь сегмент, например order.{order_id}. См. https://selyusize.github.io/events-router/errors/invalid-topic-pattern/
Все варианты — на странице InvalidTopicPattern.