In Laravel, how do $request->flash(), flashOnly(), flashExcept() and the old() helper repopulate a form on the next request?
answer
- session key _old_input
- survives exactly one next request
- files are never flashed
- old($key, $default) with dot paths
- an Eloquent model works as default
basics
~20 sflash() 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.
solid answer
~40 s`$request->flash()` calls the session's `flashInput()` with `$request->input()`, storing it under the `_old_input` key as flash data that lives for one more request. `flashOnly(['keyword', 'location'])` and `flashExcept('password')` store a subset, which keeps secrets out of the session. Uploaded files are never included because `input()` excludes them. On the next request `old('keyword')` in Blade, or `$request->old('keyword')`, reads the value with dot-path support and returns the default when nothing was flashed; the default can be an Eloquent model, in which case its attribute of the same name is used, which makes shared create and edit forms easy. A validation failure on a web route flashes input for you, minus password fields.
code
php · 15 lines<?php
use App\Models\SavedSearch;
use Illuminate\Http\Request;
public function store(Request $request)
{
if (SavedSearch::where('name', $request->input('name'))->exists()) {
$request->flashExcept('password'); // keep what was typed, minus secrets
return back()->with('status', 'That search name is taken.');
}
// ...
}go deeper
Know the pair: the server flashes input before redirecting back, and Blade's old('field') puts the value back into the form.
Explain the _old_input flash key, its one-request lifetime, flashOnly and flashExcept for secrets, dot paths and the model default in old().
Recognise when the framework flashes for you, keep passwords and tokens out of the session, and design forms that survive failures raised after validation.
Weigh server-rendered redirect-and-flash flows against client-side forms that keep their own state, including what each costs in session writes.
## The problem old input solves A classic web form posts to the server, the server decides something is wrong, and it redirects the browser back to the form. The redirect is a fresh `GET` request, so the values the user typed are gone unless the application kept them. Laravel keeps them as **old input**: a copy of the previous request's fields stored in the session as **flash data**, which is available for exactly one subsequent request and then discarded. On a job-board, imagine the "save this search" form: a user fills keyword, location and a salary range, the server rejects the save because the name is taken, and the form must reappear filled in. ## Writing old input The methods live on `Illuminate\Http\Request` (trait `InteractsWithFlashData`): | Method | What it stores under `_old_input` | |---|---| | `flash()` | `$request->input()`, every field except uploaded files | | `flashOnly($keys)` | `$request->only($keys)` | | `flashExcept($keys)` | `$request->except($keys)` | | `flush()` | an empty array, clearing old input | Each one calls the session store's `flashInput()`, which in turn calls `flash('_old_input', ...)`. Points worth stating in an interview: - **Files are never flashed.** `input()` excludes uploads, so a file input always comes back empty and the user must choose the file again. - **Secrets should be excluded.** `flashExcept('password')` stops a password from being written to the session store. - **A session is required.** These methods call `$request->session()`, which throws `RuntimeException('Session store not set on request.')` on a route without the session middleware, such as the stateless `api` group. - **Redirect helpers do the same job.** Chaining `->withInput()` on a redirect flashes input as part of building the redirect response; that API belongs to redirects, but it writes the same `_old_input` key. ## Reading old input On the next request, read the values back: 1. **In Blade**, the global `old()` helper is the idiom: `value="{{ old('keyword') }}"`. It delegates to `app('request')->old()`. 2. **In PHP**, `$request->old('keyword', 'php')` does the same. 3. **Nested fields** use dot paths, because the store reads with `Arr::get`: `old('filters.salary_min')` for an input named `filters[salary_min]`. 4. **Defaults** are returned when nothing was flashed. If the default is an Eloquent model, `old()` substitutes `$model->getAttribute($key)`, so `old('title', $savedSearch)` shows the user's retry on a failed edit and the stored value on first load. 5. **Without a session**, `old()` quietly returns the default instead of throwing. For checkboxes and selects, the Blade directives `@checked(old('remote'))` and `@selected(old('type') === 'contract')` keep the markup readable. ## When you do not call flash() yourself Most apps rarely call `flash()` directly. When validation fails on a request that does not expect JSON, the framework's exception handler redirects back with the input flashed, minus the keys in its `dontFlash` list (`current_password`, `password`, `password_confirmation` by default). The explicit methods are for failures you detect yourself: a duplicate name, an external API refusing the request, or a business rule checked after validation. ## Lifetime and pitfalls - **One request only.** Old input is flash data, so it disappears after the next request; a second redirect loses it unless the flash data is kept alive by the session API. - **Old input is a snapshot.** Values come back exactly as they stood after the input middleware ran, so a blank field returns `null`. - **Do not confuse with `with()` flash messages.** Status messages use ordinary session flash keys; old input uses the dedicated `_old_input` key that `old()` reads. ## A worked flow on the job board Put the pieces in order for the "save this search" form: 1. The browser posts `name`, `keyword`, `location` and `filters[salary_min]` to `/searches`. 2. The controller finds the name taken, calls `$request->flashExcept('password')`, and returns `back()`. 3. The session now holds `_old_input` as flash data and schedules it for removal after the next request. 4. The browser follows the redirect with `GET /searches/create`; the Blade view calls `old('keyword')`, `old('filters.salary_min')` and so on, and the form reappears filled. 5. If the user now refreshes the page, the flash data has aged out and the fields fall back to their defaults. Step 5 surprises people in manual testing, but it is the intended one-request lifetime rather than a bug.
- Why does a file input come back empty after a failed form submission even though flash() was called?`flash()` stores `$request->input()`, and `input()` excludes uploaded files; only `all()` merges them in. Files could not be stored meaningfully in the session anyway, since the temporary upload is deleted when the request ends. The user must pick the file again, or the app stores it first and flashes a reference.
- What happens if a controller on an api-group route calls $request->flash()?It throws `RuntimeException` with the message 'Session store not set on request.', because the `api` group has no session middleware and `session()` refuses to run without a store. `old()` behaves differently: it checks `hasSession()` and returns the default instead of throwing.
saying these in an interview costs you the question
- Flashed input stays in the session until the user logs out
- flash() also stores uploaded files for the next request
- old() only works if the controller called flash() itself
- old() throws when no input was flashed
- Nested fields need old('filters[salary_min]') bracket syntax