Why Use Laravel Form Request and How? (BIG Code improvement)

When creating applications that receive user input, validating form data is crucial for security and data quality. Laravel can apply validation rules either inline in the controller or by applying rules defined in a Form Request class.

In this article, we’ll learn how to apply our validation rules using a FormRequest class instead of relying on inline validation inside a controller.

The main advantage of using a Form Request class is that it helps minimize the code in the Controller. Additionally, the validation rules defined in the Form Request class can be easily reused not only for validating browser forms but also for API calls and even input from a custom artisan command.

The difference in code is easy to see, especially when using many validation rules as shown in the image below:

Example Showing general Request Validation (Left) versus Form Request Validation (Right)

Let’s dive in and explore how exactly we can achieve this improvement.

Step 1: Create Model and Migration

First, create the Book model and migration. Open your terminal and run the following commands:

php artisan make:model Book --migration

This will generate a Book model in the app/Models directory and a corresponding migration file in the database/migrations directory. Open the migration file and define the table schema according to your needs. For example, the migration file may look like this:

database/migrations/2023_05_22_190633_create_books_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('author');
            $table->integer('no_pages')->nullable();
            $table->date('publish_date');
            $table->string('genre')->nullable();
            $table->string('isbn')->nullable();
            $table->string('type');
            $table->string('cover_type')->nullable();
            $table->decimal('price', 4, 2);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('books');
    }
};

Now run the migration using:

php artisan migrate

Step 2: Enable Mass Assignment

To enable mass assignment for the Book model, open the Book.php file in the app/Models directory. Add the $fillable property and specify the attributes that are allowed to be mass assigned. For our example, it may look like this:

App/Models/Book.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'author',
        'no_pages',
        'publish_date',
        'genre',
        'isbn',
        'type',
        'cover_type',
        'price',
    ];
}

Step 3: Creating the Form Request Class

To create the Form Request class for book validation, use the make:request Artisan command. Open your terminal and run the following command:

php artisan make:request StoreBookRequest

This will generate a StoreBookRequest.php file in the app/Http/Requests directory. Open the StoreBookRequest.php file and update the rules() method to define the validation rules for the book attributes.

Open the newly created StoreBookRequest class and define the validation rules as follows:

app/Http/Requests/StoreBookRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreBookRequest extends FormRequest
{
    public function rules()
    {
        return [
            'title' => 'required|string|max:255',
            'author' => 'required|string',
            'no_pages' => 'integer|min:1',
            'publish_date' => 'required|date',
            'genre' => 'nullable|string',
            'isbn' => 'nullable|digits:13',
            'type' => 'required|in:ebook,book',
            'cover_type' => 'required_if:type,book',
            'price' => 'required|numeric|min:0',
        ];
    }
}

Step 4: Create the Controller

Now, let’s create the BookController and add its store method. Run the command below to generate the controller:

php artisan make:controller BookController

Now, add the store() method to your controller using our Form Request class StoreBookRequest as shown in the code below:

app/Http/Controllers/BookController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreBookRequest;
use App\Models\Book;

class BookController extends Controller
{
    public function store(StoreBookRequest $request)
    {
        // .. when our code reached this far, the validation was succesful

        Book::create($request->validated());

        // Redirect or perform additional actions as needed
        return redirect()->route('books.index');
    }
}

It’s important to note that the store function in our Controller accepts the $request object of type StoreBookRequest. This object is automatically instantiated by Laravel’s Dependency Injection mechanism. When a Form Request is injected in this manner, Laravel automatically applies the validation rules defined in the rules() method of the corresponding class. As a result, the Controller does not require any additional code to trigger the validation process.

In the following steps, we will create a form to test our code in the browser.

Step 5: Create a Form to Store a Book

Now, let’s create the form view for storing a book. Open a view file located at resources/views/books/create.blade.php and add the following form code:

resources/views/books/create.blade.php

<h1 class="title">Add a Book</h1>

@if(session()->has('success'))
    <p>
        {{ session()->get('success') }}
    </p>
@endif

@if ($errors->any())
    <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif

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

    <table>
        <tr>
            <td>Title</td>
            <td><input type="text" name="title" value=""></td>
        </tr>
        <tr>
            <td>Author</td>
            <td><input type="text" name="author" value=""></td>
        </tr>
        <tr>
            <td>No. pages</td>
            <td><input type="text" name="no_pages" value=""></td>
        </tr>
        <tr>
            <td>Publish date</td>
            <td><input type="date" name="publish_date" value=""></td>
        </tr>
        <tr>
            <td>Genre</td>
            <td><input type="text" name="genre" value=""></td>
        </tr>
        <tr>
            <td>ISBN</td>
            <td><input type="text" name="isbn" type="number" value=""></td>
        </tr>
        <tr>
            <td>Type</td>
            <td>
                <select name="type">
                    <option value="">- Please select -</option>
                    <option value="ebook">eBook</option>
                    <option value="book">Book</option>
                </select>
            </td>
        </tr>
        <tr>
            <td>Cover type</td>
            <td>
                <select name="cover_type">
                    <option value="">- Please select -</option>
                    <option value="ebook">Hardcover</option>
                    <option value="book">Paperback</option>
                </select>
            </td>
        </tr>
        <tr>
            <td>Price</td>
            <td><input type="text" name="price" value=""></td>
        </tr>
    </table>

    <button type="submit">Save Book</button>
</form>

Step 6: Display the Book Creation Form

To display the book creation form, add a route and a corresponding method in your BookController.php file:

app/Http/Controllers/BookController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreBookRequest;
use App\Models\Book;

class BookController extends Controller
{
    // .. other functions

    public function create()
    {
        return view('books.create');
    }
}

Update your routes/web.php file with the following route definition:

routes/web.php
<?php

use App\Http\Controllers\BookController;
use Illuminate\Support\Facades\Route;

Route::get('/books/create', [BookController::class, 'create'])->name('books.create');
Route::post('/books/store', [BookController::class, 'store'])->name('books.store');

We can now view our form:

Screenshot of Our Form With Values Filled

And, when validation passes, save it:

Screenshot of Our Form With Success Message After Saving

That’s it! You’ve successfully implemented Laravel Form Request classes for validating and storing a book in your application.

Conclusion

Using Laravel Form Request classes helps a great deal to improve code organization, reusability, and maintainability. By centralizing your validation rules and separating them from your controller logic, you can create cleaner, more readable code and ensure consistent data validation across different contexts, like browser forms, API calls, and custom artisan commands.

By using Laravel’s features, such as mass assignment and FormRequest classes, you can improve the quality of your applications while reducing lines of code and avoiding duplication.

Hopefully, you’ve gained some new insights along the way which will help you develop excellent applications. Happy coding!

Johan van den Broek

Johan is the creator of laracoding.com. As a child, he began tinkering with various programming languages, many of which have been long forgotten today. Currently, he works exclusively with PHP and Laravel, and his passion for programming remains to this day.

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Posts