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

Preparing a model

Add the GetsOnboarded trait to any Eloquent model that should expose its onboarding flow. The trait asks the OnboardingManager for a copy of the model’s flow bound to the model, so every step’s rule receives that model — and Onboarding::fake() sees every trait call:

use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Onboarding\Traits\GetsOnboarded;

class User extends Model
{
    use GetsOnboarded;
}

Reading a model’s flow

$user = User::first();

$flow = $user->onboarding();                      // the resolver's flow, else defaultOnboardingKey()'s, bound to $user
$flow = $user->onboarding('admin');               // a specific flow by key
$flow = $user->onboarding('missing', new Flow()); // fallback when the key is unknown

onboarding() returns null when no matching flow is registered and no fallback is given — hence the null-safe ?-> throughout these docs.

Choosing the default key

By default the model uses the default flow key. Override defaultOnboardingKey() to choose a different one — for example, from a column on the model. When a resolver is registered, its choice comes first and defaultOnboardingKey() is the fallback for a null or unknown answer:

public function defaultOnboardingKey(): string
{
    return $this->onboarding_flow ?? 'default';
}

Null-safe readers

The trait adds one-liners so you don’t have to chain through onboarding()?->… yourself. Each accepts an optional flow key; without one it reads the same flow as $user->onboarding() — the resolver’s choice, else defaultOnboardingKey():

$user->hasCompletedOnboarding();        // bool — false when no flow is registered
$user->isOnboarding();                  // bool — in progress
$user->onboardingProgress();            // float — 0.0 when no flow is registered
$user->nextOnboardingStep();            // ?Step
$user->dismissOnboardingStep('add-bio'); // dismiss an optional, dismissible step (needs a store)
$user->hasCompletedOnboarding('admin'); // target a specific flow key
MethodReturnsPurpose
onboarding(?string $key = null, ?Flow $default = null)?FlowA copy of the flow under the key — without one, the resolver’s choice, else defaultOnboardingKey()’s — bound to the model.
resolvedOnboarding()?FlowOnboarding::for($model) — the resolver’s flow, else the default flow.
defaultOnboardingKey()stringThe model’s fallback flow key when the resolver answers null or an unknown key — OnboardingManager::$default (default) unless you override it.
hasCompletedOnboarding(?string $key = null)boolFlow complete; false when no flow is registered.
isOnboarding(?string $key = null)boolFlow in progress; false when no flow is registered.
onboardingProgress(?string $key = null)floatpercentageCompleted(); 0.0 when no flow is registered.
nextOnboardingStep(?string $key = null)?StepThe current step to resume on.
dismissOnboardingStep(string $step, ?string $key = null)voidDismiss an optional, dismissible step; a no-op without a store or for an unknown or non-dismissible step.

In a Blade view

@if ($user->isOnboarding())
    <div class="onboarding-banner">
        <progress value="{{ $user->onboardingProgress() }}" max="100"></progress>
        <p>Next: {{ $user->nextOnboardingStep()?->resolvedTitle() }}</p>
    </div>
@endif

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.