In Laravel, when would you use Validator::make() instead of $request->validate(), and how do fails(), validate() and validated() differ?
answer
- any array, not just the request
- no automatic throw or redirect
- fails() / passes() return booleans
- validate() throws, validated() throws too
- errors() gives a MessageBag
basics
~20 sValidator::make($data, $rules) validates any array and does nothing on failure until you ask. fails() returns a boolean and fills errors(); validate() throws ValidationException or returns the validated array; validated() returns the validated data and also throws if invalid.
solid answer
~30 s`Validator::make($data, $rules, $messages, $attributes)` returns an `Illuminate\Validation\Validator` without running anything. That makes it the tool when the data is not the current request (a CSV row, a webhook payload, a config array), or when you want to decide what failure means yourself. `fails()` and `passes()` run the rules and return a boolean; `errors()` then returns the `MessageBag`. `validate()` runs them and throws `ValidationException` on failure, giving the same redirect or 422 as `$request->validate()`, otherwise returns the validated data. `validated()` also throws when the data is invalid, so call it after checking `fails()`. `safe()` wraps the same data in a `ValidatedInput`.
code
php · 16 lines<?php
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($payload, [
'event' => ['required', 'in:import.completed,import.failed'],
'import_id' => ['required', 'integer'],
]);
if ($validator->fails()) {
return redirect()->route('imports.index')
->withErrors($validator)
->withInput();
}
$data = $validator->validated();go deeper
Know that Validator::make() validates any array and that fails() tells you whether it passed.
Explain fails() versus validate() versus validated(), and when to redirect manually with withErrors().
Use manual validators in jobs and commands without leaking exceptions, and read failed() for rule-aware handling.
Decide where non-HTTP validation lives so imports, webhooks and APIs share one rule set instead of drifting apart.
## What Validator::make() gives you `Illuminate\Support\Facades\Validator::make($data, $rules, $messages = [], $attributes = [])` creates a validator for **any array**. The `validator()` helper does the same (`validator($data, $rules)`); with no arguments it returns the factory. Creating the validator runs nothing yet. You then choose how to evaluate it. ## The four entry points | Method | Runs the rules? | Returns | On invalid data | |---|---|---|---| | `fails()` | yes | `true` if invalid | returns `true`; errors in `errors()` | | `passes()` | yes | `true` if valid | returns `false` | | `validate()` | yes, if not yet run | validated array | throws `ValidationException` | | `validated()` | yes, if not yet run | validated array | throws `ValidationException` | Details worth knowing: - `passes()` resets and refills the error bag each time it runs; `fails()` is just `! passes()`. - `errors()` (alias `messages()` / `getMessageBag()`) returns an `Illuminate\Support\MessageBag`. - `validated()` reuses the result if the rules already ran, so `fails()` then `validated()` does not validate twice. - `validateWithBag('bag')` is `validate()` with a named error bag set on the exception. - `safe()` returns the validated data as a `ValidatedInput` with `only()`, `except()` and `merge()`. ## When to reach for it 1. **The data is not the request.** Rows from an uploaded CSV, a decoded webhook payload, a JSON column, input to an Artisan command. `$request->validate()` only ever validates `$request->all()`. 2. **Failure is not an HTTP error.** In a queue job or console command there is no "redirect back"; you want a boolean and a message list to record. 3. **You want to redirect somewhere specific** with the errors: `return redirect()->route('imports.create')->withErrors($validator)->withInput();` 4. **You need to add rules or checks before running**: `sometimes()`, `after()`, `stopOnFirstFailure()` are called on the instance before `fails()`. ## Keeping the automatic behaviour If you only needed a validator instance to attach an `after()` callback but still want the standard HTTP handling, call `validate()` at the end: ```php Validator::make($request->all(), $rules) ->after(fn ($v) => $this->checkQuota($v)) ->validate(); ``` This throws the same `ValidationException` that `$request->validate()` would, so the exception handler produces the usual redirect or 422. ## A common bug: validated() before checking ```php $validator = Validator::make($row, $rules); $data = $validator->validated(); // throws if the row is invalid ``` `validated()` is not a "give me what passed" filter for bad data: when there are errors it throws. In non-HTTP code that exception is easy to leave uncaught. Check `fails()` first, then read `validated()`. ## Reading errors After `fails()`: - `$validator->errors()->toArray()` — field → list of messages, handy to store with an import row. - `$validator->failed()` — field → the rule names that failed, useful when code must react to a specific rule rather than a message. How those messages are worded and shown in views belongs to the error-bag side of validation; here the point is that a manual validator hands them back as data. ## Choosing - Controller, current request, standard behaviour → `$request->validate()` or a form request. - Non-request data, or failure handled by your code → `Validator::make()` with `fails()`. - Non-request data, but failure should still become an HTTP validation error → `Validator::make(...)->validate()`. ## A note on custom messages and attribute names The third and fourth arguments of `Validator::make()` are the same two arrays a form request would supply through its own methods: custom messages keyed by rule or `field.rule`, and friendly names for `:attribute`. In non-HTTP code — an import that reports "Row 12: The unit price field must be a number." — the attribute names matter more than usual, because the raw CSV header (`unit_price_eur`) is what the user would otherwise see. ## What interviewers probe Whether you know that `validated()` throws, that `fails()` does not respond on its own, and that `validate()` is the bridge back to standard HTTP handling.
- Does calling fails() and then validated() run the rules twice?No. `validated()` only runs the rules if the validator has not produced a message bag yet. After `fails()` it reuses the stored result, throwing if there were errors and otherwise returning the validated array.
- How do you tell which rule failed rather than reading the message?Call `$validator->failed()`, which returns an array keyed by attribute, each holding the names of the rules that failed with their parameters. That is safer than matching message text, which changes with translations.
saying these in an interview costs you the question
- Validator::make() throws immediately when the data is invalid
- validated() returns the valid subset even when some fields failed
- fails() redirects the user back automatically
- Validator::make() can only validate the current request