How to Handle Multi-Step Form Validation in Laravel

When I first started working with Laravel, handling multi-step forms felt overwhelming. I wanted to create a form that could collect user data across multiple steps, validate each step, and store it securely without confusing the user. After experimenting, I discovered that Laravel’s validation and session features make this process straightforward.

In this article, I’ll share a simple, beginner-friendly guide on how to build and validate a multi-step form in Laravel. Whether you’re creating a registration form or a complex survey, this step-by-step guide will help you get it done efficiently.

Step-by-Step Guide to Multi-Step Form Validation in Laravel

How to Handle Multi-Step Form Validation in Laravel

Step 1: Set Up Your Laravel Project

To get started, I create a new Laravel project using Composer. If you don’t have Laravel installed, run this command in your terminal:

composer create-project laravel/laravel multi-step-form

Once the project is set up, I navigate to the project directory and ensure my database is configured in the .env file. For this example, I’ll use MySQL, but you can use any database Laravel supports.

Step 2: Create the Database and Model

Next, I create a model and migration for storing form data. For this example, let’s assume I’m building a user registration form with fields like name, email, phone, and address split across multiple steps.

Run this command to create a UserDetail model with a migration:

php artisan make:model UserDetail -m

In the migration file (located in database/migrations), I define the schema:

Schema::create('user_details', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->string('phone')->nullable();
    $table->string('address')->nullable();
    $table->timestamps();
});

Then, I run the migration:

php artisan migrate

Step 3: Create Routes for the Multi-Step Form

I define routes to handle each step of the form. In routes/web.php, I add:

use App\Http\Controllers\FormController;

Route::get('/form/step1', [FormController::class, 'showStep1'])->name('form.step1');
Route::post('/form/step1', [FormController::class, 'postStep1'])->name('form.step1.post');
Route::get('/form/step2', [FormController::class, 'showStep2'])->name('form.step2');
Route::post('/form/step2', [FormController::class, 'postStep2'])->name('form.step2.post');
Route::get('/form/confirmation', [FormController::class, 'confirmation'])->name('form.confirmation');

Step 4: Create the Form Controller

I create a controller to handle the form logic:

php artisan make:controller FormController

In app/Http/Controllers/FormController.php, I add methods to display and process each step. Here’s a simplified version:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\UserDetail;
use Illuminate\Support\Facades\Session;

class FormController extends Controller
{
    // Show Step 1
    public function showStep1()
    {
        return view('form.step1');
    }

    // Process Step 1
    public function postStep1(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:user_details,email',
        ]);

        // Store data in session
        Session::put('form_data', $request->only('name', 'email'));

        return redirect()->route('form.step2');
    }

    // Show Step 2
    public function showStep2()
    {
        return view('form.step2');
    }

    // Process Step 2
    public function postStep2(Request $request)
    {
        $request->validate([
            'phone' => 'nullable|string|max:15',
            'address' => 'nullable|string|max:255',
        ]);

        // Merge step 2 data with session data
        $formData = Session::get('form_data');
        $formData = array_merge($formData, $request->only('phone', 'address'));

        // Save to database
        UserDetail::create($formData);

        // Clear session
        Session::forget('form_data');

        return redirect()->route('form.confirmation');
    }

    // Show Confirmation
    public function confirmation()
    {
        return view('form.confirmation');
    }
}

Step 5: Create the Form Views

I create Blade views for each step in the resources/views/form directory.

Step 1 (step1.blade.php):

<!DOCTYPE html>
<html>
<head>
    <title>Step 1 - Multi-Step Form</title>
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h2>Step 1: Personal Information</h2>
        <form method="POST" action="{{ route('form.step1.post') }}">
            @csrf
            <div>
                <label>Name</label>
                <input type="text" name="name" value="{{ old('name') }}">
                @error('name') <span>{{ $message }}</span> @enderror
            </div>
            <div>
                <label>Email</label>
                <input type="email" name="email" value="{{ old('email') }}">
                @erroremail') <span>{{ $message }}</span> @enderror
            </div>
            <button type="submit">Next</button>
        </form>
    </div>
</body>
</html>

Step 2 (step2.blade.php):

<!DOCTYPE html>
<html>
<head>
    <title>Step 2 - Multi-Step Form</title>
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h2>Step 2: Contact Information</h2>
        <form method="POST" action="{{ route('form.step2.post') }}">
            @csrf
            <div>
                <label>Phone</label>
                <input type="text" name="phone" value="{{ old('phone') }}">
                @error('phone') <span>{{ $message }}</span> @enderror
            </div>
            <div>
                <label>Address</label>
                <input type="text" name="address" value="{{ old('address') }}">
                @error('address') <span>{{ $message }}</span> @enderror
            </div>
            <button type="submit">Submit</button>
        </form>
    </div>
</body>
</html>

Confirmation (confirmation.blade.php):

<!DOCTYPE html>
<html>
<head>
    <title>Form Submitted</title>
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h2>Thank You!</h2>
        <p>Your form has been submitted successfully.</p>
    </div>
</body>
</html>

Step 6: Add Basic Styling

To make the form look clean, I add some basic CSS in resources/css/app.css:

.container {
    max-width: 600px;
    margin: 50px auto;
    padding: 20px;
}
form div {
    margin-bottom: 15px;
}
label {
    display: block;
    margin-bottom: 5px;
}
input {
    width:  regno:1px; 
    padding: 8px;
    border: 1px solid #ccc;
}
span {
    color: red;
}
button {
    padding: 10px 20px;
    background-color: #007bff;
    color: white;
    border: none;
    cursor: pointer;
}

Then, I compile the CSS:

npm install && npm run dev

Step 7: Test the Form

I start the Laravel server:

php artisan serve

Then, I visit http://localhost:8000/form/step1 to test the form. I fill out each step, check for validation errors, and ensure the data is saved to the database.

Conclusion

Building a multi-step form in Laravel was easier than I expected, thanks to Laravel’s powerful validation and session management features. By breaking the form into steps, storing data in sessions, and validating each step, I created a user-friendly experience that’s both secure and efficient. This approach is perfect for complex forms like registrations or surveys. With this guide, you can create your own multi-step form and customize it to fit your project’s needs.

FAQs

1. Why use a multi-step form instead of a single form?
Multi-step forms break down long forms into smaller, manageable steps, improving user experience and reducing form abandonment.

2. How do I prevent users from skipping steps?
You can use middleware or session checks in your controller to ensure users complete each step in order before proceeding.

3. Can I add more steps to the form?
Yes, simply add more routes, controller methods, and views for additional steps, following the same session-based approach.

4. How do I handle validation errors in multi-step forms?
Laravel’s validation system automatically returns errors to the view, which you can display using @error directives in Blade templates.

5. Is it secure to store form data in sessions?
Yes, Laravel’s session handling is secure, but ensure sensitive data is encrypted or cleared after submission for added security.


You might also like :

techsolutionstuff

Techsolutionstuff | The Complete Guide

I'm a software engineer and the founder of techsolutionstuff.com. Hailing from India, I craft articles, tutorials, tricks, and tips to aid developers. Explore Laravel, PHP, MySQL, jQuery, Bootstrap, Node.js, Vue.js, and AngularJS in our tech stack.

RECOMMENDED POSTS

FEATURE POSTS