skip to content

Input Retrieval & Old Input

$request->input(), query() and typed helpers like boolean(), date() and enum() read incoming fields; flash() keeps them for the next form. Interviewers probe missing, empty and nested fields.

on this pageshow

explore

questions

6

In a Laravel controller, how do $request->input(), query(), all(), only() and except() differ when reading a search form?

level: juniorimportance: must knowfreq 72%

answer

  1. body plus query string, body wins
  2. dot paths reach nested fields
  3. query() reads the query string only
  4. only() drops keys that were not sent
  5. all() also includes uploaded files

basics

~20 s

input() 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 s

On `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
<?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

for a junior

Know that input() reads a field from any HTTP verb, query() reads only the URL query string, and only()/except() filter the input array.

for a middle

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.

for a senior

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.

for a principal

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
open as a page

In Laravel, how do $request->has(), filled() and missing() differ for a search field submitted empty or not at all?

level: middleimportance: must knowfreq 58%

basics

~20 s

has() is true when the key is present, even with an empty value; filled() also requires a non-blank value; missing() means the key is absent. A blank text field is present but not filled; an unchecked checkbox is missing.

open as a page

In Laravel, how do $request->flash(), flashOnly(), flashExcept() and the old() helper repopulate a form on the next request?

level: middleimportance: should knowfreq 50%

basics

~20 s

flash() copies the current request's input into the session as old input for the next request only; flashOnly() and flashExcept() filter what is copied. On that next request, old('field', $default) reads it back, typically inside a Blade value attribute.

open as a page

In Laravel, what is the difference between $request->merge() and mergeIfMissing(), and why can mergeIfMissing() leave a blank field unset?

level: middleimportance: should knowfreq 34%

basics

~10 s

merge() writes keys into the request's input, overwriting existing values; mergeIfMissing() writes only keys that are absent. A field submitted blank is present as null, so mergeIfMissing() does not replace it with the default.

open as a page

In Laravel, what do $request->boolean(), integer(), date() and enum() return when a field is missing, empty or malformed?

level: middleimportance: should knowfreq 42%

basics

~20 s

boolean() is true only for 1, true, on and yes; integer() is an (int) cast, so bad text gives 0; date() returns null when empty but throws on bad input; enum() returns the default for empty or unknown values.

open as a page

A Laravel API client posts JSON without an Accept header and gets a 302 redirect instead of JSON errors; what does expectsJson() check, and why?

level: seniorimportance: should knowfreq 38%

basics

~20 s

expectsJson() checks the response the client wants, not the body it sent: an AJAX request accepting anything, or JSON as the first Accept type. A JSON body with Accept: / fails both, so errors render as HTML redirects.

open as a page