Skip to content

✅ Validation

PageBlocks includes a flexible validation system for checking user input. You can define validation rules using a pipe-delimited string or as an array of rule strings.

🧩 Validation in the Controller

You can perform validation directly in your controller using the $request->validate() method:

php
$request->validate([
    'honeypot' => 'empty',
    'username' => 'required|string',
    'password' => 'required|min:8'
]);

Alternatively, you can define rules as an array:

php
$request->validate([
    'honeypot' => ['empty'],
    'username' => ['required', 'string'],
    'password' => ['required', 'min:8']
]);

Both formats are fully supported and interchangeable.

🔁 Response Format

If validation fails, PageBlocks determines the type of request:

  • For fetch (XHR) requests, it returns a structured JSON response with a 422 status code.
  • For standard (non-XHR) requests, it redirects back and stores validation errors in the session.

📦 JSON Response Example

json
{
  "success": false,
  "message": "The given data was invalid.",
  "errors": {
    "username": ["The username field is required."],
    "password": ["The password must be at least 8 characters."]
  }
}

🧾 Session Output in Fenom

When validation errors are stored in the session, you can easily display them in templates using built-in Fenom variables:

  • {$error_message} — contains a general error message (e.g., "The given data was invalid.")
php
{if $error_message}
    <p class="form-message text-center text-error text-danger">{$error_message}</p>
{/if}
  • {$errors} — contains an array of field-specific error messages
php
<input type="text" 
       name="username" 
       class="form-control{if $errors.username} is-invalid{/if}"
>
  • {$old_input} — contains all user-submitted input data, which can be used to repopulate the form fields after a validation error.
php
<input type="text" name="name" class="form-control" value="{$old_input.name}">

✍️ Custom Error Messages

PageBlocks allows you to customize validation error messages globally or per field.

🌐 Language Files

All default validation messages are stored in:

php
lang/{lang}/validation.php

⚙️ Inline Message Overrides

You can pass custom messages as the second parameter to the validate() method.

Custom message for all fields using the same rule:

php
$request->validate([
    'honeypot' => 'empty',
    'username' => 'required|string',
    'password' => 'required|string|min:8'
], [
    'required' => 'The :attribute field is required.',
]);

Field-specific error messages:

php
$request->validate([
    'honeypot' => 'empty',
    'username' => 'required|string',
    'password' => 'required|string|min:8'
], [
    'username.required' => 'We need to know your username!',
]);

🏷 Custom Field Names (customAttributes)

You can customize the display names of your input fields by passing a third parameter to the validate() method.

php
$request->validate([
    'username' => 'required|string',
    'password' => 'required|min:8',
], [], [
    'username' => 'Username',
    'password' => 'Password',
]);

🧪 Creating a Validator Manually

While $request->validate() is the most convenient method, you can also create a validator manually using Validator::make():

php
$validator = Validator::make($request->all(), [
    'username' => 'required|string',
    'password' => 'required|string|min:8'
]);

🚫 Handling Failed Validation

php
if ($validator->fails()) {
    return response()->error('', $validator->errors());
}

🧼 Accessing Validated Input

php
$validated = $validator->validated();

⚙️ Behind the Scenes

php
new Validation($data, $rules, $messages = [], $customAttributes = []);

🔄 Automatic Redirection

If you want immediate validation with auto-redirect on failure:

php
Validator::make($request->all(), [
    'username' => 'required|string',
    'password' => 'required|string|min:8'
])->validate();

👜 Using Error Bags

When a page contains multiple forms (e.g., login and registration forms), validation errors can become confusing - all errors end up in the same errors array, making it difficult to determine which form they belong to in templates.

To solve this, you can use the validateWithBag($name) method, which stores errors, old input values, and messages in a named bag with the $name key.

🔧 Example:

php
Validator::make($request->all(), [
    'username' => 'required|string',
    'password' => 'required|string|min:8',
])->validateWithBag('login');

This call will:

  • Store errors in errors['login']
  • Save old input values in old_input['login']
  • Store any message in error_message['login'] if provided

🖼️ Template Example

html+php
{if errors.login}
    <ul class="text-danger">
        {foreach $errors.login as $field => $messages}
            {foreach $messages as $message}
                <li>{$message}</li>
            {/foreach}
        {/foreach}
    </ul>
{/if}

{if error_message.login}
    <div class="alert alert-danger">{$error_message.login}</div>
{/if}

<form method="post" action="/login">
    <input type="text" name="username" placeholder="Username"
           value="{$old_input.login.username}">
    
    <input type="password" name="password" placeholder="Password">
    
    <button type="submit">Login</button>
</form>

Similarly, you can handle a registration form with a different bag:

php
Validator::make($request->all(), [...])
    ->validateWithBag('register');
php
{if errors.register}
    ...
{/if}

{if error_message.register}
    ...
{/if}

<form method="post" action="/register">
    <input type="text" name="email" value="{$old_input.register.email}">
    ...
</form>

🤔 Why Use This?

  • ❌ Without validateWithBag(), all errors go into errors with no form association
  • ✅ With named bags you get:
    • errors.login
    • old_input.login
    • error_message.login
    • Clear form-specific error handling

🧵 Working with Validation Error Messages

📋 Retrieving All Errors

php
$errors = $validator->errors();

🎯 Messages for a Specific Field

php
$messages = $errors->get('email');

❗️ Check If a Field Has Errors

php
if ($errors->has('email')) {
    // The email field has validation errors
}

📚 Available Validation Rules

🔘 Booleans

📘 String

🔢 Numbers

🧺 Arrays

📅 Dates

📂 Files

🗄 Database

🧮 Universal

🧰 Utilities

accepted

The field must be one of: "yes", "on", 1, "1", true, or "true".

php
'terms' => 'accepted'

Used to validate checkbox confirmations (e.g. agreement to terms).

accepted_if:anotherfield,value,...

The field must be accepted only if another field has a given value.

php
'newsletter' => 'accepted_if:subscribe,true'

If subscribe is true, then newsletter must be accepted (e.g. checkbox ticked).

boolean

The field must be castable to a boolean (true, false, 1, 0, "1", "0").

php
'agree' => 'boolean'

declined

The field must be one of: "no", "off", 0, "0", false, or "false".

php
'refuse_terms' => 'declined'

declined_if:anotherfield,value,...

The field must be declined only if another field has the given value.

php
'contact_by_phone' => 'declined_if:preferred_contact,email'

If preferred_contact is email, then contact_by_phone must be declined.

String

string

The value must be a string. If nullable, use nullable|string.

php
'title' => 'string'

email

The value must be a valid email address.

php
'email' => 'required|email'

confirmed[:other_field]

There must be a matching field named {field}_confirmation, unless a custom one is specified.

php
// Default behavior: compares with `password_confirmation`
'password' => 'required|min:8|confirmed'
php
// Custom field: compares with `pass_confirm`
'password' => 'required|min:8|confirmed:pass_confirm'

This rule is commonly used for passwords, email confirmation, or any field that needs to be entered twice to prevent typos.

same:field

The field under validation must have the same value as the given field.

php
'password' => 'same:password_confirmation'

different:field

The field under validation must have a different value from the given field.

php
'username' => 'different:email'

in:foo,bar,...

The field value must be one of the following values.

php
'status' => 'in:active,inactive,pending'

not_in:foo,bar,...

The field value must not be one of the following values.

php
'role' => 'not_in:admin,root'

json

The field must be a valid JSON string.

php
'meta' => 'json'

lowercase

The field must be a lowercase string.

php
'slug' => 'lowercase'

uppercase

The field must be an uppercase string.

php
'code' => 'uppercase'

url[:protocols]

The field must be a valid URL. You can optionally define allowed protocols (e.g., http, https, steam).

php
'website' => 'url'              // Any valid URL
'game' => 'url:steam,minecraft' // Must start with allowed schemes

uuid[:versions]

The field must be a valid RFC 9562 UUID. You can optionally specify allowed UUID versions (1–8).

php
'id' => 'uuid'          // Any version
'id' => 'uuid:4'        // Only version 4
'id' => 'uuid:1,4,6'    // Multiple allowed versions

Numbers

numeric

The field must be a valid number (integer or float).

php
'price' => 'numeric'

integer

The field must be a whole number (integer only).

php
'quantity' => 'integer'

decimal:min,max

The field must have a number of decimal places between min and max.

php
'amount' => 'decimal:2,4' // Accepts 1.23, 5.678, 10.0000

gt:field

The field must be greater than the given field.

php
'price' => 'gt:discounted_price'

gte:field

The field must be greater than or equal to the given field.

php
'end_date' => 'gte:start_date'

lt:field

The field must be less than the given field.

php
'discounted_price' => 'lt:price'

lte:field

The field must be less than or equal to the given field.

php
'start_date' => 'lte:end_date'

min_digits:value

The field must be a numeric string and have at least the specified number of digits.

php
'code' => 'min_digits:6'

max_digits:value

The field must be a numeric string and have at most the specified number of digits.

php
'pin' => 'max_digits:4'

Arrays

array

The field under validation must be an array.

php
'tags' => 'array'

contains:foo,bar,...

The array must contain at least one of the given values.

php
'roles' => 'array|contains:admin,editor'
// Valid if roles contains at least 'admin' or 'editor'

If the field is a string, it will be compared directly as a single value.

distinct

The array must not contain duplicate values.

php
'tags' => 'array|distinct'
// ['php', 'html', 'php'] → ❌
// ['php', 'html', 'css'] → ✅

Works with primitive values (string, int).

in_array:otherfield.*

The value must exist in another array field.

php
'country' => 'in_array:countries.*'

/*
[
  'country' => 'UA',
  'countries' => ['UA', 'PL', 'DE']
]
*/

You can also match against a simple array field:

php
'item' => 'in_array:available_items'

Dates

date

The field must be a valid date string.

php
'birthdate' => 'date'

date_equals:field_or_date

The date must exactly match another field or literal date.

php
'start' => 'date_equals:end'
'fixed' => 'date_equals:2024-01-01'

date_format:format,...

The date must match at least one of the given formats.

php
'start' => 'date_format:Y-m-d'
'custom' => 'date_format:d/m/Y,H:i'

You may provide multiple formats separated by comma:

php
'start' => 'date_format:Y-m-d,H:i'

before:field_or_date

The date must be before the target.

php
'start' => 'before:end'

before_or_equal:field_or_date

The date must be before or equal to the target.

php
'start' => 'before_or_equal:end'

after:field_or_date

The date must be after the given field or literal date.

php
'end' => 'after:start'
'event_date' => 'after:2025-06-01'

after_or_equal:field_or_date

The date must be after or equal to the target.

php
'end' => 'after_or_equal:start'

Files

file

The field must be a valid uploaded file.

php
'avatar' => 'file'

image

The field must be an image (jpeg, png, gif, webp, ...).

php
'avatar' => 'image'

mimes:jpeg,png,...

The file's MIME type must match one of the given.

php
'document' => 'file|mimes:application/pdf'
'photo'    => 'image|mimes:image/jpeg,image/png'

Validates by internal MIME (header-based).

extensions:jpg,png,...

The file name must have one of the specified extensions.

php
'upload' => 'file|extensions:pdf,docx'

Less strict than mimes — based on file name only.

dimensions:min_width=100,max_height=800,...

Validates image dimensions.

php
'photo' => 'image|dimensions:min_width=200,max_height=500'

Supported constraints:

  • width, height
  • min_width, max_width
  • min_height, max_height

Database

unique:table,column[,exceptId|field,exceptValue]

The field value must be unique in the given database table and column.

php
'email' => 'unique:modUserProfile,email'

Ignoring current record You can exclude a record by ID:

php
'email' => 'unique:modUserProfile,email,123' // where id != 123

Or by any other column:

php
'slug' => 'unique:modResource,alias,createdby,3' // where createdby != 3

exists:table,column[,exceptId|field,exceptValue]

The field value must already exist in the given table and column.

php
'country' => 'exists:pbTableValue,code'

Exclude a record from check You may skip one by ID or custom column:

php
'email' => 'exists:modUserProfile,email,123'           // where id != 123
'email' => 'exists:modUserProfile,email,blocked,1'     // where blocked != 1

Universal

These rules apply to strings, numbers, arrays, and files. Behavior changes depending on the input type.

min:value

The field must have a minimum:

  • Numeric: at least value
  • String: at least value characters
  • Array: at least value items
  • File: at least value kilobytes
php
'age'       => 'min:18',          // number ≥ 18
'title'     => 'min:10',          // string length ≥ 10
'tags'      => 'array|min:2',     // at least 2 elements
'avatar'    => 'file|min:100',    // at least 100 KB
'uploads'   => 'min:2',           // file array: at least 2 files

max:value

The field must not exceed:

  • Numeric: max number
  • String: max characters
  • Array: max items
  • File: max kilobytes
php
'age'       => 'max:99',
'title'     => 'max:255',
'tags'      => 'array|max:5',
'avatar'    => 'file|max:500',
'uploads'   => 'max:10',

between:min,max

The field must fall between a min and max:

  • Numeric: value in range
  • String: length in range
  • Array: number of elements in range
  • File: size in KB
php
'age'       => 'between:18,65',
'title'     => 'between:10,100',
'tags'      => 'array|between:2,4',
'avatar'    => 'file|between:100,500',
'uploads'   => 'between:1,5',

size:value

The field must match exactly the given size:

  • Numeric: equal to value
  • String: exact number of characters
  • Array: exact number of elements
  • File: exactly value KB
php
'code'      => 'size:6',            // string of 6 characters
'count'     => 'size:10',           // number == 10
'tags'      => 'array|size:3',      // array must have exactly 3 items
'avatar'    => 'file|size:300',     // file must be exactly 300 KB

required

The field must be present and not empty.

php
'name' => 'required'

required_if:anotherfield,value

The field is required only when anotherfield equals value.

php
'promo_code' => 'required_if:has_discount,true'

nullable

Allows the field to be null or empty. Skips other validation rules if null.

php
'bio' => 'nullable|string'

empty

The field must be empty. Use this to explicitly require the absence of a value.

php
'honeypot' => 'empty'

filled

The field must be present and not empty if it's in the input.

php
'nickname' => 'filled'

exclude

The field will be excluded from validated data.

php
'debug' => 'exclude'

exclude_if:anotherfield,value

The field will be excluded from validated data only if another field has the given value.

php
'discount_reason' => 'exclude_if:has_discount,false'

Excluded fields are removed from the validated output, even if they pass validation.

© PageBlocks 2019-present