Skip to content

Concepts

Start with what the caller needs back. Actions do something, filters return one changed value, and collectors return every callback’s contribution. All three are synchronous and ordered.

Type Listener input Listener return Empty-listener behavior Use it for
Action The explicit variadic invocation arguments Ignored doAction() returns void Ordered side effects such as notifications or audit writes
Filter The current value first, then explicit variadic arguments Becomes the next current value applyFilters() returns the original value Sequential value transformation
Collector The explicit variadic invocation arguments One raw result per listener collect() returns [] Independent contributions that the caller can inspect or process

All three types support priorities. The default is 10; lower numbers run first, and equal priorities keep registration order. Each registration returns a handle, so you can remove that exact callback later.

An action callback receives the arguments passed to doAction(). Its return value is intentionally ignored:

use Magdicom\Hooks;
$hooks = new Hooks();
$hooks->addAction('invoice.paid', static function (int $invoiceId): void {
// Notify a synchronous integration.
});
$hooks->doAction('invoice.paid', 42);

With no listeners, doAction() simply returns void. Use an action when the caller needs to trigger work, not when it needs a transformed value or a list of results.

The current value is always the first callback argument. Any additional arguments passed to applyFilters() follow it. Each callback’s return value becomes the value given to the next callback:

use Magdicom\Hooks;
$hooks = new Hooks();
$hooks->addFilter('email.subject', static function (string $title, string $locale): string {
return $locale === 'en' ? $title : '[' . $locale . '] ' . $title;
});
$displayTitle = $hooks->applyFilters('email.subject', 'Release notes', 'en');

When no filter is registered, applyFilters() returns the value it was given. Priorities determine order; equal-priority filters run in registration order.

A collector invokes each listener with the explicit arguments and returns one raw array entry per listener:

use Magdicom\Hooks;
$hooks = new Hooks();
$hooks->addCollector('dashboard.widgets', static fn (string $userId): array => ['owner' => $userId]);
$hooks->addCollector('dashboard.widgets', static fn (string $userId): array => ['count' => 3]);
$cards = $hooks->collect('dashboard.widgets', 'user-42');
// [['owner' => 'user-42'], ['count' => 3]]

collect() leaves the raw results alone. It does not flatten or merge them, and returns [] when there are no listeners. If you need to reduce or format those results, use process() or render(); see processors and renderers.

  • Need to tell listeners that something happened and ignore their results? Use an action.
  • Need one value refined by a sequence of listeners? Use a filter.
  • Need every listener’s contribution separately? Use a collector.
  • Need domain events, queueing, broadcasting, or Laravel workflow behavior? Use Laravel Events.
  • Need a fixed middleware-style chain? Use Laravel Pipeline.

The full dispatch, priority, mutation, registration, and removal semantics are documented on the individual hook-type pages.