NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Because the package is stateless it can’t detect transitions on its own — but it announces the current truth when you ask. Call record() at your own checkpoint and it dispatches StepCompleted for every complete visible step and FlowCompleted when the whole flow is complete. Reads never dispatch; only record() does.

use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Onboarding\Events\FlowCompleted;
use RoundlyConsulting\Onboarding\Events\StepCompleted;
use RoundlyConsulting\Onboarding\Facades\Onboarding;

Onboarding::for($user)?->record();   // announce every complete step (+ FlowCompleted when done)
$user->onboarding()?->record();      // the same through the trait

Pass a step key to announce just that step, if it is visible and complete. It fires FlowCompleted only when that completion finishes the flow — the step is required, was announced (not already recorded in a store), and the flow is now complete. An unknown, incomplete or optional step never fires it:

Onboarding::for($user)?->record('upload-photo');   // announce just one step

Payloads

EventPropertiesFired when
StepCompleted$step, $forrecord() announces a complete step that the store has not already recorded.
FlowCompleted$flow, $forrecord() runs while the whole flow is complete; record($key) only when it announced that required step and the flow is now complete.

Both events live under RoundlyConsulting\Onboarding\Events and carry the bound subject in $for — a Model, any Authenticatable, or null.

Listening

Event::listen(StepCompleted::class, function (StepCompleted $e): void {
    // $e->step (Step), $e->for (the subject — Model, Authenticatable or null)
});

Event::listen(FlowCompleted::class, function (FlowCompleted $e): void {
    // $e->flow (Flow), $e->for (the subject)
});

A checkpoint in a controller

public function update(Request $request): RedirectResponse
{
    $request->user()->update([
        'avatar_path' => $request->file('photo')->store('avatars'),
    ]);

    // a checkpoint of your choosing: announce the step this request may have completed
    Onboarding::for($request->user())?->record('upload-photo');

    return back();
}

Without a persistence store, every record() call re-announces every complete step. Plug in a store with Onboarding::useStore() to get once-only StepCompleted events — see Persistence store.

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

By donating, you agree to our donation terms.

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.