✅ 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:
$request->validate([
'honeypot' => 'empty',
'username' => 'required|string',
'password' => 'required|min:8'
]);Alternatively, you can define rules as an array:
$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
422status code. - For standard (non-XHR) requests, it redirects back and stores validation errors in the session.
📦 JSON Response Example
{
"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.")
{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
<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.
<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:
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:
$request->validate([
'honeypot' => 'empty',
'username' => 'required|string',
'password' => 'required|string|min:8'
], [
'required' => 'The :attribute field is required.',
]);Field-specific error messages:
$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.
$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():
$validator = Validator::make($request->all(), [
'username' => 'required|string',
'password' => 'required|string|min:8'
]);🚫 Handling Failed Validation
if ($validator->fails()) {
return response()->error('', $validator->errors());
}🧼 Accessing Validated Input
$validated = $validator->validated();⚙️ Behind the Scenes
new Validation($data, $rules, $messages = [], $customAttributes = []);🔄 Automatic Redirection
If you want immediate validation with auto-redirect on failure:
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:
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
{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:
Validator::make($request->all(), [...])
->validateWithBag('register');{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 intoerrorswith no form association - ✅ With named bags you get:
errors.loginold_input.loginerror_message.login- Clear form-specific error handling
🧵 Working with Validation Error Messages
📋 Retrieving All Errors
$errors = $validator->errors();🎯 Messages for a Specific Field
$messages = $errors->get('email');❗️ Check If a Field Has Errors
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".
'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.
'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").
'agree' => 'boolean'declined
The field must be one of: "no", "off", 0, "0", false, or "false".
'refuse_terms' => 'declined'declined_if:anotherfield,value,...
The field must be declined only if another field has the given value.
'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.
'title' => 'string'email
The value must be a valid email address.
'email' => 'required|email'confirmed[:other_field]
There must be a matching field named {field}_confirmation, unless a custom one is specified.
// Default behavior: compares with `password_confirmation`
'password' => 'required|min:8|confirmed'// 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.
'password' => 'same:password_confirmation'different:field
The field under validation must have a different value from the given field.
'username' => 'different:email'in:foo,bar,...
The field value must be one of the following values.
'status' => 'in:active,inactive,pending'not_in:foo,bar,...
The field value must not be one of the following values.
'role' => 'not_in:admin,root'json
The field must be a valid JSON string.
'meta' => 'json'lowercase
The field must be a lowercase string.
'slug' => 'lowercase'uppercase
The field must be an uppercase string.
'code' => 'uppercase'url[:protocols]
The field must be a valid URL. You can optionally define allowed protocols (e.g., http, https, steam).
'website' => 'url' // Any valid URL
'game' => 'url:steam,minecraft' // Must start with allowed schemesuuid[:versions]
The field must be a valid RFC 9562 UUID. You can optionally specify allowed UUID versions (1–8).
'id' => 'uuid' // Any version
'id' => 'uuid:4' // Only version 4
'id' => 'uuid:1,4,6' // Multiple allowed versionsNumbers
numeric
The field must be a valid number (integer or float).
'price' => 'numeric'integer
The field must be a whole number (integer only).
'quantity' => 'integer'decimal:min,max
The field must have a number of decimal places between min and max.
'amount' => 'decimal:2,4' // Accepts 1.23, 5.678, 10.0000gt:field
The field must be greater than the given field.
'price' => 'gt:discounted_price'gte:field
The field must be greater than or equal to the given field.
'end_date' => 'gte:start_date'lt:field
The field must be less than the given field.
'discounted_price' => 'lt:price'lte:field
The field must be less than or equal to the given field.
'start_date' => 'lte:end_date'min_digits:value
The field must be a numeric string and have at least the specified number of digits.
'code' => 'min_digits:6'max_digits:value
The field must be a numeric string and have at most the specified number of digits.
'pin' => 'max_digits:4'Arrays
array
The field under validation must be an array.
'tags' => 'array'contains:foo,bar,...
The array must contain at least one of the given values.
'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.
'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.
'country' => 'in_array:countries.*'
/*
[
'country' => 'UA',
'countries' => ['UA', 'PL', 'DE']
]
*/You can also match against a simple array field:
'item' => 'in_array:available_items'Dates
date
The field must be a valid date string.
'birthdate' => 'date'date_equals:field_or_date
The date must exactly match another field or literal date.
'start' => 'date_equals:end'
'fixed' => 'date_equals:2024-01-01'date_format:format,...
The date must match at least one of the given formats.
'start' => 'date_format:Y-m-d'
'custom' => 'date_format:d/m/Y,H:i'You may provide multiple formats separated by comma:
'start' => 'date_format:Y-m-d,H:i'before:field_or_date
The date must be before the target.
'start' => 'before:end'before_or_equal:field_or_date
The date must be before or equal to the target.
'start' => 'before_or_equal:end'after:field_or_date
The date must be after the given field or literal date.
'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.
'end' => 'after_or_equal:start'Files
file
The field must be a valid uploaded file.
'avatar' => 'file'image
The field must be an image (jpeg, png, gif, webp, ...).
'avatar' => 'image'mimes:jpeg,png,...
The file's MIME type must match one of the given.
'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.
'upload' => 'file|extensions:pdf,docx'Less strict than mimes — based on file name only.
dimensions:min_width=100,max_height=800,...
Validates image dimensions.
'photo' => 'image|dimensions:min_width=200,max_height=500'Supported constraints:
width,heightmin_width,max_widthmin_height,max_height
Database
unique:table,column[,exceptId|field,exceptValue]
The field value must be unique in the given database table and column.
'email' => 'unique:modUserProfile,email'Ignoring current record You can exclude a record by ID:
'email' => 'unique:modUserProfile,email,123' // where id != 123Or by any other column:
'slug' => 'unique:modResource,alias,createdby,3' // where createdby != 3exists:table,column[,exceptId|field,exceptValue]
The field value must already exist in the given table and column.
'country' => 'exists:pbTableValue,code'Exclude a record from check You may skip one by ID or custom column:
'email' => 'exists:modUserProfile,email,123' // where id != 123
'email' => 'exists:modUserProfile,email,blocked,1' // where blocked != 1Universal
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
'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 filesmax:value
The field must not exceed:
- Numeric: max number
- String: max characters
- Array: max items
- File: max kilobytes
'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
'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
'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 KBrequired
The field must be present and not empty.
'name' => 'required'required_if:anotherfield,value
The field is required only when anotherfield equals value.
'promo_code' => 'required_if:has_discount,true'nullable
Allows the field to be null or empty. Skips other validation rules if null.
'bio' => 'nullable|string'empty
The field must be empty. Use this to explicitly require the absence of a value.
'honeypot' => 'empty'filled
The field must be present and not empty if it's in the input.
'nickname' => 'filled'exclude
The field will be excluded from validated data.
'debug' => 'exclude'exclude_if:anotherfield,value
The field will be excluded from validated data only if another field has the given value.
'discount_reason' => 'exclude_if:has_discount,false'Excluded fields are removed from the validated output, even if they pass validation.