First, make sure you have the following installed:

Install Verbs

Install Verbs using composer:

1composer require hirethunk/verbs

Publish and Run Migrations

The last thing you need to do before you use Verbs is run migrations:

1php artisan vendor:publish --tag=verbs-migrations
2php artisan migrate

Firing your first event

To generate an event, use the built in artisan command

1php artisan verbs:event CustomerBeganTrial

This will generate an event in the app/Events directory of your application. You can then begin customizing your event to suit your needs:

1class CustomerBeganTrial extends Event
3 public function __construct(
4 public int $customer_id,
5 ) {}
7 public function handle()
8 {
9 // Your event handler logic will go here.
10 }

You can now fire this event anywhere in your code using:

1CustomerBeganTrial::fire(customer_id: 1);

Using handle() to write model data

Every Verbs event comes with a handle() method which can be used to respond to events. A common use case for this is creating or updating Eloquent models based on an event. Lets generate new Subcription model for our customer in the handle() method of our event.

1class CustomerBeganTrial extends Event
3 public function __construct(
4 public int $customer_id,
5 ) {}
7 public function handle()
8 {
9 Subscription::create([
10 'customer_id' => $this->customer_id,
11 'expires_at' => now()->addDays(30),
12 ]);
13 }

Compiling data and validating events using states

States in Verbs are simple PHP objects containing data which is mutated over time by events.

Lets assume we want to prevent a customer from signing up for a free trial if they have already signed up for one in the past year. We can store a latest_trial_started_at timestamp to a CustomerState when they sign up. We can then check that timestamp each time the CustomerBeganTrial event is fired to validate that the customer is allowed to start a trial.

We can begin by creating a new state using:

1php artisan verbs:state CustomerState

This will create a CustomerState class in our app/States directory. We can customize it to add our timestamp.

1class CustomerState extends State
3 public Carbon|null $latest_trial_started_at = null;

We can now add a few things to our event to take advantage of our new state.

  • We can add a #[StateId(CustomerState::class) attribute to our $customer_id property telling Verbs that we want to look up the CustomerState using this ID.
  • We can add a validate() method which accepts an instance of CustomerState. If the validate method returns true, the event can be fired. If it returns false or throws an exception, the event will not be fired.
  • We can add an apply() method which accepts an instance of CustomerState to mutate the state when our event fires.
1class CustomerBeganTrial extends Event
3 public function __construct(
4 #[StateId(CustomerState::class)]
5 public int $customer_id,
6 ) {}
8 public function validate(CustomerState $state)
9 {
10 return $state->latest_trial_started_at === null
11 || $state->last_trial_started_at->diffInDays() > 365
12 }
14 public function handle()
15 {
16 Subscription::create([
17 'customer_id' => $this->customer_id,
18 'expires_at' => now()->addDays(30),
19 ]);
20 }