Skip to content

Chapter 2.6: Data Validation

Time to learn: 50 minutes


1. Validation: A Cosmic-Scale Shield

Validation is the process of checking incoming data against a set of rules. Without it:

  • 🚀 Incorrect data could "destroy" your database
  • 🌌 Attackers could inject malicious code
  • 🪐 Users would receive confusing errors

💡 Space Analogy: Validation = The space station's defense system:

  • It checks the "cargo" (data) before docking
  • It rejects dangerous objects
  • It filters out space debris

2. Where to Validate in a Laravel API

The main approaches are:

  1. In the Controller (quick, but clutters the code)
  2. Form Request (recommended, clean architecture)

3. Validation in the Controller

We use the validate() method of the Request object:

<?php
public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required|string|max:255|unique:planets',
        'description' => 'required|string',
        'size_km' => 'required|integer|min:100|max:500000',
        'solar_system' => 'required|string|max:100',
        'image_url' => 'nullable|url|max:2048',
        'is_habitable' => 'boolean'
    ]);

    // ... create the planet
}

Popular Validation Rules:

Rule Description Example
required The field is mandatory 'name' => 'required'
string Must be a string value 'description' => 'string'
integer Must be an integer 'size_km' => 'integer'
min:value Minimum value/length 'size_km' => 'min:100'
max:value Maximum value/length 'name' => 'max:255'
unique:table,column Must be unique in the table 'name' => 'unique:planets'
url Must be a valid URL 'image_url' => 'url'
boolean true/false/1/0 'is_habitable' => 'boolean'

4. Custom Error Messages

Change the default error messages:

<?php
$validated = $request->validate(
    [
        'name' => 'required|unique:planets',
        'size_km' => 'min:1000'
    ],
    [
        'name.required' => 'The planet name is required!',
        'name.unique' => 'A planet with this name already exists in the catalog',
        'size_km.min' => 'The planet diameter cannot be less than 1000 km'
    ]
);

Example response on error (automatically 422 Unprocessable Entity):

{
    "message": "The given data was invalid.",
    "errors": {
        "name": ["A planet with this name already exists in the catalog"],
        "size_km": ["The planet diameter cannot be less than 1000 km"]
    }
}


5. Creating a Form Request

For complex validation, we create a dedicated class:

Step 1: Generation

php artisan make:request StorePlanetRequest

Step 2: Edit app/Http/Requests/StorePlanetRequest.php

<?php
public function authorize()
{
    return true; // Usually true for APIs
}

public function rules()
{
    return [
        'name' => 'required|string|max:255|unique:planets',
        'description' => 'required|string',
        'size_km' => 'required|integer|min:100|max:500000',
        'solar_system' => 'required|string|max:100',
        'image_url' => 'nullable|url|max:2048',
        'is_habitable' => 'boolean'
    ];
}

public function messages()
{
    return [
        'name.unique' => 'A planet with this name already exists!',
        'size_km.min' => 'The diameter cannot be less than 100 km'
    ];
}

Step 3: Use it in the controller

<?php
use App\Http\Requests\StorePlanetRequest;

public function store(StorePlanetRequest $request)
{
    // The data is already validated!
    $validated = $request->validated();
    $planet = Planet::create($validated);
    return response()->json($planet, 201);
}


6. Custom Validation Rules

Let's create a rule to check for the "reasonableness" of a planet's name. Standard Laravel rules can't check if a name is "forbidden," so we'll write our own logic.

Step 1: Generate the Rule

Laravel provides an Artisan command to create a rule class boilerplate. Let's run it in the terminal:

php artisan make:rule ValidPlanetName

Step 2: Edit app/Rules/ValidPlanetName.php

Open the created file. Its structure is simple and clear. Our task is to implement the logic inside the validate method.

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class ValidPlanetName implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // Our "blacklist" of names
        $forbidden = ['Earth 2.0', 'Nibiru', 'Planet X'];

        // Check if the input value is in our list,
        // ignoring case.
        if (in_array(strtolower($value), array_map('strtolower', $forbidden))) {
            // If the check fails, call the $fail function
            // with the error message the user will see.
            $fail('This planet name is forbidden!');
        }
    }
}

Step 3: Use it in the Form Request

Now our custom rule is ready to use. We can include it in any Form Request by simply creating a new instance of our class.

Let's open app/Http/Requests/StorePlanetRequest.php and add new ValidPlanetName to the rules array for the name field.

<?php
// app/Http/Requests/StorePlanetRequest.php

namespace App\Http\Requests;

use App\Rules\ValidPlanetName; // <-- Don't forget to import the class
use Illuminate\Foundation\Http\FormRequest;

class StorePlanetRequest extends FormRequest
{
    // ... (authorize method)

    public function rules(): array
    {
        return [
            'name' => [
                'required',
                'string',
                'max:255',
                'unique:planets',
                new ValidPlanetName, // <-- Here is our custom rule
            ],
            'description' => 'required|string',
            'size_km' => 'required|integer|min:100|max:500000',
            'solar_system' => 'required|string|max:100',
            'image_url' => 'nullable|url|max:2048',
            'is_habitable' => 'required|boolean'
        ];
    }

    // ... (messages method)
}
Done. Now when creating a planet, Laravel will sequentially apply all rules to the name field and, upon reaching new ValidPlanetName, will execute our custom logic.


7. Validation for Updates

Specifics for updating data:

When updating a record, validation rules often differ. The main feature is the uniqueness check, which must ignore the current record being updated.

Step 1: Create a separate Form Request for updates

php artisan make:request UpdatePlanetRequest
Step 2: Edit app/Http/Requests/UpdatePlanetRequest.php
<?php
use Illuminate\Validation\Rule;

public function authorize(): bool
{
    return true;
}
public function rules(): array
{
    $planet = $this->route('planet'); // Get the model from the route

     return [
         'name' => [
             'sometimes', // Validate only if the field is present in the request
             'required',
             'string',
             'max:255',
             Rule::unique('planets')->ignore($planet->id),
         ],
         'description'  => 'sometimes|required|string',
         'size_km'      => 'sometimes|required|integer|min:100|max:500000',
         // ... other fields with 'sometimes'
     ];
 }
Step 3: Use it in the controller
<?php
 use App\Http\Requests\UpdatePlanetRequest;

 public function update(UpdatePlanetRequest $request, Planet $planet)
 {
     $validated = $request->validated();
     $planet->update($validated);
     return response()->json($planet);
 }


8. Testing Validation in Postman

Scenario 1: Name Uniqueness Error

POST /api/planets
{
    "name": "Mars",
    "description": "The red planet, a target for future colonization",
    "size_km": 6779,
    "solar_system": "Solar System",
    "is_habitable": false
}
Expected Response:
{
    "message": "The given data was invalid.",
    "errors": {
        "name": ["A planet with this name already exists!"]
    }
}

Scenario 2: Incorrect Diameter

{
    "name": "Pea Planet",
    "size_km": 50 // < min:100
}
Expected Response:
"errors": {
    "size_km": ["The diameter cannot be less than 100 km"]
}


Review Quiz

1. The HTTP status for validation errors is:

2. The rule for checking uniqueness is:

3. Where is the best place to put complex validation rules?

4. The `authorize()` method in a Form Request should return:

5. The `sometimes` rule means:


🚀 Chapter Summary:

You have installed a powerful defense system for your cosmic API:

  • 🛡️ Basic and custom validation rules
  • 📝 Readable error messages
  • 🧩 Form Requests for complex scenarios
  • ⚙️ Unique rules for updating data

Your universe is now protected! Next, we will learn how to handle "cosmic accidents" - server errors.

📌 Checkpoint:

  1. Create a Form Request for updating planets
  2. Add a custom rule to validate the name
  3. Test the errors using Postman

⚠️ If validation doesn't work:

  • Check the Form Request injection in the controller method
  • Make sure authorize() returns true
  • For uniqueness on update, use Rule::unique