Validating Strings and Numbers in Laravel

Most validation work comes down to two questions: “is this text acceptable?” and “is this number in range?” The rules in this guide answer both. They cover the everyday cases (a name, a price, an email address) along with the awkward ones that show up once your form goes to production: a value that arrives as a string instead of a number, a Unicode character that breaks an ASCII regex, an MX check that needs a PHP extension nobody installed.

Accepting Text Input

The string rule rejects anything that is not a PHP string. Arrays, integers, and booleans all fail:

$request->validate([
    'title' => ['required', 'string', 'max:255'],
    'bio'   => ['nullable', 'string', 'max:1000'],
]);

When a field can legitimately arrive as null (a JSON body, for instance), pair string with nullable. Without it, null fails the string check before any other rule runs.

An exact character count uses size:

'country_code' => ['required', 'string', 'size:2'],   // "US", "DE"
'otp'          => ['required', 'string', 'size:6'],    // "482910"

For a length range, between is shorter than writing both min and max:

'username' => ['required', 'string', 'between:3,30'],

If the field might hold either a string or an integer (a polymorphic ID from a frontend), skip the string rule entirely and validate each possibility with a custom rule:

// Accept either a numeric ID or a UUID slug
'reference' => ['required', function ($attribute, $value, $fail) {
    if (!is_string($value) && !is_int($value)) {
        $fail('The reference must be a string or integer.');
    }
}],

When a field may arrive as either a string or an array (a tag input that sends a single value or a list), the cleanest approach is to normalize in prepareForValidation() and then validate a single type:

protected function prepareForValidation(): void
{
    if (is_string($this->tags)) {
        $this->merge(['tags' => [$this->tags]]);
    }
}

public function rules(): array
{
    return [
        'tags'   => ['required', 'array', 'max:10'],
        'tags.*' => ['string', 'max:50'],
    ];
}

This avoids having to branch on type at the validation layer. The input always becomes an array before rules run.

The contains rule checks whether an array includes specific values. It has nothing to do with substring matching on strings. For “string contains substring” checks, use regex.

The Fluent String Builder

Laravel 13 provides Rule::string() with chainable constraints:

use Illuminate\Validation\Rule;

'username' => [
    'required',
    Rule::string()
        ->min(3)
        ->max(30)
        ->alphaDash(ascii: true),
],

Available methods: alpha, alphaDash, alphaNumeric, ascii, between, doesntEndWith, doesntStartWith, endsWith, exactly, lowercase, max, min, startsWith, uppercase. The builder is conditionable, so when() and unless() work too:

Rule::string()
    ->min(2)
    ->when($requireAscii, fn ($rule) => $rule->ascii()),

Alpha, AlphaDash, and AlphaNum

These three rules control which characters are allowed. By default all three are Unicode-aware, meaning accented characters like ñ, ü, or Cyrillic letters pass:

RuleAllowsExample passes
alphaLetters only ([\p{L}], [\p{M}])François
alpha_dashLetters, digits, dashes, underscoresvue-router_2
alpha_numLetters and digits onlyRoom4B

To restrict to ASCII range (a-z, A-Z, 0-9), append :ascii:

'slug'     => 'required|alpha_dash:ascii|max:80',
'username' => 'required|alpha_num:ascii|between:3,20',

None of these rules accept spaces. A “name” field that should allow letters and spaces needs a different approach:

// regex that allows Unicode letters, spaces, hyphens, and apostrophes
'name' => ['required', 'string', 'max:100', 'regex:/^[\p{L}\s\-\']+$/u'],

For slugs and machine identifiers, alpha_dash:ascii is the standard choice. It permits exactly the characters found in URL slugs and database column names:

'slug'       => 'required|alpha_dash:ascii|between:2,80',
'api_key'    => 'required|alpha_num:ascii|size:32',
'short_code' => 'required|alpha:ascii|between:2,5|uppercase',

Integers, Numerics, and Digits

These three rules look similar but solve different problems, and picking the wrong one causes the most common number-validation bugs.

integer uses PHP’s FILTER_VALIDATE_INT. It accepts "42" (a string containing an integer) by default. It rejects 3.14, "3.14", and empty strings. Use integer:strict to reject string integers:

'quantity' => 'required|integer|min:1',

// API that must receive an actual int type, not "5"
'count' => 'required|integer:strict|min:0',

numeric uses PHP’s is_numeric(). It accepts integers, floats, and their string representations: 42, "42", 3.14, "3.14", "-0.5". Use numeric:strict to accept only int/float types:

'price' => 'required|numeric|min:0.01|max:999999.99',

// The field arrives as a proper float from a typed JSON body
'latitude' => 'required|numeric:strict|between:-90,90',

digits:N checks that the value is an integer with exactly N digits. It validates string length, not numeric value:

'zip' => 'required|digits:5',        // "02134" passes, "2134" fails
'pin' => 'required|digits_between:4,6', // 4, 5, or 6 digits

Related digit rules: max_digits:N caps the digit count, min_digits:N sets a floor:

'account_number' => 'required|digits_between:8,12',
'routing_number' => 'required|digits:9',
'invoice_number' => 'required|integer|max_digits:10',

Nullable Numbers

Numeric fields from optional form inputs often arrive as null or as an empty string. Use nullable to let null pass through, and remember that empty strings are converted to null by Laravel’s ConvertEmptyStringsToNull middleware:

'discount'   => ['nullable', 'numeric', 'between:0,100'],
'max_weight' => ['nullable', 'integer', 'min:1'],

The nullable rule allows the value to be null, but the field itself must still be present in the request. If the field can be omitted entirely, sometimes is the right tool – it tells Laravel to skip validation when the key is missing. The two are often combined: ['sometimes', 'nullable', 'numeric'] means “if you send it, it can be null or a number; if you do not send it, that is fine too.”

For API endpoints where a field can hold either an integer or a decimal (a generic “number” input), numeric covers both:

'threshold' => ['required', 'numeric', 'min:0'],  // accepts 5, 5.0, "5.5"

When to Use Which

ScenarioRuleWhy
Quantity, age, page numberinteger|min:1Whole numbers only
Price, weight, coordinatesnumeric|between:...Allows decimals
ZIP code, PIN, OTPdigits:N or digits_between:N,MExact digit length matters
Precise decimal places (currency)decimal:2 or decimal:2,4Controls fraction part
Positive integer, no zerointeger|min:1gt:0 also works but needs type match

The strict variants (integer:strict, numeric:strict) are useful for typed JSON APIs where the client sends actual numbers, not string representations. Without strict mode, "42" and 42 both pass integer. With it, only 42 passes. This distinction matters when your API contract specifies types and you want to enforce them at the validation layer rather than relying on implicit casting later.

A common confusion: numeric accepts "1,500" in some locales? No. PHP’s is_numeric() rejects comma-separated numbers. Input like "1,500" or "3,5" (European decimal) will fail. Strip commas or convert the separator before validation:

// In a FormRequest's prepareForValidation()
protected function prepareForValidation(): void
{
    if (is_string($this->price)) {
        $this->merge([
            'price' => str_replace(',', '.', $this->price),
        ]);
    }
}

Constraining Length and Value

The min, max, size, and between rules change meaning based on the type of the field:

Typemin:5 meansmax:100 means
stringat least 5 charactersat most 100 characters
numeric / integervalue >= 5value <= 100
arrayat least 5 elementsat most 100 elements
fileat least 5 KBat most 100 KB

Without an explicit type rule, Laravel guesses the type from the input. This leads to bugs: a field containing "42" might be treated as numeric one request and as a string the next, depending on whether other rules hint at the type. Always pair size rules with a type rule:

// Ambiguous: does max:255 mean 255 characters or numeric value 255?
'code' => 'required|max:255',

// Clear: 255 characters
'code' => 'required|string|max:255',

The size rule checks for an exact match:

'state' => 'required|string|size:2',           // exactly 2 chars
'answer' => 'required|integer|size:42',         // exactly 42
'coordinates' => 'required|array|size:2',       // exactly 2 elements

between is inclusive on both ends:

'age'    => 'required|integer|between:18,120',
'rating' => 'required|numeric|between:0,5',
'title'  => 'required|string|between:10,200',

gt, gte, lt, lte: Comparing Against Another Field

These rules compare the current field to another field’s value (or to a literal value). Both fields must be of the same type:

$request->validate([
    'min_price' => 'required|numeric|min:0',
    'max_price' => 'required|numeric|gt:min_price',

    'start_qty' => 'required|integer|min:1',
    'end_qty'   => 'required|integer|gte:start_qty',
]);

For a simple “greater than zero” check without a second field, min:1 (for integers) or gt:0 (for numerics) does the job:

'amount' => 'required|numeric|gt:0',   // must be positive, decimals OK
'seats'  => 'required|integer|min:1',  // must be at least 1

A practical difference between gt and min: gt:0 fails on exactly 0 (strictly greater), while min:0 allows 0 (greater than or equal). For “the value must not be zero or negative”, gt:0 is the right choice. For “zero is fine, negative is not”, use min:0.

Capping the maximum numeric value works the same way:

'percentage' => 'required|integer|between:0,100',
'latitude'   => 'required|numeric|between:-90,90',
'year'       => 'required|integer|min:1900|max:2100',

Email Validation

The email rule defaults to RFC validation only. An address like user@localhost passes because it is technically valid per the RFC spec. For production forms, add more validators:

// Basic: RFC check only
'email' => 'required|email',

// Recommended: RFC + working MX record
'email' => 'required|email:rfc,dns',

// Paranoid: RFC + DNS + Unicode spoof protection
'email' => 'required|email:rfc,dns,spoof',

The available validators:

ValidatorWhat it checksNeeds
rfcSyntax per RFC (default)nothing extra
strictRFC with no warnings (rejects trailing dots, consecutive dots)nothing extra
dnsDomain has a valid MX recordPHP intl extension
spoofNo homograph/deceptive Unicode charactersPHP intl extension
filterPHP’s filter_var checknothing extra
filter_unicodefilter_var allowing Unicode local partsnothing extra

The fluent builder reads more clearly when stacking multiple validators:

use Illuminate\Validation\Rule;

'email' => [
    'required',
    Rule::email()
        ->rfcCompliant(strict: false)
        ->validateMxRecord()
        ->preventSpoofing(),
],

For a newsletter signup where you just need a working inbox, email:rfc,dns catches the worst typos without slowing things down. For a login form, email:rfc alone is fine because the address already exists in your database. For a payment form that must prevent fraud, add spoof to catch Unicode lookalike domains.

One thing to watch: dns and spoof require PHP’s intl extension. Many Docker images and shared hosting environments do not install it by default. If the extension is missing, email:rfc,dns throws a LogicException at runtime, not a graceful fallback. Verify with php -m | grep intl before deploying rules that rely on DNS or spoof checks.

To ensure the email is unique in a database table, chain it with the unique rule:

'email' => 'required|email:rfc,dns|unique:users,email',

Email or Phone

There is no built-in “email or phone” rule. Handle it with a closure that runs each validator and accepts the input if either passes:

use Illuminate\Support\Facades\Validator;

'contact' => ['required', function ($attribute, $value, $fail) {
    $asEmail = Validator::make(
        ['contact' => $value],
        ['contact' => 'email:rfc']
    );

    $asPhone = preg_match('/^\+?[1-9]\d{6,14}$/', (string) $value);

    if ($asEmail->fails() && !$asPhone) {
        $fail('Provide a valid email address or phone number.');
    }
}],

Routing the email branch through Laravel’s own validator keeps the logic consistent with the rest of the form. If you ever switch the email rule (say, to add dns), the closure picks up the change automatically.

URL and UUID

url checks whether the value is a well-formed URL. By default it accepts any scheme. To restrict to web URLs:

'website' => 'required|url:http,https',

A common question: how to accept a URL without the http:// prefix? The url rule requires a scheme, so you have two options. Prepend the scheme before validation, or use a regex:

// Option A: prepend in prepareForValidation
protected function prepareForValidation(): void
{
    if ($this->website && !preg_match('#^https?://#', $this->website)) {
        $this->merge(['website' => 'https://' . $this->website]);
    }
}

// Option B: regex that accepts with or without scheme
'website' => ['required', 'regex:/^(https?:\/\/)?[\w\-]+(\.[\w\-]+)+[\/\w\-\.~:?#\[\]@!$&\'()*+,;=%]*$/'],

active_url goes further: it extracts the hostname with parse_url() and looks up an A or AAAA record via dns_get_record(). This catches typos like gogle.com but adds DNS-lookup latency and can fail for internal hostnames or during outages. Reserve it for public-facing URL fields where reachability actually matters.

You can also restrict to HTTPS-only in production while allowing HTTP in local development:

'webhook_url' => [
    'required',
    app()->isProduction() ? 'url:https' : 'url:http,https',
],

uuid validates RFC 9562 UUIDs (versions 1, 3, 4, 5, 6, 7, 8). To restrict to a specific version:

'token'     => 'required|uuid',     // any version
'public_id' => 'required|uuid:4',   // v4 only
'sort_id'   => 'required|ulid',     // ULID format instead

Use uuid:4 if your system generates v4 UUIDs and you want to reject v1 (time-based) or v7 (sortable) IDs that might come from other systems. Use bare uuid when accepting IDs from third-party integrations where the version is not under your control.

Regex Patterns

The regex and not_regex rules run the value through PHP’s preg_match(). The pattern must include delimiters:

'slug' => ['required', 'regex:/^[a-z0-9]+(-[a-z0-9]+)*$/'],

If the pattern contains a pipe character |, you must use array syntax. The string syntax breaks because | is also the rule separator:

// Broken: Laravel sees three separate rules
'code' => 'required|regex:/^(AB|CD|EF)\d+$/',

// Fixed: array syntax
'code' => ['required', 'regex:/^(AB|CD|EF)\d+$/'],

Special characters (anchors, quantifiers, Unicode classes) work the same as in raw PHP:

// Unicode letters and spaces, 2-50 chars
'name' => ['required', 'regex:/^[\p{L}\s]{2,50}$/u'],

// Hex color code
'color' => ['required', 'regex:/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/'],

// No consecutive spaces
'comment' => ['required', 'string', 'not_regex:/\s{2,}/'],

To set a custom error message for a regex rule, reference it by attribute and rule name:

$request->validate(
    ['slug' => ['required', 'regex:/^[a-z0-9\-]+$/']],
    ['slug.regex' => 'The slug may only contain lowercase letters, numbers, and hyphens.']
);

Regex error messages default to “The {field} format is invalid”, which tells the user nothing about what went wrong. Always provide a custom message that describes the expected format.

not_regex is the inverse – it rejects values that match the pattern. Useful for blocking unwanted input:

// Block HTML tags in a plain-text bio
'bio' => ['required', 'string', 'max:500', 'not_regex:/<[^>]+>/'],

// Reject strings that are only whitespace
'title' => ['required', 'string', 'not_regex:/^\s+$/'],

For complex validation logic where a single regex is hard to read, consider a custom rule class instead. A regex that needs a comment to explain itself is probably better as a named rule.

Phone Number Validation

Laravel has no built-in phone rule, so the choice is between writing a regex yourself or pulling in a package.

Regex works for a single expected format:

// E.164 international format: +1234567890 (7-15 digits after +)
'phone' => ['required', 'regex:/^\+[1-9]\d{6,14}$/'],

// North American: (555) 123-4567 or 555-123-4567
'phone' => ['required', 'regex:/^(\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}$/'],

A package handles international formats, carrier validation, and formatting. The most popular is propaganistas/laravel-phone:

composer require propaganistas/laravel-phone
use Propaganistas\LaravelPhone\Rules\Phone;

'mobile' => ['required', (new Phone)->mobile()->country(['US', 'GB', 'DE'])],

The package validates against Google’s libphonenumber library, which covers number plans for every country. For any form accepting international phone numbers, a regex alone is not enough – country-specific formatting rules, variable length, and carrier prefixes make it impractical.

A middle ground: validate the format loosely on the backend and verify the number exists by sending an SMS or calling a verification API (Twilio, Vonage). This separates “looks like a phone number” from “is a real phone number”:

// Loose format check, then verify asynchronously
'phone' => ['required', 'string', 'min:7', 'max:16', 'regex:/^\+?\d[\d\s\-()]+$/'],

Boolean as String

HTML checkboxes submit "1" or nothing. The boolean rule accepts true, false, 1, 0, "1", and "0":

'newsletter' => 'boolean',
'terms'      => 'accepted', // must be "yes", "on", 1, "1", true, or "true"

When you need the field to be an actual true/false (typed JSON, for example), use the strict mode:

'is_active' => 'required|boolean:strict',

A common trap with checkboxes: browsers do not send unchecked checkboxes at all. The field will be absent from the request, not false. Use sometimes or set a default in prepareForValidation():

// Option A: only validate if present
'newsletter' => 'sometimes|boolean',

// Option B: ensure it always has a value
protected function prepareForValidation(): void
{
    $this->mergeIfMissing(['newsletter' => false]);
}

Decimal Precision

The decimal rule enforces exact decimal places:

'price'       => 'required|decimal:2',     // 9.99 OK, 9.9 fails
'measurement' => 'required|decimal:1,4',   // 9.5 to 9.1234 OK
'percentage'  => 'required|decimal:0,2',   // 100 or 99.5 or 99.55 OK

This pairs well with numeric or between to cap the overall value:

'rate' => 'required|numeric|decimal:2|between:0.01,999.99',

For currency stored as integers (cents), skip decimal and validate as an integer:

'amount_cents' => 'required|integer|min:1|max:99999999',

Other String and Number Checks

Several rules handle specific formats that do not fit the categories above.

Case rules enforce lowercase or uppercase on the entire value:

'slug'         => 'required|string|lowercase|alpha_dash:ascii',
'country_code' => 'required|string|uppercase|size:2',
'locale'       => 'required|string|lowercase|size:2',

Prefix and suffix checks avoid regex for simple cases:

'callback_url' => ['required', 'string', 'starts_with:https://'],
'filename'     => ['required', 'string', 'doesnt_end_with:.exe,.bat,.sh'],
'ref'          => ['required', 'string', 'ends_with:-ref'],

Network and identifier formats:

'server_ip'    => 'required|ip',          // IPv4 or IPv6
'gateway'      => 'required|ipv4',        // IPv4 only
'device_mac'   => 'required|mac_address',
'config'       => 'required|json',        // valid JSON string
'theme_color'  => 'required|hex_color',   // #fff or #a1b2c3
'csv_row'      => 'required|ascii',       // 7-bit ASCII only

multiple_of verifies divisibility, useful for pack sizes, seating, and pagination:

'pack_size'  => 'required|integer|multiple_of:6',   // 6, 12, 18...
'page_size'  => 'required|integer|multiple_of:10',  // 10, 20, 30...
'seat_count' => 'required|integer|multiple_of:2',   // pairs only

Recipes

Product Form

public function rules(): array
{
    return [
        'name'        => ['required', 'string', 'between:2,200'],
        'sku'         => ['required', 'alpha_dash:ascii', 'max:40', 'unique:products,sku'],
        'price'       => ['required', 'numeric', 'decimal:2', 'between:0.01,99999.99'],
        'weight_kg'   => ['nullable', 'numeric', 'min:0.001', 'max:1000'],
        'quantity'    => ['required', 'integer', 'min:0', 'max:999999'],
        'category_id' => ['required', 'integer', 'exists:categories,id'],
        'tags'        => ['nullable', 'array', 'max:10'],
        'tags.*'      => ['string', 'alpha_dash:ascii', 'max:30'],
    ];
}

Registration Form with Phone or Email

public function rules(): array
{
    return [
        'name'     => ['required', 'string', 'max:100', 'regex:/^[\p{L}\s\-\']+$/u'],
        'email'    => ['required', 'email:rfc,dns', 'unique:users,email'],
        'phone'    => ['nullable', 'regex:/^\+[1-9]\d{6,14}$/'],
        'password' => ['required', 'string', 'min:8', 'confirmed'],
        'age'      => ['required', 'integer', 'between:13,120'],
        'website'  => ['nullable', 'url:http,https', 'max:255'],
    ];
}

API Settings Endpoint

$validated = $request->validate([
    'display_name' => ['required', Rule::string()->min(2)->max(50)],
    'locale'       => ['required', 'string', 'size:2', 'lowercase'],
    'timezone'     => ['required', 'string', 'timezone:all'],
    'page_size'    => ['required', 'integer', 'between:10,100', 'multiple_of:10'],
    'is_public'    => ['required', 'boolean:strict'],
    'avatar_url'   => ['nullable', 'url:https', 'max:500'],
    'bio'          => ['nullable', 'string', 'max:500'],
]);

Price Range Filter

$request->validate([
    'min_price' => ['nullable', 'numeric', 'min:0'],
    'max_price' => ['nullable', 'numeric', 'gt:min_price'],
    'currency'  => ['required', 'string', 'uppercase', 'size:3'],
]);

Geocoordinates

$request->validate([
    'lat' => ['required', 'numeric', 'between:-90,90'],
    'lng' => ['required', 'numeric', 'between:-180,180'],
    'radius_km' => ['nullable', 'numeric', 'gt:0', 'max:500'],
    'label' => ['nullable', 'string', 'max:100'],
]);

Bulk Import Row

A single row from a CSV import, where every field arrives as a string and needs both type validation and business-logic constraints:

public function rules(): array
{
    return [
        'rows'              => ['required', 'array', 'min:1', 'max:1000'],
        'rows.*.sku'        => ['required', 'alpha_dash:ascii', 'max:40'],
        'rows.*.name'       => ['required', 'string', 'between:2,200'],
        'rows.*.price'      => ['required', 'numeric', 'decimal:2', 'min:0.01'],
        'rows.*.quantity'   => ['required', 'integer', 'min:0'],
        'rows.*.weight'     => ['nullable', 'numeric', 'min:0'],
        'rows.*.country'    => ['required', 'string', 'uppercase', 'size:2'],
        'rows.*.email'      => ['nullable', 'email:rfc'],
        'rows.*.notes'      => ['nullable', 'string', 'max:500'],
    ];
}

Common Mistakes

1. Missing type rule before min/max. The field 'age' => 'required|min:18' treats "25" as a 2-character string, not the number 25. Add integer:

'age' => 'required|integer|min:18',

2. Using digits to cap numeric value. digits:5 means exactly 5 digits long, not “up to 99999”. For a value cap, use integer|max:99999.

3. Expecting numeric to accept commas. Input "1,500.00" fails numeric. Strip formatting in prepareForValidation().

4. Regex without array syntax. Any regex containing | must use array notation, otherwise Laravel splits the rule at the pipe.

5. Email dns without intl extension. The dns and spoof validators need PHP’s intl extension. Without it, a LogicException is thrown at runtime. Check with php -m | grep intl before deploying.

6. active_url in tests. DNS lookups fail for fake domains in test environments. Mock the validator or use url for tests and active_url only in production rules.

7. Alpha rules rejecting accented names. alpha:ascii rejects José and Müller. Drop the :ascii option or switch to a regex that allows the characters you need.

8. Confusing size with max. size:10 means exactly 10 (characters, value, elements). For “up to 10”, use max:10.

9. Using numeric where integer is needed. A quantity field validated with numeric accepts 2.5, which makes no sense for item counts. Use integer for fields that must be whole numbers.

10. Forgetting decimal needs a numeric value. decimal:2 enforces decimal places but does not itself check that the input is a number. Combine it with numeric:

'price' => 'required|numeric|decimal:2|min:0',

Testing Validation Rules

Laravel’s test helpers make it straightforward to verify that your validation rules work as expected:

public function test_product_price_must_be_positive_decimal(): void
{
    $this->post('/products', ['price' => '-5.00'])
        ->assertSessionHasErrors('price');

    $this->post('/products', ['price' => '0'])
        ->assertSessionHasErrors('price');

    $this->post('/products', ['price' => '19.99', /* other required fields */])
        ->assertSessionDoesntHaveErrors('price');
}

For JSON APIs, use assertJsonValidationErrors:

$this->postJson('/api/settings', ['page_size' => 15])
    ->assertJsonValidationErrors(['page_size']);

$this->postJson('/api/settings', ['page_size' => 20])
    ->assertJsonMissingValidationErrors(['page_size']);

When testing regex and email rules with edge cases, test the boundary values that real users will submit:

// Edge cases for email validation
$this->postJson('/api/register', ['email' => 'user@localhost'])
    ->assertJsonValidationErrors(['email']); // fails dns check

$this->postJson('/api/register', ['email' => '[email protected]'])
    ->assertJsonMissingValidationErrors(['email']);

Quick Reference

NeedRule combination
Required text, capped lengthrequired|string|max:255
Optional textnullable|string|max:N
Username (ASCII, dashes OK)required|alpha_dash:ascii|between:3,20
Whole number, positiverequired|integer|min:1
Decimal pricerequired|numeric|decimal:2|min:0.01
Value in rangerequired|integer|between:1,100
One field > anotherrequired|numeric|gt:other_field
Email (production)required|email:rfc,dns
URL (web only)required|url:http,https
Phone (international)required|regex:/^\+[1-9]\d{6,14}$/
UUID v4required|uuid:4
Exact string lengthrequired|string|size:N
JSON payloadrequired|json

Cross-references: