A web setup wizard for Laravel application.
This package is adapted from smajti1/laravel-wizard.
- Laravel Wizard
Laravel Wizard | Laravel | PHP |
---|---|---|
1.0.x | 5.5 | ^7.0 |
1.1.x | ^5.6 | ^7.1.3 |
2.0.x,2.1.x | ^5.6 | ^6.x | ^7.1.3 |
2.2.x | ^5.6 | ^6.x | ^7.x | ^7.1.3 |
2.3.x | >=5.6 | <=9.0 | >=7.1.3 | <=8.2 |
3.x | >=9.0 | >=8.1 |
4.x | >=11.0 | >=8.2 |
Install the package via composer:
composer require ycs77/laravel-wizard
Publish config:
php artisan vendor:publish --tag=wizard-config
Now you can qckly generate the wizard controller and the wizard steps:
php artisan make:wizard User NameStep,EmailStep
This command generates the UserWizardController
, NameStep
, and EmailStep
class, and appends the wizard route to routes/web.php
.
routes/web.php
use App\Http\Controllers\UserWizardController;
use Illuminate\Support\Facades\Route;
use Ycs77\LaravelWizard\Facades\Wizard;
...
Wizard::routes('/wizard/user', UserWizardController::class, 'wizard.user');
If you can't use auto append route, you can set
config/wizard.php
attributeappend_route
tofalse
.
This is generated NameStep class, you can to model
method set the model, to rules
method set form validation, and save $data
to your database via the saveData
method, for example:
app/Steps/User/NameStep.php
<?php
namespace App\Steps\User;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Ycs77\LaravelWizard\Step;
class NameStep extends Step
{
/**
* The step slug.
*
* @var string
*/
protected $slug = 'name';
/**
* The step show label text.
*
* @var string
*/
protected $label = 'Name';
/**
* Set the step model instance or the relationships instance.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation|null
*/
public function model(Request $request)
{
return User::find(1);
}
/**
* Save this step form data.
*
* @param \Illuminate\Http\Request $request
* @param array|null $data
* @param \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation|null $model
* @return void
*/
public function saveData(Request $request, $data = null, $model = null)
{
$data = Arr::only($data, 'name');
$model->update($data);
}
/**
* Validation rules.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function rules(Request $request)
{
return [
'name' => 'reqred',
];
}
}
And add some steps view, for example:
resources/views/steps/user/name.blade.php
<div class="form-group mb-3">
<label for="name">Name</label>
<input type="text" name="name" id="name" @class(['form-control', 'is-invalid' => $errors->has('name')]) value="{{ old('name', $step->data('name')) }}">
@error('name')
<span class="invalid-feedback">{{ $message }}</span>
@enderror
</div>
resources/views/steps/user/email.blade.php
<div class="form-group mb-3">
<label for="email">E-mail</label>
<input type="email" name="email" id="email" @class(['form-control', 'is-invalid' => $errors->has('email')]) value="{{ old('email', $step->data('email')) }}">
@error('email')
<span class="invalid-feedback">{{ $message }}</span>
@enderror
</div>
Next, browse the URL /wizard/user
, and start to use the Laravel Wizard.
If you want to get the layout you can copy Laravel layouts/app.blade.php to
resources/views/layouts/app.blade.php
The CSS for this package default view is based on the Bootstrap Steps, use NPM installation to use:
npm install bootstrap bootstrap-steps
// or yarn
yarn add bootstrap bootstrap-steps
Import to the app.scss
file and run npm run dev
or yarn run dev
:
resources/sass/app.scss
...
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-steps/scss/bootstrap-steps';
To use the database
wizard cache driver, you will need a database table to hold the cache data of the wizard. Generate a migration that creates this table, and runs the wizard:table
Artisan command:
php artisan wizard:table
php artisan migrate
Set cache
in config/wizard.php
to false
to disable cache input data:
'cache' => false,
Or set it to your WizardController wizardOptions
property:
protected $wizardOptions = [
'cache' => false,
];
If disabled cache, the data will be saved in the data immediately after each step is sent. If you are afraid to save the data repeatedly, you can hide the Prev button, or use Model::updateOrCreate()
(https://laravel.com/docs/6.x/eloquent#other-creation-methods).
Add wizardOptions
property to your wizard controller, you can use the cache
, driver
, connection
, and table
options to override configuration.
app/Http/Controllers/UserWizardController.php
/**
* The wizard options.
*
* @var array
*/
protected $wizardOptions = [
'cache' => true,
'driver' => 'session',
'table' => 'wizards',
];
This package layout view uses Bootstrap 5, but if you don't want to use default views, you can publish views to custom it:
php artisan vendor:publish --tag=wizard-views-bs5
If you used Bootstrap 4, you could publish the layouts:
php artisan vendor:publish --tag=wizard-views-bs4
If you used Tailwind CSS, you could publish the layouts:
php artisan vendor:publish --tag=wizard-views-tailwind
Now you can customize resources/views/vendor/wizard/*.blade.php
in your Laravel project.
But if you want a custom-only one wizard view base view, you can copy the views from resources/views/vendor/wizard/*.blade.php
to resources/views/wizards/user/*.blade.php
. (user
is wizardName
property value on your wizard controller),
For example, FirstStep
has name
and email
fields, and SecondStep
has age
and phone
fields. you can use the data
method of step to get step data:
$name = $firstStep->data('name');
// 'Lucas'
$data = $secondStep->data();
// ['age' => '30', 'phone' => '0900111222']
Or you can use the step repository to get other step data:
$data = $secondStep->find('first')->data();
// ['name' => 'Lucas']
$name = $secondStep->find('first')->data('name');
// 'Lucas'
If you want to manually redirect to another step, you can use the redirectToStep()
of the wizard, it will return a redirect response.
// given a step slug
return $wizard->redirectToStep('second');
// given a step insatnce
return $wizard->redirectToStep($secondStep);
Step repository saves all steps data, if you want to use another step, you need to use it:
From wizard:
$stepRepo = $wizard->stepRepo();
From step:
$stepRepo = $step->getRepo();
Get the previous step:
$prevStep = $step->prev();
// same as:
$prevStep = $step->getRepo()->prev();
Get the next step:
$prevStep = $step->next();
// same as:
$nextStep = $step->getRepo()->next();
Step repository all can use method detailed reference: src/StepRepository.php
Since v2.3.3 upload files in Cache and No Cache are supported, if use the Cache Mode you can cache all input data and uploaded files to save in the last step:
<?php
class LastStep extends Step
{
public function model(Request $request)
{
return $request->user();
}
public function saveData(Request $request, $data = null, $model = null)
{
$data = [
'avatar' => $this->find('has-avatar-step')->data('avatar'),
];
$data['avatar'] = $data['avatar']->store('avatar', ['disk' => 'public']);
$model->update($data);
}
}
Then add a step view to upload the avatar image:
resources/views/steps/user/has-avatar.blade.php
<div class="form-group mb-3">
<label for="avatar">Avatar</label>
<input type="file" name="avatar" id="avatar" @class(['form-control', 'is-invalid' => $errors->has('avatar')])>
@error('avatar')
<span class="invalid-feedback">{{ $message }}</span>
@enderror
</div>
Note: v2.3.3+
To make Step skippable, set the $skip
property to true
, then this Step will skip the validation and save data:
app/Steps/User/NameStep.php
<?php
class NameStep extends Step
{
/**
* Is it possible to skip this step.
*
* @var boolean
*/
protected $skip = true;
}
Because each step is injected into the view of the step, so just add the method to return the data in the step class. For example, pass the data of the select options to view:
app/Steps/User/NameStep.php
<?php
class NameStep extends Step
{
public function getOptions()
{
return [
'Taylor',
'Lucas',
];
}
}
resources/views/steps/user/name.blade.php
<div class="form-group mb-3">
<label for="name">Select name</label>
<select id="name" name="name" @class(['form-control', 'is-invalid' => $errors->has('name')])>
<option value="">Select...</option>
@foreach ($step->getOptions() as $option)
<option
value="{{ $option }}"
@selected($option === old('name', $step->data('name')))
>
{{ $option }}
</option>
@endforeach
</select>
@error('name')
<span class="invalid-feedback">{{ $message }}</span>
@enderror
</div>
The getOptions
method is custom and can be changed at will.
Suppose there are now two Steps NameStep
and EmailStep
.
First, don't set the Model for all Steps, but don't use the last one:
app/Steps/User/NameStep.php
<?php
class NameStep extends Step
{
public function model(Request $request)
{
//
}
public function saveData(Request $request, $data = null, $model = null)
{
//
}
}
Next, receive all the data in the last Step and save the Model:
app/Steps/User/EmailStep.php
<?php
class EmailStep extends Step
{
public function model(Request $request)
{
return new User();
}
public function saveData(Request $request, $data = null, $model = null)
{
$data = $this->getStepsData();
$model->fill($data)->save();
}
}
Similarly, you can set the relationships model in the model
method of the step.
use Illuminate\Support\Arr;
/**
* Set the step model instance or the relationships instance.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation|null
*/
public function model(Request $request)
{
return $request->user()->posts();
}
/**
* Save this step form data.
*
* @param \Illuminate\Http\Request $request
* @param array|null $data
* @param \Illuminate\Database\Eloquent\Model\Illuminate\Database\Eloquent\Relations\Relation|null $model
* @return void
*/
public function saveData(Request $request, $data = null, $model = null)
{
$data = Arr::only($data, ['title', 'content']);
$model->create($data);
}
If you don't keep the cache on click the start wizard button, you can change the link to /wizard/user?_reset=1
.
If you have any custom cache data you want to clean up, you can set the clean up into cleanUpWizard()
hook in your WizardController
, then it will clean on wizard done or visit route with ?_reset=1
parameter:
app/Http/Controllers/UserWizardController.php
/**
* Clean up the wizard event.
*
* @return void
*/
protected function cleanUpWizard(Request $request)
{
// Cleanup wizard cache...
}
Make wizard:
The make:wizard
command and make:wizard:controller
command difference, is make:wizard
command will append the route and not confirm generate step.
php artisan make:wizard User NameStep,EmailStep
Make controller:
The make:wizard:controller
command only generates the WizardController
, NameStep
, and EmailStep
class.
php artisan make:wizard:controller UserController --steps=NameStep,EmailStep
Make step:
php artisan make:wizard:step NameStep
With step label and wizard:
php artisan make:wizard:step NameStep --label="Name" --slug=name --wizard=user
Add custom view path:
php artisan make:wizard:step NameStep --label="Name" --slug=name --view=steps.user.name --wizard=user
If you think this package has helped you, please consider Becoming a sponsor to support my work~ and your avatar will be visible on my major projects.