Skip to Content
🎉 WpMVC 2.0 is released! Now compatible with PHP 7.4 to 8.5. Read the guide →
DocumentationRequest Validator

Request Validator

The Request Validator provides a robust and extensible system for validating REST API requests in WordPress plugins. It supports a fluent rule-based syntax inspired by Laravel and integrates seamlessly with WP_REST_Request.


Basic Usage

Inside a controller method:

<?php namespace MyPluginNamespace\App\Http\Controllers; defined( 'ABSPATH' ) || exit; use MyPluginNamespace\WpMVC\RequestValidator\Request; class PostController extends Controller { public function store( Request $request ) { $request->validate( [ 'title' => 'required|string|min:3|max:255', 'email' => 'required|email', 'price' => 'numeric|min:0', 'tags' => 'array', 'image' => 'file|mimes:png,jpg,jpeg|max:2048', 'launched' => 'date:Y-m-d|after_or_equal:2024-01-01', ] ); // Validation passed; continue logic } }

If validation fails, a WpMVC\Exceptions\Exception is thrown with HTTP status 422 and the error messages.


Form Requests

For more complex validation scenarios, you may wish to create a “form request” class. Form requests are custom request classes that encapsulate their own validation and authorization logic.

Generating Form Requests

To create a new form request, use the make:request Artisan command:

php artisan make:request StorePostRequest

The new class will be placed in your app/Http/Requests directory. Extend the FormRequest class to define your rules:

<?php namespace MyPluginNamespace\App\Http\Requests; defined( 'ABSPATH' ) || exit; use MyPluginNamespace\WpMVC\RequestValidator\FormRequest; class StorePostRequest extends FormRequest { /** * Determine if the user is authorized to make this request. */ public function authorize(): bool { return true; } /** * Get the validation rules that apply to the request. */ public function rules(): array { return [ 'title' => 'required|string|max:255', 'content' => 'required', 'status' => 'required|in:publish,draft,pending' ]; } /** * Get custom messages for validator errors. */ public function messages(): array { return [ 'title.required' => 'A title is absolutely required for your post.', ]; } /** * Get custom attributes for validator errors. */ public function attributes(): array { return [ 'status' => 'publication status', ]; } /** * Configure the validator instance. */ public function with_validator( $validator ): void { $validator->after( function ( $validator ) { if ( $this->something_else_is_invalid() ) { $validator->errors['field_name'][] = 'Something went wrong!'; } } ); } }

Once defined, you can seamlessly type-hint the class on your controller method. The incoming request is validated and authorized before the controller method is even called:

public function store( StorePostRequest $request ) { // The incoming request is valid and authorized... }

Available Rules

RuleDescription
acceptedMust be yes, on, 1, true, 1, or true.
after:dateMust be after the given date (supports relative dates like today).
after_or_equal:dateMust be after or equal to the given date.
alphaMust be entirely alphabetic characters.
alpha_dashMust have alpha-numeric characters, dashes, and underscores.
alpha_numMust be entirely alpha-numeric characters.
arrayMust be an array.
bailStop running validation rules after the first failure.
before:dateMust be before the given date.
before_or_equal:dateMust be before or equal to the given date.
between:min,maxMust have a size between the given min and max.
booleanMust be a boolean value.
confirmedfield_confirmation must match the field value.
date:formatMust be a date in the given format (default: Y-m-d).
date_equals:dateMust exactly match the given date.
different:fieldMust have a different value than the specified field.
digits:valueMust be numeric and have an exact length of value.
digits_between:min,maxMust be numeric and have a length between min and max.
emailMust be a valid email address.
ends_with:foo,bar,...Must end with one of the given values.
fileMust be a valid uploaded file.
imageMust be an image (jpeg, png, bmp, gif, svg, webp).
in:foo,bar,...Must be included in the given list of values.
integerMust be an integer.
ipMust be a valid IP address.
ipv4Must be a valid IPv4 address.
ipv6Must be a valid IPv6 address.
jsonMust be a valid JSON string.
mac_addressMust be a valid MAC address.
max:valueMax length/size/value (string, numeric, file, array).
mimes:jpg,png,...Allowed file extensions based on MIME types.
mimetypes:text/plain,...Must match one of the given MIME types.
min:valueMin length/size/value (string, numeric, file, array).
not_in:foo,bar,...Must not be included in the given list of values.
not_regex:patternMust not match the given regular expression.
nullableField may be null or empty.
numericMust be a numeric value.
prohibited_unless:field,valProhibited unless field is equal to any of the values.
regex:patternMust match the given regular expression.
requiredField must be present and non-empty.
required_if:field,valRequired if the field is equal to the given value.
same:fieldMust match the value of the given field.
size:valueMust have a matching size (string length, array count, file KB).
sometimesRun rules only if the field is present in the request.
starts_with:foo,bar,...Must start with one of the given values.
stringMust be a string.
timezoneMust be a valid timezone identifier.
urlMust be a valid URL.
uuidMust be a valid UUID.

File Validation Example

$request->validate([ 'photo' => 'required|file|mimes:png,jpg|max:1024', ]);
  • Validates that photo is a file
  • Accepts only png, jpg
  • Maximum 1MB (1024 KB)

Date Validation Example

$request->validate([ 'launched' => 'required|date:Y-m-d|after_or_equal:2022-01-01', ]);

Supports custom formats and comparison logic using native PHP DateTime.


Array Validation Example

The validator supports wildcard dot-notation (.*) to validate elements within an array where the exact indexes are unknown.

For instance, you can validate each email in an array of participants:

// Request Payload: { "participants": [ { "email": "[email protected]" }, { "email": "[email protected]" } ] } $request->validate([ 'participants' => 'required|array', 'participants.*.email' => 'required|email' // Evaluates participants.0.email, participants.1.email, etc. ]);

Custom Error Messages & Rules

You can customize the error messages by passing a third array to the $request->make($request, $rules, $messages, $attributes) method, or by overriding the messages() and attributes() methods in your FormRequest class.

Placeholders

The following placeholders can be used within your custom error messages:

  • :attribute: The name of the field being validated.
  • :min, :max, :size: Respective numeric/size values.
  • :other: Name of the field being compared against.
  • :value, :values: The value(s) required by the rule.
  • :format: The expected date format.

Example:

$messages = [ 'email.required' => 'We really need your :attribute!', 'age.min' => 'You must be at least :min years old.', ];

You can also pass inline Closures directly into the rules array for fast custom logic:

$request->validate([ 'title' => [ 'required', function ($attribute, $value, $fail) { if ($value === 'reserved_word') { $fail("The {$attribute} contains an invalid word."); } }, ] ]);

Custom Rule Objects

For complex validation, you can create a dedicated rule class by extending the WpMVC\RequestValidator\Rules\Rule base class:

<?php namespace MyPluginNamespace\App\Rules; defined( 'ABSPATH' ) || exit; use MyPluginNamespace\WpMVC\RequestValidator\Rules\Rule; class UppercaseRule extends Rule { public static function get_name(): string { return 'uppercase'; } public function passes( string $attribute, $value ): bool { return strtoupper( $value ) === $value; } protected function default_message(): string { /* translators: 1: attribute name */ return sprintf( __( 'The %1$s must be completely uppercase.', 'wpmvc' ), ':attribute' ); } } // In your controller: $request->validate( [ 'title' => [ 'required', new UppercaseRule() ] ] );

Fluent Rule Builder

Instead of using pipe-separated strings, you may fluently construct validation rules using the Rule class. This is particularly useful for complex rules or when you need to avoid string concatenation errors (such as using an in rule with an array of predefined data).

use WpMVC\RequestValidator\Rule; $request->validate([ 'email' => [Rule::required(), Rule::email(), Rule::max(255)], 'status' => [Rule::required(), Rule::in(['active', 'pending', 'banned'])], 'type' => [Rule::required_if('is_admin', true)], ]);

Validation Error Response Format

If validation fails, the engine throws a Exception that the WpMVC framework catches and automatically converts into a 422 Unprocessable Entity JSON response.

The JSON response format is structured as follows, where errors contains arrays of error messages keyed by the failing field name:

{ "data": { "status_code": 422 }, "messages": { "email": ["The email field must be a valid email address."], "price": ["The price must be at least 0."] } }

Manual Check

If you prefer to handle validation manually without throwing an exception, you can pass false as the second argument to the validate method. You can then use the fails() method to check for errors:

$request->validate( $rules, false ); if ( $request->fails() ) { wp_send_json_error( [ 'errors' => $request->errors ], 422 ); }

Validation Hooks (After)

The Validation engine allows you to attach callbacks to be run after validation completes. This is useful for performing additional validation across multiple fields:

$validation = $request->make( $request, $rules )->after( function ( $validation ) { if ( $this->something_else_is_invalid() ) { $validation->errors['field_name'][] = 'Something went wrong!'; } } ); if ( $validation->fails() ) { // Handle failures... }

Usage Outside of a Controller

If you want to use the validator in a different context (like a cron job, WP-CLI command, or a regular WordPress hook action), you can manually build a WP_REST_Request and pass it to either Request or Validation.

Using Validation directly:

use WpMVC\RequestValidator\Validation; $request = new \WP_REST_Request(); $request->set_param('email', '[email protected]'); $validation = new Validation($request, [ 'email' => 'required|email' ]); if ($validation->fails()) { $errors = $validation->errors(); }

Using Request class:

<?php defined( 'ABSPATH' ) || exit; use MyPluginNamespace\WpMVC\RequestValidator\Request; $wp_request = new \WP_REST_Request(); $wp_request->set_params( $_POST ); // Hydrate with raw global data $request = new Request( $wp_request ); $request->validate( [ 'title' => 'required|string|max:255' ] );
Last updated on