Chain of Responsibility (Ланцюжок обов'язків) - запит проходить послідовністю обробників. Кожен вирішує: обробити запит і зупинити ланцюжок, або щось зробити й передати далі.
Middleware Laravel - саме такий ланцюжок:
final class EnsureUserIsSubscribed
{
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed()) {
return redirect()->route('billing'); // зупинити ланцюжок
}
$response = $next($request); // передати далі
$response->headers->set('X-Plan', $request->user()->plan); // після обробки
return $response;
}
}
$next($request)- виклик наступного обробника; код до нього виконується на шляху запиту, після - на шляху відповіді;- повернення відповіді без
$next- ланцюжок зупиняється (автентифікація, CSRF, обмеження частоти, режим обслуговування); - обробники не знають один про одного - лише про
$next.
Під капотом - Illuminate\Pipeline\Pipeline, який можна використати й для власних процесів:
$order = Pipeline::send($order)
->through([
ValidateStock::class,
ApplyDiscounts::class,
CalculateTaxes::class,
ReserveItems::class,
])
->thenReturn();
Кожен крок - клас з методом handle($order, Closure $next). Кроки легко переставити, додати, протестувати окремо.
Переваги:
- відокремлення відправника від обробників;
- набір і порядок обробників - конфігурація, а не код (групи middleware, порядок
->through()); - кожен обробник з однією відповідальністю.
Пастки:
- порядок має значення: middleware автентифікації після middleware, що використовує користувача, - помилка. Laravel має
$middleware->priority()для впорядкування; - ніхто не обробив: у класичному варіанті запит може пройти весь ланцюжок без обробки - потрібен обробник за замовчуванням;
- налагодження: довгий ланцюжок з побічними ефектами важко відстежити - кожен крок має бути простим;
- продуктивність: глобальні middleware виконуються на кожен запит, тож у них не місце важким операціям.
Інші приклади: обробники винятків, валідатори з кількома правилами, системи знижок, ланцюжки фільтрів запитів (пошук з фільтрами - кожен фільтр додає умову й передає далі).
Докладніше в документації: Refactoring.Guru: Chain of Responsibility