---
title: "DI and actions — Events for Laravel | Roundly"
description: "Skip the facade: inject EventsManager or call one of the 49 single-purpose actions — every execute() signature with its facade path."
url: https://roundly-consulting.com/open-source/docs/events-for-laravel/without-facade
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Events for Laravel](https://roundly-consulting.com/open-source/docs/events-for-laravel.md)

# DI and actions

The facade is the recommended default, not the only way in. Every write is an action, the manager exposes each one and the facade is sugar over the manager — three entry points that run the same code: the Events facade (shortest); RoundlyConsulting\\Events\\EventsManager — the facade’s root, a container singleton — injected through the constructor (same API, explicit dependency, no static calls); and the actions, final readonly classes with one execute() for composing into your own jobs and commands:

```php
use RoundlyConsulting\Events\Actions\Events\CancelEventAction;
use RoundlyConsulting\Events\EventsManager;
use RoundlyConsulting\Events\Facades\Events;

// Facade
Events::for($event)->by($organizer)->cancel('Venue flooded');

// Dependency injection: the same API
public function __construct(private EventsManager $events) {}
$this->events->for($event)->by($organizer)->cancel('Venue flooded');

// The raw action (no handle policy check, no fake recording)
app(CancelEventAction::class)->execute($event, actor: $organizer, reason: 'Venue flooded');

// Model traits delegate to the manager
$user->rsvp($event)->going();
```

## Inject the manager

```php
use Illuminate\Http\Request;
use RoundlyConsulting\Events\EventsManager;
use RoundlyConsulting\Events\Models\Event;
use RoundlyConsulting\Events\Models\Rsvp;

final class JoinEventController
{
    public function __construct(private EventsManager $events) {}

    public function __invoke(Request $request, Event $event): Rsvp
    {
        // by() checks the event policy (rsvp) for the signed-in user
        return $this->events->for($event)->by($request->user())
            ->rsvp($request->user())
            ->going(tier: $request->input('tier'), guests: (int) $request->input('guests', 0));
    }
}
```

The manager is safe on Octane: it holds no container and resolves every action from the current one on each call, so a binding you make — or a request sandbox makes — applies.

## Call an action

```php
use RoundlyConsulting\Events\Actions\Rsvps\PromoteWaitlistAction;
use RoundlyConsulting\Events\Actions\Venues\GeocodeVenueAction;

// Trusted code (a job, a command): no handle policy check and no fake recording
$promoted = app(PromoteWaitlistAction::class)->execute($event, limit: 5);
app(GeocodeVenueAction::class)->execute($venue, force: true);
```

Calling an action directly skips the handle’s policy check and the fake’s recording. A few actions check the policy themselves when you pass an actor: CreateEventAction (create, and that the organizer allows the actor), the venue actions (the venue policy) and CheckInByCodeAction (the unscoped scan). The status actions’ $system flag runs the lifecycle as the system — what a handle without by() does.

## Facade method → action

The 49 host-facing actions; namespaces are relative to RoundlyConsulting\\Events\\Actions:

| Action | execute(…) | Facade path |
| --- | --- | --- |
| `Events\CreateEventAction` | `(CreateEventData $data): Event` | `Events::create(), make()->create() / ->publish()` |
| `Events\UpdateEventAction` | `(Event $event, UpdateEventData $data, ?Model $actor = null, bool $silent = false): Event` | `->update(), ->edit()->save()` |
| `Events\PublishEventAction` | `(Event $event, ?Model $actor = null, bool $system = false, bool $silent = false): TransitionResult` | `->publish()` |
| `Events\UnpublishEventAction` | same shape | `->unpublish()` |
| `Events\PostponeEventAction` | `(Event $event, ?Model $actor = null, bool $system = false, ?string $reason = null, bool $silent = false): TransitionResult` | `->postpone()` |
| `Events\RescheduleEventAction` | `(Event $event, CarbonInterface\|string $startsAt, CarbonInterface\|string\|null $endsAt = null, ?string $reason = null, ?Model $actor = null, bool $system = false, bool $silent = false): Event` | `->reschedule()` |
| `Events\CancelEventAction` | `(Event $event, ?Model $actor = null, bool $system = false, ?string $reason = null, bool $silent = false): TransitionResult` | `->cancel()` |
| `Events\CompleteEventAction` | `(Event $event, ?Model $actor = null, bool $system = false, bool $silent = false): TransitionResult` | `->complete()` |
| `Events\TransitionEventAction` | `(Event $event, string $transition, ?Model $actor = null, bool $system = false, ?string $reason = null, array $payload = [], bool $silent = false): TransitionResult` | `->transition()` |
| `Events\MoveEventOnlineAction` | `(Event $event, string $url, bool $keepVenue = false, ?Model $actor = null, bool $silent = false): Event` | `->moveOnline()` |
| `Events\TransferOrganizerAction` | `(Event $event, Model&Organizer $organizer, bool $keepAsHost = false, ?Model $actor = null): Event` | `->transferTo()` |
| `Events\SetPosterAction` | `(Event $event, UploadedFile\|string\|null $file = null, ?string $draft = null, ?Model $actor = null): Event` | `->poster()` |
| `Events\DeleteEventAction` | `(Event $event, ?Model $actor = null, bool $force = false): void` | `->delete()` |
| `Events\RestoreEventAction` | `(Event $event, ?Model $actor = null): Event` | `->restore()` |
| `Tags\CategorizeEventAction` | `(Event $event, Tag\|int\|string\|null $category, ?Model $actor = null, bool $silent = false): TagChanges` | `->categorize()` |
| `Tags\AttachLabelsAction` | `(Event $event, mixed $labels, ?Model $actor = null, bool $silent = false): TagChanges` | `->labels()->attach()` |
| `Tags\DetachLabelsAction` | same shape | `->labels()->detach()` |
| `Tags\SyncLabelsAction` | same shape | `->labels()->sync()` |
| `Venues\CreateVenueAction` | `(VenueData $data): Venue` | `Events::venues()->create(), ->make()->create()` |
| `Venues\UpdateVenueAction` | `(Venue $venue, UpdateVenueData $data, ?Model $actor = null): Venue` | `venues()->update()` |
| `Venues\DeleteVenueAction` | `(Venue $venue, ?Model $actor = null): void` | `venues()->delete()` |
| `Venues\RestoreVenueAction` | `(Venue $venue, ?Model $actor = null): Venue` | `venues()->restore()` |
| `Venues\GeocodeVenueAction` | `(Venue $venue, bool $force = false, ?Model $actor = null): Venue` | `venues()->geocode()` |
| `Tiers\CreateTierAction` | `(Event $event, TierData $data, ?Model $actor = null): AdmissionTier` | `->tiers()->create() / add() / make()->create()` |
| `Tiers\UpdateTierAction` | `(Event $event, AdmissionTier\|int\|string $tier, UpdateTierData $data, ?Model $actor = null): AdmissionTier` | `->tiers()->update()` |
| `Tiers\DeleteTierAction` | `(Event $event, AdmissionTier\|int\|string $tier, ?Model $actor = null): void` | `->tiers()->remove()` |
| `Tiers\RestoreTierAction` | `(Event $event, AdmissionTier\|int\|string $tier, ?Model $actor = null): AdmissionTier` | `->tiers()->restore()` |
| `Tiers\ReorderTiersAction` | `(Event $event, array $order): void` | `->tiers()->reorder()` |
| `Rsvps\RespondToEventAction` | `(Event $event, RsvpRequest $request): Rsvp` | `->rsvp($a)->going() / interested() / notGoing(), $user->rsvp($event)` |
| `Rsvps\AddAttendeeAction` | `(Event $event, Model $attendee, AdmissionTier\|int\|string\|null $tier = null, int $guests = 0, bool $overCapacity = false, ?Model $actor = null): Rsvp` | `->attendees()->add()` |
| `Rsvps\RemoveAttendeeAction` | `(Event $event, Rsvp\|Model $who, ?Model $actor = null): Rsvp` | `->attendees()->remove()` |
| `Rsvps\ChangeTierAction` | `(Event $event, Rsvp\|Model $who, AdmissionTier\|int\|string $tier, ?Model $actor = null): Rsvp` | `->attendees()->changeTier()` |
| `Rsvps\ChangeGuestsAction` | `(Event $event, Rsvp\|Model $who, int $guests, ?Model $actor = null): Rsvp` | `->attendees()->changeGuests()` |
| `Rsvps\ConfirmRsvpAction` | `(Event $event, Rsvp\|Model $who, ?string $reference = null, ?Model $actor = null): Rsvp` | `->attendees()->confirm(), Events::rsvps()->confirm()` |
| `Rsvps\ConfirmPaymentAction` | `(string $reference): Rsvp` | `Events::rsvps()->confirmPayment()` |
| `Rsvps\ReleaseHoldAction` | `(Event $event, Rsvp\|Model $who, ?Model $actor = null): Rsvp` | `->attendees()->release(), Events::rsvps()->release()` |
| `Rsvps\RequestAdmissionPaymentAction` | `(Rsvp $given): ?PaymentRequest` | `Events::rsvps()->requestPayment()` |
| `Rsvps\PromoteWaitlistAction` | `(Event $event, ?int $limit = null): Collection` | `->waitlist()->promote()` |
| `CheckIns\CheckInByCodeAction` | `(string $input, ?Event $event = null, ?Model $by = null, ?int $guests = null, bool $ignoreWindow = false): CheckInResult` | `->checkIn(), Events::checkIn()` |
| `CheckIns\CheckInAttendeeAction` | `(Event $event, Rsvp\|Model $who, ?int $guests = null, ?Model $actor = null): CheckInResult` | `->attendees()->checkIn()` |
| `CheckIns\UndoCheckInAction` | `(Event $event, Rsvp\|Model $who, ?Model $actor = null): Rsvp` | `->attendees()->undoCheckIn()` |
| `CheckIns\RotateCheckInCodeAction` | `(Rsvp $rsvp): Rsvp` | `Events::rsvps()->rotateCode()` |
| `Hosts\AddCoHostAction` | `(Event $event, Model&Organizer $host, ?Model $actor = null): bool` | `->hosts()->add()` |
| `Hosts\RemoveCoHostAction` | `(Event $event, Model $host, ?Model $actor = null): bool` | `->hosts()->remove()` |
| `Invitations\SendInvitationAction` | `(Event $event, Model $invitee, ?string $message = null, ?Model $actor = null): EventInvitation` | `->invitations()->send()` |
| `Invitations\RevokeInvitationAction` | `(Event $event, Model $invitee, ?Model $actor = null): EventInvitation` | `->invitations()->revoke()` |
| `Invitations\RespondToInvitationAction` | `(string $token, RsvpResponse $response = RsvpResponse::Going, ?Model $by = null, bool $system = false): Rsvp` | `Events::invitations()->accept() / decline()` |
| `Maintenance\SweepEventsAction` | `(?SweepOptions $options = null): SweepResult` | `Events::sweep()` |
| `Maintenance\RepairEventsAction` | `(?RepairOptions $options = null): RepairReport` | `Events::repair()` |

- The manager resolves each action from the container on every call, so a host binding of an action applies to every path — the facade, an injected manager, a handle, a sub-handle, a builder and a model trait.
- Events::fake() swaps the manager behind the facade and in the container, so an injected EventsManager is faked too. A direct action call is not recorded.
- The building blocks under Actions\\Internal — seat changes, automatic promotion, hold expiry, reminder claims, code issuing, the deletion hook, schedule resolution, co-host writes and the like — are @internal and not part of the public API.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/events-for-laravel.md)
