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:
- In the Controller (quick, but clutters the code)
- 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
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:
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)
}
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
Step 2: Editapp/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'
];
}
<?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
}
{
"message": "The given data was invalid.",
"errors": {
"name": ["A planet with this name already exists!"]
}
}
Scenario 2: Incorrect Diameter
Expected Response:Review Quiz
🚀 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:
- Create a Form Request for updating planets
- Add a custom rule to validate the name
- 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