infinitypaul/laravel-multistep-forms

Multistep form functionality for Laravel, with the ability to persist data for each step, navigate back and forward, prevent accessing future steps and more.

0.0.1 2020-06-20 19:46 UTC

This package is auto-updated.

Last update: 2024-10-27 23:54:16 UTC


README

Latest Version on Packagist Build Status Quality Score Total Downloads

Hi Fellas! So you know how you would like to create a dynamic registration form but then you can't because you feel this is impossible with PHP.

Well, I have good news for ya, this is so POSSIBLE with this package. Yeah that's right, I mean it. Let's get down on the "how":

So we will be working with a 3 step form:

Installation

You can install the package via composer:

composer require infinitypaul/laravel-multistep-forms

Usage

After installing the package, I will be creating 3 blades for the different steps of the form:

Step 1: Create the blades for the form.

1.blade.php

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

    @extends('layouts.app')

    @section('content')

        <div class="container">
            <div class="row justify-content-center">
                <div class="col-md-8">
                    <div class="card">
                        <div class="card-header">{{ __('Register') }} </div>
                        <div class="card-body">

                            <form method="POST" action="{{ route('auth.register.1.store') }}">
                                @csrf

                                <div class="form-group row">
                                    <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>

                                    <div class="col-md-6">
                                        <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') ?: $step->name }}" required autocomplete="name" autofocus>

                                        @error('name')
                                        <span class="invalid-feedback" role="alert">
                                            <strong>{{ $message }}</strong>
                                        </span>
                                        @enderror
                                    </div>


                                </div>

                                <div class="form-group row">
                                    <label for="middle" class="col-md-4 col-form-label text-md-right">{{ __('Middle Name') }}</label>
                                    <div class="col-md-6">
                                        <input id="text" type="text" class="form-control @error('middle') is-invalid @enderror" name="middle" value="{{ old('middle') ?: $step->middle }}" required autocomplete="email" autofocus>

                                        @error('middle')
                                        <span class="invalid-feedback" role="alert">
                                            <strong>{{ $message }}</strong>
                                        </span>
                                        @enderror
                                    </div>
                                </div>

                                <div class="form-group row mb-0">
                                    <div class="col-md-6 offset-md-4">
                                        <button type="submit" class="btn btn-primary">
                                            {{ __('Next') }}
                                        </button>
                                    </div>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>

    @endsection

2.blade.php

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">{{ __('Register') }}</div>

                    <div class="card-body">
                        <form method="POST" action="{{ route('auth.register.2.store') }}">
                            @csrf

                            <div class="form-group row">
                                <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                                <div class="col-md-6">
                                    <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') ?: $step->email }}" required autocomplete="email">

                                    @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                    @enderror
                                </div>
                            </div>

                            <div class="form-group row mb-0">
                                <div class="col-md-6 offset-md-4">
                                    <button type="submit" class="btn btn-primary">
                                        {{ __('Next') }}
                                    </button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

3.blade.php

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">{{ __('Register') }}</div>

                    <div class="card-body">
                        <form method="POST" action="{{ route('auth.register.3.store') }}">
                            @csrf


                            <div class="form-group row">
                                <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                                <div class="col-md-6">
                                    <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

                                    @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                    @enderror
                                </div>
                            </div>

                            <div class="form-group row mb-0">
                                <div class="col-md-6 offset-md-4">
                                    <button type="submit" class="btn btn-primary">
                                        {{ __('Finish') }}
                                    </button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Step 2: Create the controller for the each form.

After creating the blade views for each of the forms, p.s: I created them in a folder "register". We'll be heading to the controller, so in app\Http\Controllers\Auth, we would be creating a folder "Register" i.e our path will be "app\Http\Controllers\Auth\Register". In the Register folder, we would be creating 3 controllers for the three steps:

RegisterControllerStep1.php

<?php

namespace App\Http\Controllers\Auth\Register;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Infinitypaul\MultiStep\MultiStep;


class RegisterControllerStep1 extends Controller
{
    public function index(){

        $step  =MultiStep::step('auth.register', 1);
        return view('auth.register.1', compact('step'));
    }

    public function store(Request $request){
        MultiStep::step('auth.register', 1)->store(['name' => $request->name, 'middle' => $request->middle])->complete();
        return redirect()->route('auth.register.2.index');
    }
}

RegisterControllerStep2.php

<?php

namespace App\Http\Controllers\Auth\Register;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Infinitypaul\MultiStep\MultiStep;

class RegisterControllerStep2 extends Controller
{
    public function index(){
        $step  =MultiStep::step('auth.register', 2);
        return view('auth.register.2', compact('step'));
    }


    public function store(Request $request){
        MultiStep::step('auth.register', 2)->store($request->only('email'))->complete();

        return redirect()->route('auth.register.3.index');
    }
}

RegisterControllerStep3.php

<?php

namespace App\Http\Controllers\Auth\Register;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Infinitypaul\MultiStep\MultiStep;


class RegisterControllerStep3 extends Controller
{
    public function index(){
        $step = MultiStep::step('auth.register', 3);
        if($step->notCompleted(1)){
            return redirect()->route('auth.register.1.index');
        }
        return view('auth.register.3');
    }

    public function store(MultiStep $multiStep, Request $request){
        MultiStep::step('auth.register', 3)->store($request->only('password'))->complete();

        MultiStep::clearAll();
    }
}

Step 3: Routing!

Let's move on to the route, in our web.php, we will include this:

Route::multistep('auth/register', 'Auth\Register\RegisterController')
    ->steps('3')
    ->name('auth.register')
    ->only(['index', 'store']);

We're done guys!!!

So if I head to {URL}/auth/register/1, I would see this: Form 1

When I click on next, it takes me to {URL}/auth/register/2 and this will display: Form 2

On clicking on next, we get {URL}/auth/register/3: Form 3

Its a wrap! Well done guys!!!

Testing

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Bugs & Fixtures

If you have spotted any bugs, or would like to request additional features from the library, please file an issue via the Issue Tracker on the project's Github page: https://github.com/infinitypaul/laravel-multistep-forms/issues.

Security

If you discover any security related issues, please email infinitypaul@live.com instead of using the issue tracker.

License

The MIT License (MIT). Please see License File for more information.