---
title: "Preparing a model — Onboarding for Laravel | Roundly"
description: "Add GetsOnboarded to a model to read its flow, choose its default key and use null-safe one-line readers."
url: https://roundly-consulting.com/open-source/docs/onboarding-for-laravel/model-trait
language: en
---

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

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

# 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:

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

class User extends Model
{
    use GetsOnboarded;
}
```

## Reading a model’s flow

```php
$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', $fallback);  // a copy of $fallback bound to $user 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. A fallback flow is copied before it is bound, so two models sharing one never overwrite each other’s subject.

## 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:

```php
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():

```php
$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
```

| Method | Returns | Purpose |
| --- | --- | --- |
| `onboarding(?string $key = null, ?Flow $default = null)` | `?Flow` | A copy of the flow under the key — without one, the resolver’s choice, else defaultOnboardingKey()’s — bound to the model; otherwise a bound copy of $default. |
| `resolvedOnboarding()` | `?Flow` | Onboarding::for($model) — the resolver’s flow, else the default flow. |
| `defaultOnboardingKey()` | `string` | The 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)` | `bool` | Flow complete; false when no flow is registered. |
| `isOnboarding(?string $key = null)` | `bool` | Flow in progress; false when no flow is registered. |
| `onboardingProgress(?string $key = null)` | `float` | percentageCompleted(); 0.0 when no flow is registered. |
| `nextOnboardingStep(?string $key = null)` | `?Step` | The current step to resume on. |
| `dismissOnboardingStep(string $step, ?string $key = null)` | `void` | Dismiss an optional, dismissible step; a no-op without a store or for an unknown, required or non-dismissible step. |

## In a Blade view

```blade
@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](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/onboarding-for-laravel.md)
