sometimes

Runs the field's remaining rules only when the key is actually present in the input data.

Signature
sometimes

Errors

Nothing. It suppresses the rest of the field's rules when the key is absent rather than raising anything itself.

In some situations, you may wish to run validation checks against a field only if that field is present in the data being validated. Adding sometimes to the front of a rule list does exactly that: if the key is missing from the input, every other rule on that field is skipped.

The PATCH rule set

This is the rule that makes partial updates workable. 'email' => 'sometimes|required|email|unique:users' reads as: you do not have to send an email, but if you do, it must be a non-empty, well-formed, unused address. Without sometimes, the same rules force every client to resend every field on every update.

Not the same as nullable

sometimes keys off the key being absent; nullable keys off the value being null. sometimes|required therefore rejects ['email' => null], treating a deliberately sent null as an attempt to blank the field. If you want the client to be able to clear a value, use sometimes|nullable.

Two different features share this name

The $validator->sometimes() method is unrelated to the rule. It appends rules when a closure you supply returns true, receiving an Illuminate\Support\Fluent of the input — and, for wildcard attributes, a second argument holding the current array item.

Example

Example · php
<?php

use Illuminate\Support\Facades\Validator;

$rules = ['email' => 'sometimes|required|email'];

// Key absent: every following rule is skipped.
var_dump(Validator::make([], $rules)->passes());

// Key present but empty: "required" now applies and fails.
var_dump(Validator::make(['email' => ''], $rules)->passes());

// Key present and valid.
var_dump(Validator::make(['email' => '[email protected]'], $rules)->passes());

// bool(true)
// bool(false)
// bool(true)

When to use it

  • Writing a PATCH endpoint where any subset of the profile fields may be sent.
  • Validating an optional filter parameter on a listing endpoint only when the caller supplies it.
  • Skipping expensive unique or exists lookups for fields the client did not touch.
  • Adding extra rules dynamically when another field crosses a threshold, via the sometimes() method.

Frequently asked questions

What is the difference between sometimes and nullable?

`sometimes` skips the rules when the key is absent from the payload; `nullable` allows the value to be null when the key is present. `sometimes|required` therefore rejects an explicit null, while `nullable` accepts it. Combine them as `sometimes|nullable` when a client may either omit a field or send null to clear it.

How do I validate a PATCH request where any field may be missing?

Prefix each field with `sometimes`, then keep the rest of its rules unchanged — typically `sometimes|required|...`. `validated()` returns only the keys that were sent, so it can be handed directly to `update()` without overwriting untouched columns.

Why does the sometimes rule have to come first?

It does not strictly have to, but putting it first is the convention and keeps rule lists readable. What matters is that `sometimes` and the rules it guards are on the same field; the validator checks for presence before evaluating the field's other rules regardless of position.

More examples

A form request that serves both store and update

`validated()` returns only the keys that were actually sent, so passing it straight to `$user->update()` touches nothing else. Note the deliberate mix: `name` uses `sometimes|required` (send it or omit it, never blank it) while `bio` uses `sometimes|nullable` (omit it, or send null to clear it).

Example · php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name'  => ['sometimes', 'required', 'string', 'max:255'],
            'email' => [
                'sometimes',
                'required',
                'email',
                Rule::unique('users')->ignore($this->route('user')),
            ],
            'bio'   => ['sometimes', 'nullable', 'string', 'max:1000'],
        ];
    }
}

// PATCH /users/7 with body {"name": "Taylor"}
//
// print_r($request->validated());
//
// Array
// (
//     [name] => Taylor
// )

The $validator->sometimes() method

The first argument is the field (or fields) to conditionally validate, the second the rules to add, the third a closure returning true when they should apply. The `$input` argument is an `Illuminate\Support\Fluent` wrapping the data and files under validation, so `$input->games` reads the submitted value.

Example · php
<?php

use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Fluent;

$validator = Validator::make(['email' => '[email protected]', 'games' => 120], [
    'email' => 'required|email',
    'games' => 'required|integer|min:0',
]);

// Add rules only when the closure returns true.
$validator->sometimes('reason', 'required|max:500', function (Fluent $input) {
    return $input->games >= 100;
});

// Several fields at once.
$validator->sometimes(['reason', 'cost'], 'required', function (Fluent $input) {
    return $input->games >= 100;
});

var_dump($validator->fails());
print_r($validator->errors()->keys());

// bool(true)
// Array
// (
//     [0] => reason
//     [1] => cost
// )

Per-row rules with the second closure argument

When you do not know the array index, the closure's second argument gives you the individual item currently being validated, so row 0 is checked with the `email` rule and row 1 with `url`. `$item` is a Fluent when the attribute data is an array; otherwise it is a plain string.

Example · php
<?php

use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Fluent;

$input = [
    'channels' => [
        ['type' => 'email', 'address' => '[email protected]'],
        ['type' => 'url',   'address' => 'https://example.com'],
    ],
];

$validator = Validator::make($input, [
    'channels.*.type'    => 'required|in:email,url',
    'channels.*.address' => 'required',
]);

$validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) {
    return $item->type === 'email';
});

$validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) {
    return $item->type !== 'email';
});

var_dump($validator->passes());   // bool(true)

Discussion

  • Be the first to comment on this lesson.