Usage Guide

Comprehensive guide to the WPZylos Validation package.

Table of Contents


1. Basic Validation

Create a Validator with data and rules, then check the result:

use WPZylos\Framework\Validation\Validator;

$validator = new Validator(
    data: $request->all(),
    rules: [
        'name'  => 'required|string|min:2|max:100',
        'email' => 'required|email',
        'age'   => 'required|integer|min:18',
    ],
);

// Option 1: Check result
if ($validator->validate()) {
    // Valid - validate() returns bool
    $data = $validator->validated();
}

// Option 2: Check failure
if ($validator->fails()) {
    $errors = $validator->errors();
}

// Option 3: Check success
if ($validator->passes()) {
    // All good
}

Important: validate() returns bool, not an object. Data and rules are passed to the constructor.

Getting Validated Data

validated() returns only the fields that had rules defined. If validation fails, it throws ValidationException:

try {
    $data = $validator->validated();
} catch (\WPZylos\Framework\Validation\ValidationException $e) {
    $errors = $e->errors(); // MessageBag
}

2. Available Rules

Built-in Rules (Validator class)

These rules are handled internally by the Validator class:

RuleExampleDescription
required'required'Field must be present, not null, not empty string, not empty array
string'string'Value must be a string
integer'integer'Value must be a valid integer (via FILTER_VALIDATE_INT)
int'int'Alias for integer
numeric'numeric'Value must be numeric (via is_numeric)
boolean'boolean'Value must be true, false, 0, 1, '0', '1', 'true', or 'false'
array'array'Value must be an array
email'email'Value must be a valid email (via FILTER_VALIDATE_EMAIL)
url'url'Value must be a valid URL (via FILTER_VALIDATE_URL)
min'min:3'Minimum: string length, array count, or numeric value
max'max:255'Maximum: string length, array count, or numeric value
in'in:draft,published'Value must be one of the listed values (strict comparison)
regex'regex:/^[a-z]+$/'Value must match the regex pattern
nullable'nullable'Field is optional. Skips all rules if value is null or empty string

Standalone Rule Classes (Rules/ directory)

These 12 classes implement RuleInterface and can be registered via Validator::extend():

ClassDescription
RequiredRuleNot null, not empty string, not empty array
EmailRuleValid email via FILTER_VALIDATE_EMAIL
UrlRuleValid URL via FILTER_VALIDATE_URL
NumericRuleNumeric via is_numeric
AlphaRuleOnly alphabetic characters (Unicode letters \pL\pM)
AlphaNumericRuleOnly letters and numbers
MinRuleMinimum string length / array count / numeric value
MaxRuleMaximum string length / array count / numeric value
BetweenRuleValue between min and max (string length / array count / numeric)
InRuleValue in parameter list
ConfirmedRuleField matches {field}_confirmation
RegexRuleValue matches regex pattern

3. Rule Syntax

Pipe-Separated String

'name' => 'required|string|min:2|max:100'

Array Syntax

'name' => ['required', 'string', 'min:2', 'max:100']

Rule Parameters

Parameters follow a colon, multiple parameters are comma-separated:

'age'    => 'min:18',                    // one parameter
'status' => 'in:active,inactive,banned', // multiple parameters
'code'   => 'regex:/^[A-Z]{3}[0-9]+$/', // regex pattern

4. Custom Error Messages

Field-Specific Messages

Target a specific field + rule combination:

$validator = new Validator(
    data: $data,
    rules: ['email' => 'required|email'],
    messages: [
        'email.required' => 'We need your email address.',
        'email.email'    => 'That does not look like a valid email.',
    ],
);

Rule-Level Messages

Apply to all fields using that rule:

$validator = new Validator(
    data: $data,
    rules: $rules,
    messages: [
        'required' => 'The :attribute field cannot be blank.',
        'email'    => 'Please enter a valid email for :attribute.',
    ],
);

Message Placeholders

PlaceholderReplaced With
:attributeField name (underscores replaced with spaces)
:param0First rule parameter
:param1Second rule parameter
:paramNNth rule parameter

Message Resolution Order

  1. Field-specific custom message (e.g., email.required)
  2. Rule-level custom message (e.g., required)
  3. Extension custom message (from RuleInterface::message())
  4. Default message (translated via Translator if available)

5. Custom Attribute Names

Replace field names in error messages with human-friendly labels:

$validator = new Validator(
    data: $data,
    rules: ['phone_number' => 'required'],
    attributes: [
        'phone_number' => 'phone number',
    ],
);

// Error: "The phone number field is required." instead of "The phone_number field is required."

6. Nullable Fields

Add nullable to make a field optional. If the value is null or an empty string, all other rules are skipped:

$rules = [
    'name'     => 'required|string',
    'nickname' => 'nullable|string|min:2',  // Only validated if present
    'bio'      => 'nullable|max:500',
];

7. FormRequest

FormRequest is an abstract class that combines authorization, sanitization, and validation. It lives in this package (WPZylos\Framework\Validation), not in wpzylos-http.

Creating a FormRequest

use WPZylos\Framework\Validation\FormRequest;

class UpdateSettingsRequest extends FormRequest
{
    public function authorize(): bool
    {
        return current_user_can('manage_options');
    }

    public function rules(): array
    {
        return [
            'site_title'       => 'required|string|max:200',
            'admin_email'      => 'required|email',
            'posts_per_page'   => 'required|integer|min:1|max:100',
            'enable_comments'  => 'nullable|boolean',
        ];
    }

    public function sanitize(): array
    {
        return [
            'site_title'      => 'text',
            'admin_email'     => 'email',
            'posts_per_page'  => 'int',
            'enable_comments' => 'bool',
        ];
    }

    public function messages(): array
    {
        return [
            'site_title.required' => 'Your site needs a title.',
        ];
    }

    public function attributes(): array
    {
        return [
            'admin_email'     => 'administrator email',
            'posts_per_page'  => 'posts per page',
        ];
    }
}

Using in a Controller

public function update(Request $request): Response
{
    $form = new UpdateSettingsRequest($request);

    if (!$form->authorize()) {
        return Response::error('Unauthorized', 403);
    }

    if ($form->fails()) {
        return Response::json([
            'errors' => $form->errors()->toArray(),
        ], 422);
    }

    $data = $form->validated(); // Sanitized + validated data
    // Save settings...

    return Response::json(['status' => 'ok']);
}

How FormRequest Works

  1. data() is called to get sanitized input from the HTTP request
  2. Sanitizers from sanitize() are applied to each field
  3. A Validator is created with the sanitized data + rules + messages + attributes
  4. validate(), fails(), errors(), validated() all delegate to this internal Validator

8. Sanitizer Types

Used in FormRequest::sanitize() to map fields to WordPress sanitization functions:

TypeWordPress FunctionDescription
text (default)sanitize_text_field()Plain text, strips tags
textareasanitize_textarea_field()Multi-line text, preserves newlines
htmlwp_kses_post()Rich HTML (post-content level tags)
emailsanitize_email()Valid email characters
urlesc_url_raw()Safe URL for database storage
int / integer(int) castInteger value
absintabsint()Non-negative integer
floatfilter_var(FILTER_SANITIZE_NUMBER_FLOAT)Float value
bool / booleanfilter_var(FILTER_VALIDATE_BOOLEAN)Boolean value
slugsanitize_title()URL-safe slug
keysanitize_key()Lowercase alphanumeric with dashes and underscores

Fields without a sanitizer mapping in sanitize() are passed through unchanged.


9. Custom Rules

Implementing RuleInterface

use WPZylos\Framework\Validation\RuleInterface;

class UniqueEmailRule implements RuleInterface
{
    public function passes(string $field, mixed $value, array $parameters, array $data): bool
    {
        // $parameters[0] could be a table name, etc.
        return !email_exists($value);
    }

    public function message(): string
    {
        return 'The :attribute is already taken.';
    }
}

Important: The passes() signature is (string $field, mixed $value, array $parameters, array $data). The $data parameter gives access to all data being validated.

Registering Custom Rules

Use Validator::extend() with a rule name and a RuleInterface instance (not a closure):

use WPZylos\Framework\Validation\Validator;

Validator::extend('unique_email', new UniqueEmailRule());

Using Custom Rules

$validator = new Validator(
    data: ['email' => '[email protected]'],
    rules: ['email' => 'required|email|unique_email'],
);

Custom rules are registered globally and available to all Validator instances.


10. MessageBag API

MessageBag collects validation errors per field.

$errors = $validator->errors();

// Check for errors
$errors->hasErrors();         // bool - any errors at all?
$errors->has('email');        // bool - errors for this field?

// Get messages
$errors->first('email');      // ?string - first error for field
$errors->get('email');        // string[] - all errors for field
$errors->all();               // array<string, string[]> - all grouped by field
$errors->flatten();           // string[] - all messages as flat array

// Metadata
$errors->count();             // int - total error count
$errors->keys();              // string[] - field names with errors
$errors->toArray();           // array<string, string[]> - same as all()

Displaying Errors (Example)

if ($validator->fails()) {
    foreach ($validator->errors()->all() as $field => $messages) {
        foreach ($messages as $message) {
            echo "<p class='error'>{$message}</p>";
        }
    }
}

11. ValidationException Handling

ValidationException is thrown by validated() when validation fails:

use WPZylos\Framework\Validation\ValidationException;

try {
    $data = $validator->validated();
} catch (ValidationException $e) {
    $errors  = $e->errors();         // MessageBag
    $message = $e->getMessage();     // "The given data was invalid."
}

The exception carries a MessageBag with all validation errors.


12. Service Provider Registration

The ValidationServiceProvider registers a validator factory with the container:

use WPZylos\Framework\Validation\ValidationServiceProvider;

$app->register(new ValidationServiceProvider());

What Gets Registered

BindingTypeDescription
'validator'FactoryReturns a factory function for creating Validator instances

Using the Factory

$makeValidator = $container->get('validator');

$validator = $makeValidator(
    $data,                   // array - data to validate
    $rules,                  // array - validation rules
    $messages,               // array - custom messages (optional)
    $attributes,             // array - custom attribute names (optional)
);

The factory automatically injects a Translator instance if one is registered in the container.