In a Laravel controller, how do $request->input(), query(), all(), only() and except() differ when reading a search form?
answer
- body plus query string, body wins
- dot paths reach nested fields
- query() reads the query string only
- only() drops keys that were not sent
- all() also includes uploaded files
basics
~20 sinput() reads a field from the request body and query string together (body wins on a clash) and follows dot paths; query() reads only the query string; all() returns every field plus uploaded files; only() and except() return a filtered subset.
solid answer
~40 sOn `Illuminate\Http\Request`, `input('keyword', 'php')` reads from the body (form fields, or the JSON payload when the `Content-Type` is JSON) merged with the query string, the body winning on a clash, and accepts dot paths such as `input('filters.salary_min')` or `input('skills.*')`. `query('page')` reads only the query string and looks up a top-level key. `all()` returns every input field merged with uploaded files. `only(['keyword', 'location'])` returns just the keys that were actually sent, while `except('_token')` returns everything minus the listed keys. The default argument applies only when a key is absent: an empty field that the default middleware turned into `null` comes back as `null`.
code
php · 17 lines<?php
use Illuminate\Http\Request;
// GET /jobs?keyword=php&location=&filters[salary_min]=50000
public function index(Request $request)
{
$keyword = $request->input('keyword'); // 'php'
$location = $request->input('location', 'Anywhere'); // null: key exists, emptied to null
$salary = $request->input('filters.salary_min'); // '50000'
$viaQuery = $request->query('filters.salary_min'); // null: query() has no dot paths
$criteria = $request->only(['keyword', 'sort']); // ['keyword' => 'php']
$shape = $request->all(['keyword', 'sort']); // ['keyword' => 'php', 'sort' => null]
return view('jobs.index', compact('keyword', 'location', 'salary', 'criteria'));
}go deeper
Know that input() reads a field from any HTTP verb, query() reads only the URL query string, and only()/except() filter the input array.
Explain the body-wins merge in input(), dot and wildcard paths, and why a default never fires for a field the middleware converted to null.
Show the habit of allow-listing with only() before anything that writes, and spot bugs where query() and input() disagree on POST endpoints with URL parameters.
Frame input access as a boundary: raw strings enter here, so teams standardise on typed helpers and validation instead of sprinkling input() defaults through controllers.
## The request object and where input comes from In a Laravel controller you type-hint `Illuminate\Http\Request` and the service container injects the current request. Every field the browser or API client sent lives in one of three **parameter bags**: the query string (`?keyword=php&page=2`), the form body (`application/x-www-form-urlencoded` or `multipart/form-data`), or a JSON payload when the `Content-Type` header contains `/json` or `+json`. Laravel's accessor methods decide which bags they read, and that decision is what interviewers probe. Take a job-board search form submitted with `GET /jobs?keyword=php&location=&filters[salary_min]=50000&remote=1`. For a `GET` or `HEAD` request the "input source" *is* the query string, so every method below sees the same data; the differences show up on `POST` forms and JSON requests. ## The five accessors side by side | Method | Reads | Dot paths | Missing key returns | |---|---|---|---| | `input($key, $default)` | body (or JSON) + query string | yes, including `*` | `$default` | | `query($key, $default)` | query string only | no, top-level key only | `$default` | | `all()` | body + query + uploaded files | via `all(['a.b'])` | key set to `null` when you pass keys | | `only($keys)` | same data as `all()` | yes | key omitted | | `except($keys)` | same data as `all()` | yes | not applicable | A few details make the table precise: - **`input()` merges with the body winning.** The source code builds the array as input-source `+` query, and PHP's array union keeps the left-hand value, so a `POST` body field named `page` hides `?page=3`. - **`input()` without arguments** returns the whole merged array, but *not* uploaded files; `all()` adds files on top. - **Dot and wildcard paths** work on `input()`: `input('filters.salary_min')` reaches `filters[salary_min]`, and `input('skills.*.name')` collects a column from an array of inputs. - **`query()` does not traverse dot paths.** It looks up `all()[$key]` on the query bag, so `query('filters.salary_min')` returns the default; use `input('filters.salary_min')` or read `query('filters')` and index into the array. - **`only()` versus `all($keys)`.** `only(['keyword', 'sort'])` silently omits `sort` if it was never sent, while `all(['keyword', 'sort'])` returns `sort => null`. Pick the one whose shape your code expects. - **Dynamic properties** such as `$request->keyword` read the input first and fall back to a route parameter of the same name. ## Defaults apply to absence, not emptiness Laravel 13's default global middleware includes `TrimStrings` and `ConvertEmptyStringsToNull`, so the empty `location=` above reaches your controller as `null`. That matters for defaults: 1. `$request->input('location', 'Anywhere')` returns `null`, because the key exists and `data_get` only falls back when a key is absent. 2. `$request->query('location', 'Anywhere')` returns `'Anywhere'`, because `query()` uses the null-coalescing operator on the raw bag. 3. `$request->input('location') ?? 'Anywhere'` is the explicit, readable way to treat "empty" and "absent" alike. This asymmetry is a frequent source of "my default never applies" bugs in search forms, where users clear a field and resubmit. ## Choosing the right accessor - Use **`input()`** as the everyday default; it does not care whether the form used `GET` or `POST`, or whether an SPA sent JSON. - Use **`query()`** when the location of the value matters, for example a `?page=` or `?sort=` parameter on a `POST` endpoint that must not be overridden by a body field. - Use **`only()`** to build an allow-list before passing data onward, for instance to a query builder or a model; it is the safer habit than handing `all()` to anything that writes. - Use **`except()`** for logging or re-displaying input without secrets or the `_token` field. - Use **`all()`** when you genuinely need files and fields together. ## What stays outside these methods These accessors only *read*; they do not validate. A value from `input()` is whatever the client sent, typed as a string or an array. Converting it to a boolean, integer, date or enum is the job of the typed helpers (`boolean()`, `integer()`, `date()`, `enum()`), and checking it is the job of validation. Route parameters are read with `$request->route('id')` or by type-hinting them on the controller method, not with `input()` (except through the dynamic-property fallback). ## A worked comparison on a JSON request Single-page apps and mobile clients usually post JSON. Suppose the job board's "save search" endpoint receives `POST /searches?page=2` with `Content-Type: application/json` and the body `{"keyword": "php", "filters": {"remote": true}}`: - `input('filters.remote')` returns the boolean `true`, because the JSON payload became the input source and JSON keeps native types. - `input('page')` returns `'2'` from the query string, since the body has no `page` key to win the merge. - `query('keyword')` returns `null`: the keyword lives in the JSON body, not the URL. - `only(['keyword', 'filters.remote'])` returns `['keyword' => 'php', 'filters' => ['remote' => true]]`, rebuilding the nesting from the dot path. The same controller code therefore works for a classic HTML form and for a JSON client, which is the main reason `input()` is the recommended default and `query()` the deliberate exception.
- On a POST request with both ?page=3 in the URL and a page field in the body, what does $request->input('page') return?The body value. `input()` builds its array as the input source (the form body, or the JSON payload) `+` the query bag, and PHP's array union keeps the left-hand entry. `$request->query('page')` still returns `3`, so reach for `query()` when the URL value must win.
- Why prefer $request->only([...]) over $request->all() when passing search criteria or attributes onward?`only()` is an allow-list: fields an attacker adds to the form, and keys you never expected, cannot reach the query builder or the model. `all()` passes everything, including uploaded files and extra keys. `only()` also omits unsent keys, so downstream code sees exactly what the client chose to send.
saying these in an interview costs you the question
- input() only reads POST bodies, so GET searches need query()
- The default in input('x', 'y') applies when the field is empty
- query('filters.salary_min') follows dot notation like input()
- only() returns requested keys with null for the missing ones
- all() excludes uploaded files