Docs LATEST

Validation

The Validator class provides declarative input validation with support for common rules.

Services

Namespace: VelvetCMS\Validation\Validator


Definition #

class Validator
{
    public function __construct(array $data, array $rules);
    public static function make(array $data, array $rules): self;
    public function validate(): array;
    
    public static function extend(string $rule, Closure $callback): void;
    public static function hasExtension(string $rule): bool;
}

Basic Usage #

Creating a Validator #

use VelvetCMS\Validation\Validator;

$validator = Validator::make($data, [
    'name' => 'required|min:2|max:100',
    'email' => 'required|email',
    'age' => 'integer|min:18',
]);

try {
    $validated = $validator->validate();
    // Use $validated data
} catch (ValidationException $e) {
    $errors = $e->errors();
}

Request Validation #

The Request class provides a shorthand:

public function store(Request $request): Response
{
    $data = $request->validate([
        'title' => 'required|max:200',
        'body' => 'required',
        'status' => 'in:draft,published',
    ]);
    
    // $data contains only validated fields
    $this->service->create($data);
}

Available Rules #

required #

Field must be present and not empty.

'name' => 'required'

Empty values: null, '', []


min #

Minimum length for strings or minimum value for numeric types.

'password' => 'min:8'           // At least 8 characters
'items' => 'array|min:1'        // At least 1 item

max #

Maximum length for strings or count for arrays.

'title' => 'max:200'            // Up to 200 characters
'tags' => 'array|max:5'         // Up to 5 items

email #

Field must be a valid email address.

'email' => 'required|email'

url #

Field must be a valid URL.

'website' => 'url'

numeric #

Field must be numeric (int, float, or numeric string).

'price' => 'numeric'

integer #

Field must be an integer.

'quantity' => 'required|integer'

boolean #

Field must be a boolean value.

Accepted values: true, false, 0, 1, '0', '1', 'true', 'false'

'active' => 'boolean'

alpha #

Field must contain only letters.

'name' => 'alpha'

alphanumeric #

Field must contain only letters and numbers.

'username' => 'alphanumeric'

in #

Field must be one of the listed values.

'status' => 'in:draft,published,archived'
'role' => 'in:admin,editor,viewer'

regex #

Field must match the regular expression.

'slug' => 'regex:/^[a-z0-9-]+$/'
'phone' => 'regex:/^\+?[0-9]{10,15}$/'

same #

Field must match another field's value.

'password_confirmation' => 'same:password'

different #

Field must differ from another field's value.

'new_password' => 'different:current_password'

date #

Field must be a valid date string.

'published_at' => 'date'

array #

Field must be an array.

'items' => 'array'
'tags' => 'array|min:1|max:10'

Rule Syntax #

Pipe-separated rules #

'email' => 'required|email|max:255'

Array of rules #

'email' => ['required', 'email', 'max:255']

Rules with parameters #

Use : to pass parameters:

'status' => 'in:draft,published'    // Comma-separated values
'min_age' => 'integer|min:18'       // Single parameter

Error Handling #

ValidationException #

Thrown when validation fails:

try {
    $data = $request->validate($rules);
} catch (ValidationException $e) {
    $errors = $e->errors();
    // [
    //     'email' => ['The email must be a valid email address.'],
    //     'name' => ['The name field is required.']
    // ]
}

API Error Responses #

public function store(Request $request): Response
{
    try {
        $data = $request->validate([
            'email' => 'required|email',
            'name' => 'required|min:2',
        ]);
        
        return Response::json(['data' => $this->create($data)], 201);
        
    } catch (ValidationException $e) {
        return Response::json([
            'message' => 'Validation failed',
            'errors' => $e->errors()
        ], 422);
    }
}

Error Messages #

Default error messages by rule:

Rule Message
required The {field} field is required.
min The {field} must be at least {value} characters.
max The {field} may not be greater than {value} characters.
email The {field} must be a valid email address.
url The {field} must be a valid URL.
numeric The {field} must be a number.
integer The {field} must be an integer.
boolean The {field} must be true or false.
alpha The {field} may only contain letters.
alphanumeric The {field} may only contain letters and numbers.
in The selected {field} is invalid.
regex The {field} format is invalid.
same The {field} must match {other}.
different The {field} must be different from {other}.
date The {field} is not a valid date.
array The {field} must be an array.

Usage Examples #

User Registration #

$data = $request->validate([
    'username' => 'required|alphanumeric|min:3|max:20',
    'email' => 'required|email|max:255',
    'password' => 'required|min:8|max:128',
    'password_confirmation' => 'required|same:password',
    'terms' => 'required|boolean',
]);

Blog Post #

$data = $request->validate([
    'title' => 'required|max:200',
    'slug' => 'required|regex:/^[a-z0-9-]+$/|max:200',
    'body' => 'required',
    'status' => 'required|in:draft,published,scheduled',
    'published_at' => 'date',
    'tags' => 'array|max:10',
]);

Contact Form #

$data = $request->validate([
    'name' => 'required|min:2|max:100',
    'email' => 'required|email',
    'subject' => 'required|max:200',
    'message' => 'required|min:10|max:5000',
]);

API Filters #

$filters = $request->validate([
    'page' => 'integer|min:1',
    'per_page' => 'integer|min:1|max:100',
    'sort' => 'in:created_at,updated_at,title',
    'order' => 'in:asc,desc',
    'status' => 'in:all,published,draft',
]);

Optional Fields #

Fields without required rule are optional. Validation rules only apply when the field is present and not empty:

$data = $request->validate([
    'name' => 'required|max:100',     // Required
    'nickname' => 'max:50',            // Optional, validated if present
    'bio' => 'max:1000',               // Optional
]);

Custom Rule Extensions #

Register custom validation rules globally using Validator::extend().

extend() #

public static function extend(string $rule, Closure $callback): void

Parameters:

  • $rule - Rule name to register (used as 'rulename' or 'rulename:param')
  • $callback - fn(mixed $value, ?string $parameter, array $data, string $field): bool|string

Callback return values:

  • true - Validation passed
  • false - Validation failed (generic message)
  • string - Validation failed with custom message

Example:

Validator::extend('slug', function ($value, $parameter, $data, $field) {
    if (!preg_match('/^[a-z0-9-]+$/', $value)) {
        return "The {$field} must be a valid slug.";
    }
    return true;
});

Validator::extend('unique_email', function ($value, $parameter, $data, $field) {
    $exists = app('db')->table('users')->where('email', $value)->exists();
    return $exists ? "The {$field} is already taken." : true;
});

hasExtension() #

public static function hasExtension(string $rule): bool

Check if a custom rule is registered:

if (Validator::hasExtension('slug')) {
    // Rule exists
}