Laravel wizard is an easy way to create a clear way of conditional steps.
You can install the package via composer:
composer require lenderspender/laravel-wizard
A step is where the logic and view for that particular step is defined. Steps can also be conditional.
You are able to inject any dependencies you need into the step's view
and store
methods. The Laravel service container will automatically inject all dependencies that are type-hinted.
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Http\Request;
use LenderSpender\LaravelWizard\StepDetails;
use LenderSpender\LaravelWizard\WizardStep;
use Symfony\Component\HttpFoundation\Response;
class FirstStep extends WizardStep
{
public function view(): Response
{
return view('steps.first-step');
}
public function store(Request $request): void
{
$data = $request->validate([
'foo' => 'required',
]);
SomeModel::update(['foo' => $data['foo']]);
}
public function getStepDetails(): StepDetails
{
return new StepDetails('First step', 'This is the first step', 'first-step');
}
public function isCompleted(?Authenticatable $user): bool
{
return true;
}
public function isRequired(?Authenticatable $user): bool
{
return true;
}
}
Sometimes you wish to throw additional errors. When the StoreStepException
is thrown from the store
method in your step.
Users are automatically redirected back to the previous page with errors.
use LenderSpender\LaravelWizard\Exceptions\StoreStepException;
public function store(Authenticatable $user)
{
if (! $user->emailVerified()) {
throw new StoreStepException('Email address is not yet verified');
}
$user->update(['foo' => 'bar']);
}
Create a new Controller that will handle your steps.
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Contracts\Auth\Authenticatable;
use LenderSpender\LaravelWizard\Wizard;
use Symfony\Component\HttpFoundation\Response;
class WizardController
{
public function show(string $step, Authenticatable $user): Response
{
return $this->getWizard($user)
->view($step);
}
public function store(string $step, Authenticatable $user): Response
{
$wizard = $this->getWizard($user);
if ($redirect = $wizard->store($step)) {
return $redirect;
}
return redirect(action(
[WizardController::class, 'show'],
[
'step' => (string) $wizard->nextStep($wizard->getStepFromSlug($step)),
]
));
}
private function getWizard(Authenticatable $user): Wizard
{
return new Wizard(
[
new FirstStep(),
new SecondStep(),
],
false,
$user
);
}
}
Then you should define your routes:
Route::get('/wizard/{step}', [WizardController::class, 'show']);
Route::post('/wizard/{step}', [WizardController::class, 'store']);
A new wizard can be initialized by creating a new Wizard
object.
$wizard = new Wizard([
FirstStep::class,
new FirstStep($param),
[FirstStep::class => ['param' => $param]]
]);