
Package Demo
GitHub Repository
Installation
Install the package via composer:
composer require sjwatts119/livewire-wizard
Creating Your First Wizard
To make your first wizard component, run the following command:
php artisan make:wizard
This command supports any arguments used in the Livewire Make Command.
A new wizard class and a corresponding step view will have been created. The class will look like this:
<?php namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; class YourWizard extends Wizard { public function wizardSteps(): array { return [ WizardStep::make( title: 'Step 1', view: view('step-1'), ), ]; } public function render(): View { return $this ->currentStep() ->view(); } }
Creating Wizard Steps
To create a new Wizard Step, you can add a new WizardStep instance in the wizardSteps() array.
A WizardStep accepts:
- title: The name of the step. This must be unique from your other steps.
- view: The view to be rendered when the step is active.
- canNavigate (Optional): A boolean closure, prevents access to a step unless the closure evaluates to true. For more information, see Defining Step Rules.
Here is an example of a contact form wizard with two steps:
<?php namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; class YourWizard extends Wizard { public function wizardSteps(): array { return [ WizardStep::make( title: 'Your Message', view: view('livewire.wizard.message'), ), WizardStep::make( title: 'Your Details', view: view('livewire.wizard.details'), ), ]; } public function render(): View { return $this ->currentStep() ->view(); } }
Defining Step Rules
As mentioned previously, a WizardStep can accept a canNavigate closure. This closure will be called when the step is rendered, and should return a boolean value.
The wizard class exposes a helpful method validatePropertiesForStep() which can be used to validate any number of class properties. Internally, this uses Livewire's Validation to validate the properties, and returns a boolean value indicating whether the validation passed for all provided properties.
This validation also works with Livewire Form Object Properties. These should be referenced using dot notation, e.g. form.name.
For example, the following step will only be accessible if the validation rules defined on the name and message properties pass:
<?php namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; class YourWizard extends Wizard { #[Validate('required')] public string $name = ''; #[Validate('required')] public string $message = ''; public function wizardSteps(): array { return [ // Previous steps... WizardStep::make( title: 'Confirm', view: view('livewire.wizard.confirm'), canNavigate: fn () => $this->validatePropertiesForStep(['name', 'message']), ), ]; } public function render(): View { return $this ->currentStep() ->authorise() // Validate the 'name' and 'message' properties if we are on step 'Confirm'. ->view(); } }
Rendering The Wizard
If you'd like to retrieve the view for the current step, you can call:
$this->currentStep()->view();
If any of your steps have canNavigate closures, you can run these before rendering the view:
$this->currentStep() ->authorise() ->view();
The authorise() method will abort with a 403 response if the closure returns false.
Optionally, you can pass a false boolean value to the aborts parameter of authorise().
$this->currentStep() ->authorise(aborts: false) ->view();
This will cause a StepNotAuthorisedException to be thrown when the canNavigate closure returns false instead of aborting the request. You could then gracefully handle this exception using Livewire's Exception Lifecycle Hook. For example:
namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; use SamWatts\LivewireWizard\Exceptions\StepNotAuthorisedException; class YourWizard extends Wizard { /* * Gracefully handle the exception. */ public function exception($e, $stopPropagation) { if ($e instanceof StepNotAuthorisedException) { $this->notify('Post is not found'); $stopPropagation(); } } public function wizardSteps(): array { return [ // Steps with canNavigate closures... ]; } public function render(): View { return $this ->currentStep() ->authorise(aborts: false) // Throws StepNotAuthorisedException if rules fail... ->view(); } }
Navigation
In your step views, you likely want to add some navigation buttons to allow the user to move between steps.
You could use the wire:click directive to call any of the navigation methods on the wizard class. For example:
<!-- Navigating to a specific step --> <x-wizard.nav.item :current="$currentStep->is($step)" :disabled="!$step->canNavigate()" wire:click="navigateToStep('{{ $step->title() }}')" > {{ $step->title() }} </x-wizard.nav.item> <!-- Navigating to the next step --> <x-wizard.nav.item :disabled="!$nextStep->canNavigate()" wire:click="navigateToNextStep()" > Next </x-wizard.nav.item> <!-- Navigating to the previous step --> <x-wizard.nav.item :disabled="!$previousStep->canNavigate()" wire:click="navigateToPreviousStep()" > Back </x-wizard.nav.item>
Want To Read On?
The whole readme file can be found on this project's GitHub Repository.