Skip to content

Form Requests

Form Requests are a powerful way to handle request validation in AvelPress. They provide a clean, organized approach to validating incoming HTTP requests before they reach your controllers.

Introduction

Form Requests extend the base AvelPress\Http\FormRequest class and encapsulate validation logic for specific requests. This keeps your controllers clean and makes validation rules reusable.

Creating Form Requests

Manual Creation

Create form request classes in your app/Http/Requests directory:

php
<?php

namespace App\Http\Requests;

use AvelPress\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:8',
            'age' => 'integer|min:18',
        ];
    }
}

Using CLI (if available)

bash
# Generate a form request
avel make:request StoreUserRequest

Basic Usage

Defining Validation Rules

The rules() method should return an array of validation rules:

php
public function rules(): array
{
    return [
        'title' => 'required|string|max:255',
        'content' => 'required|string',
        'status' => 'in:draft,published,archived',
        'category_id' => 'required|integer|exists:categories,id',
        'tags' => 'array',
        'tags.*' => 'string|max:50',
        'publish_date' => 'date|after:today',
    ];
}

Available Validation Rules

AvelPress supports various validation rules:

RuleDescriptionExample
requiredField must be present and not empty'name' => 'required'
stringField must be a string'title' => 'string'
integerField must be an integer'age' => 'integer'
numericField must be numeric'price' => 'numeric'
emailField must be a valid email'email' => 'email'
min:valueField must be at least value'password' => 'min:8'
max:valueField must not exceed value'name' => 'max:255'
in:valuesField must be one of values'status' => 'in:active,inactive'
arrayField must be an array'tags' => 'array'
dateField must be a valid date'birth_date' => 'date'
unique:table,columnField must be unique in table'email' => 'unique:users,email'
exists:table,columnField must exist in table'user_id' => 'exists:users,id'

Using in Controllers

Inject form requests into your controller methods:

php
<?php

namespace App\Controllers;

use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use App\Models\User;
use AvelPress\Routing\Controller;

class UserController extends Controller
{
    public function store(StoreUserRequest $request)
    {
        // Request is automatically validated
        // If validation fails, error response is returned automatically
        
        $user = User::create($request->validated());
        return new JsonResource($user);
    }

    public function update(UpdateUserRequest $request)
    {
        $user = User::findOrFail($request->get_param('id'));
        $user->update($request->validated());
        
        return new JsonResource($user);
    }
}

Advanced Features

Data Preparation

Use prepareForValidation() to modify data before validation:

php
public function prepareForValidation()
{
    $this->merge([
        'email' => strtolower(trim($this->get('email'))),
        'name' => ucwords(trim($this->get('name'))),
        'slug' => sanitize_title($this->get('title')),
    ]);
}

Conditional Validation

Apply different rules based on request data:

php
public function rules(): array
{
    $rules = [
        'name' => 'required|string|max:255',
        'email' => 'required|email',
    ];

    // Add password validation only for new users
    if (!$this->get('id')) {
        $rules['password'] = 'required|string|min:8';
    }

    // Different validation for user types
    if ($this->get('user_type') === 'admin') {
        $rules['admin_code'] = 'required|string';
    }

    return $rules;
}

Custom Validation Messages

Override default validation messages:

php
public function messages(): array
{
    return [
        'email.required' => 'The email address is required.',
        'email.email' => 'Please provide a valid email address.',
        'password.min' => 'The password must be at least :min characters.',
        'name.max' => 'The name may not be greater than :max characters.',
    ];
}

Custom Attribute Names

Customize attribute names in error messages:

php
public function attributes(): array
{
    return [
        'user_email' => 'email address',
        'user_name' => 'full name',
        'pwd' => 'password',
    ];
}

WordPress Integration

WordPress-Specific Validation

php
public function rules(): array
{
    return [
        'post_title' => 'required|string|max:255',
        'post_content' => 'required|string',
        'post_status' => 'in:draft,publish,private',
        'post_type' => 'in:post,page,product',
        'author_id' => 'required|integer|wp_user_exists',
    ];
}

public function prepareForValidation()
{
    $this->merge([
        'post_title' => sanitize_text_field($this->get('post_title')),
        'post_content' => wp_kses_post($this->get('post_content')),
        'author_id' => get_current_user_id(),
    ]);
}

File Upload Validation

php
public function rules(): array
{
    return [
        'title' => 'required|string|max:255',
        'attachment' => 'required|file|max:2048', // Max 2MB
        'image' => 'file|image|max:1024', // Max 1MB, must be image
    ];
}

public function prepareForValidation()
{
    // Get file parameters from WordPress request
    $files = $this->request->get_file_params();
    
    if (!empty($files)) {
        $this->merge($files);
    }
}

Real-World Examples

User Registration Request

php
<?php

namespace App\Http\Requests;

use AvelPress\Http\FormRequest;

class RegisterUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:8|confirmed',
            'password_confirmation' => 'required|string',
            'terms' => 'accepted',
            'age' => 'required|integer|min:18',
            'phone' => 'string|max:20',
        ];
    }

    public function prepareForValidation()
    {
        $this->merge([
            'email' => strtolower(trim($this->get('email'))),
            'name' => sanitize_text_field($this->get('name')),
            'phone' => sanitize_text_field($this->get('phone')),
        ]);
    }

    public function messages(): array
    {
        return [
            'email.unique' => 'This email address is already registered.',
            'password.confirmed' => 'Password confirmation does not match.',
            'terms.accepted' => 'You must accept the terms and conditions.',
            'age.min' => 'You must be at least 18 years old to register.',
        ];
    }
}

Quote Creation Request (from Infixs Mega ERP)

php
<?php

namespace Infixs\MegaErp\App\Modules\Quote\Http\Requests;

use AvelPress\Http\FormRequest;

class StoreQuoteRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'customer' => 'required|array',
            'customer.email' => 'required|email',
            'customer.name' => 'required|string|max:255',
            'customer.type' => 'required|in:individual,company',
            'customer.phone' => 'string|max:20',
            'customer.document' => 'string|max:50',
            
            'items' => 'required|array|min:1',
            'items.*.product_id' => 'required|integer',
            'items.*.product_name' => 'required|string|max:255',
            'items.*.quantity' => 'required|integer|min:1',
            'items.*.price' => 'required|numeric|min:0',
            'items.*.discount' => 'numeric|min:0',
            
            'payment_methods' => 'array',
            'payment_methods.*.method_id' => 'required|string',
            'payment_methods.*.discount' => 'numeric|min:0',
            'payment_methods.*.discount_type' => 'in:fixed,percentage',
            
            'expires_in' => 'required|integer|min:1',
            'notes' => 'string|max:1000',
        ];
    }

    public function prepareForValidation()
    {
        $this->merge([
            'customer.email' => sanitize_email($this->get('customer.email')),
            'customer.name' => sanitize_text_field($this->get('customer.name')),
            'customer.type' => sanitize_text_field($this->get('customer.type')),
            'expires_in' => (int) $this->get('expires_in'),
            'notes' => sanitize_textarea_field($this->get('notes')),
        ]);
    }

    public function messages(): array
    {
        return [
            'customer.required' => 'Customer information is required.',
            'customer.email.required' => 'Customer email is required.',
            'customer.email.email' => 'Please provide a valid customer email.',
            'items.required' => 'At least one item is required.',
            'items.min' => 'At least one item is required.',
            'items.*.quantity.min' => 'Item quantity must be at least 1.',
            'expires_in.min' => 'Quote must be valid for at least 1 day.',
        ];
    }
}

Product Update Request

php
<?php

namespace App\Http\Requests;

use AvelPress\Http\FormRequest;

class UpdateProductRequest extends FormRequest
{
    public function rules(): array
    {
        $productId = $this->get_param('id');
        
        return [
            'name' => 'required|string|max:255',
            'slug' => "required|string|max:255|unique:products,slug,{$productId}",
            'description' => 'string',
            'price' => 'required|numeric|min:0',
            'sale_price' => 'numeric|min:0|lt:price',
            'sku' => "required|string|max:100|unique:products,sku,{$productId}",
            'stock_quantity' => 'integer|min:0',
            'status' => 'in:draft,published,archived',
            'categories' => 'array',
            'categories.*' => 'integer|exists:categories,id',
            'tags' => 'array',
            'tags.*' => 'string|max:50',
            'weight' => 'numeric|min:0',
            'dimensions' => 'array',
            'dimensions.length' => 'numeric|min:0',
            'dimensions.width' => 'numeric|min:0',
            'dimensions.height' => 'numeric|min:0',
        ];
    }

    public function prepareForValidation()
    {
        $this->merge([
            'name' => sanitize_text_field($this->get('name')),
            'slug' => sanitize_title($this->get('slug') ?: $this->get('name')),
            'description' => wp_kses_post($this->get('description')),
            'sku' => strtoupper(sanitize_text_field($this->get('sku'))),
            'price' => floatval($this->get('price')),
            'sale_price' => floatval($this->get('sale_price')),
            'weight' => floatval($this->get('weight')),
        ]);
    }

    public function messages(): array
    {
        return [
            'slug.unique' => 'This product slug is already taken.',
            'sku.unique' => 'This SKU is already in use.',
            'sale_price.lt' => 'Sale price must be less than regular price.',
        ];
    }
}

Error Handling

When validation fails, AvelPress automatically returns a WP_Error response with status code 422:

json
{
    "code": "validation_error",
    "message": "Validation failed",
    "data": {
        "status": 422,
        "errors": {
            "email": ["The email field is required."],
            "password": ["The password must be at least 8 characters."]
        }
    }
}

Best Practices

1. Keep Rules Simple and Clear

php
// Good
public function rules(): array
{
    return [
        'title' => 'required|string|max:255',
        'content' => 'required|string',
        'status' => 'in:draft,published',
    ];
}

// Avoid complex nested validation in rules method

2. Use Preparation for Data Cleaning

php
public function prepareForValidation()
{
    // Clean and format data before validation
    $this->merge([
        'email' => strtolower(trim($this->get('email'))),
        'phone' => preg_replace('/[^0-9+]/', '', $this->get('phone')),
        'title' => sanitize_text_field($this->get('title')),
    ]);
}

3. Provide Clear Error Messages

php
public function messages(): array
{
    return [
        'email.required' => 'Please provide your email address.',
        'email.unique' => 'This email is already registered.',
        'password.min' => 'Password must be at least 8 characters long.',
    ];
}
php
// Create separate requests for different operations
class StoreUserRequest extends FormRequest { }
class UpdateUserRequest extends FormRequest { }
class DeleteUserRequest extends FormRequest { }

Form Requests in AvelPress provide a clean, organized way to handle request validation while maintaining compatibility with WordPress standards and security practices.

Released under the MIT License.