Skip to content

Events

The engine dispatches Symfony events at every lifecycle transition so consumers (mail, payment, workflow, logging) can react without the core knowing they exist. Event names are constants on \Drupal\yoyaku\Event\BookingEvents.

Event catalogue

Constant Name Event class When
HOLD_REQUESTED booking.hold_requested BookingHoldRequestedEvent Before a hold - vetoable.
HELD booking.held BookingEvent After a hold is created.
CONFIRMED booking.confirmed BookingEvent After confirm().
RELEASED booking.released BookingEvent After release().
EXPIRED booking.expired BookingEvent After a hold expires.
CANCELLED booking.cancelled BookingEvent After cancel().
HOLD_EXTENDED booking.hold_extended BookingEvent After extendHold().

BookingEvent exposes the readonly ->booking. Transition events fire after the transaction commits (the row lock is released first), so subscribers can safely do slow work.

Where they fire

sequenceDiagram
    participant Caller
    participant BM as BookingManager
    participant D as Dispatcher

    Caller->>BM: hold()
    BM->>D: HOLD_REQUESTED (vetoable)
    alt denied
        D-->>BM: deny(reason)
        BM-->>Caller: BookingException
    else allowed
        BM->>BM: txn + lock + reserve + COMMIT
        BM->>D: HELD
        BM-->>Caller: booking
    end

    Caller->>BM: confirm()
    BM->>BM: txn + lock + COMMIT
    BM->>D: CONFIRMED

Vetoing a hold

Subscribe to HOLD_REQUESTED and call deny() to block a hold for any reason the built-in capacity check does not cover. The category, requested quantity and subject are available on the event.

use Drupal\yoyaku\Event\BookingEvents;
use Drupal\yoyaku\Event\BookingHoldRequestedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class MyBookingRules implements EventSubscriberInterface {

  public static function getSubscribedEvents(): array {
    return [BookingEvents::HOLD_REQUESTED => 'onHoldRequested'];
  }

  public function onHoldRequested(BookingHoldRequestedEvent $event): void {
    if ($event->quantity > 4) {
      $event->deny('At most 4 places per booking.');
    }
  }

}

Tip

For reusable, configurable rules prefer a constraint plugin; use the vetoable event for one-off or contextual logic.

Reacting to a confirmed booking

use Drupal\yoyaku\Event\BookingEvent;
use Drupal\yoyaku\Event\BookingEvents;

public static function getSubscribedEvents(): array {
  return [BookingEvents::CONFIRMED => 'onConfirmed'];
}

public function onConfirmed(BookingEvent $event): void {
  $booking = $event->booking;
  // Send a confirmation email, issue a ticket, notify a workflow…
}