wazum/slug-cascade-contracts

PSR-14 event interface for slug-cascade situations.

Maintainers

Package info

github.com/wazum/slug-cascade-contracts

pkg:composer/wazum/slug-cascade-contracts

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-21 05:24 UTC

This package is auto-updated.

Last update: 2026-05-22 15:09:30 UTC


README

PSR-14 event interface for slug-cascade situations.

An interface-only package. A producer dispatches an event implementing the interface when a parent page's slug has been (or is about to be) changed and descendants need their slugs adjusted. A consumer subscribes to the interface and can short-circuit synchronous propagation by calling deferCascade().

Install

composer require wazum/slug-cascade-contracts

PHP 8.1+. No framework dependency; works with any PSR-14 event dispatcher (TYPO3 v12/v13/v14, Symfony, Laminas, …).

The contract

namespace Wazum\SlugCascadeContracts;

use Psr\EventDispatcher\StoppableEventInterface;

interface SlugCascadeEvent extends StoppableEventInterface
{
    public function getPageId(): int;
    public function getOldParentSlug(): string;
    public function getNewParentSlug(): string;
    public function getCorrelationId(): string;
    public function deferCascade(): void;
}

deferCascade() flips isPropagationStopped() to true. The dispatch site reads that flag and skips its own synchronous cascade when a consumer has taken over.

Producer side

namespace Acme\Producer\Event;

use Wazum\SlugCascadeContracts\SlugCascadeEvent;

final class SlugChanged implements SlugCascadeEvent
{
    private bool $cascadeDeferred = false;

    public function __construct(
        public readonly int $pageId,
        public readonly string $oldParentSlug,
        public readonly string $newParentSlug,
        public readonly string $correlationId,
    ) {}

    public function getPageId(): int { return $this->pageId; }
    public function getOldParentSlug(): string { return $this->oldParentSlug; }
    public function getNewParentSlug(): string { return $this->newParentSlug; }
    public function getCorrelationId(): string { return $this->correlationId; }

    public function deferCascade(): void { $this->cascadeDeferred = true; }
    public function isPropagationStopped(): bool { return $this->cascadeDeferred; }
}

Dispatch site:

$event = new SlugChanged($pageId, $oldSlug, $newSlug, $correlationId);
$this->eventDispatcher->dispatch($event);
if ($event->isPropagationStopped()) {
    return;
}
$this->rebuildChildSlugsSynchronously(...);

Consumer side

namespace Acme\Consumer\EventListener;

use Wazum\SlugCascadeContracts\SlugCascadeEvent;

final class HandleCascade
{
    public function __invoke(SlugCascadeEvent $event): void
    {
        // … handle the cascade …
        $event->deferCascade();
    }
}

Service registration (TYPO3):

Acme\Consumer\EventListener\HandleCascade:
  tags:
    - name: event.listener
      event: Wazum\SlugCascadeContracts\SlugCascadeEvent

License

GPL-2.0-or-later