In Laravel, how do you store, read and remove session data with get(), put(), push(), pull() and forget()?
answer
- three entry points to one store
- dot keys nest arrays
- default may be a closure
- pull() reads then deletes
- has() is false for null values
basics
~20 sThrough $request->session(), the session() helper or the Session facade: put() stores a value, get() reads it with an optional default, push() appends to an array value, pull() reads and removes in one call, and forget() deletes keys; flush() empties the session.
solid answer
~40 sLaravel's session store is reachable as `$request->session()`, the global `session()` helper or the `Session` facade. `put('quote.vehicle', $data)` sets a value, and dot keys write nested arrays; `session(['step' => 2])` does the same through the helper. `get('quote.vehicle', $default)` reads it, and the default can be a closure that runs only when the key is absent. `push('quote.drivers', $driver)` appends to an array value, `pull('quote')` returns the value and removes it, `forget('quote')` or `forget([...])` deletes keys, and `flush()` clears everything. `has()` is false when a key holds `null`; `exists()` is true as long as the key is present. Changes are written back to the driver when the response is sent.
code
php · 23 lines<?php
use Illuminate\Http\Request;
public function storeDriver(Request $request)
{
$session = $request->session();
$session->put('quote.step', 3);
$session->push('quote.drivers', $request->only(['name', 'licence_years']));
$vehicle = $session->get('quote.vehicle', fn () => ['make' => null]); // closure runs only if absent
$count = count(session('quote.drivers', [])); // helper reads the same store
if ($request->boolean('start_over')) {
$session->forget('quote');
}
return redirect('/quote/coverage');
}
// later, on the payment step:
// $quoteId = $request->session()->pull('quote.priced_id'); // read once, then removedgo deeper
Know the verbs: put() to store, get() with a default to read, push() for arrays, pull() to read once, forget() to delete, and the three ways to reach the store.
Explain dot-key nesting, lazy closure defaults, has() versus exists() for null values, and why changes persist only when the response is sent.
Keep session payloads small and scoped, clean up wizard state, and recognise when state belongs in a database draft rather than the session.
Set conventions for what may live in the session, since every byte is loaded on each request and shapes how the app scales.
## The session store and how to reach it A **session** keeps data about one visitor between HTTP requests. Laravel identifies the visitor by a session cookie and keeps the data in the driver that `SESSION_DRIVER` selects. Inside a request, you work with an in-memory store object, an `Illuminate\Session\Store`, and the framework writes it back to the driver when the response goes out. There are three equivalent ways to reach that store: - `$request->session()`, the most explicit and easiest to test; - the global `session()` helper: `session('key')` reads, `session(['key' => 'value'])` writes, and `session()` with no argument returns the store; - the `Session` facade, `Session::get('key')`. The running example is a multi-step insurance quote wizard that collects vehicle, drivers and coverage over several pages before pricing a policy. ## Writing and reading | Method | What it does | |---|---| | `put($key, $value)` or `put([...])` | sets one or many values; dot keys build nested arrays | | `get($key, $default = null)` | reads a value; the default may be a closure evaluated lazily | | `push($key, $value)` | reads an array value (or `[]`), appends, writes it back | | `increment($key, $amount = 1)` / `decrement()` | numeric counters, returning the new value | | `remember($key, $callback)` | returns the value, or computes, stores and returns it when it is `null` | | `all()`, `only([...])`, `except([...])` | read the whole store or a subset | Dot notation is not cosmetic. `put('quote.vehicle.make', 'Volvo')` creates `['quote' => ['vehicle' => ['make' => 'Volvo']]]`, and `get('quote.vehicle')` returns the nested array. That makes it natural to keep the whole wizard under one `quote` key. ## Removing data - **`pull($key, $default = null)`** returns the value and removes it in one call. It suits one-shot values, such as a pending quote ID handed from the pricing step to the payment step. - **`forget($key)`** removes one key; `forget(['quote', 'step'])` removes several. Dot keys remove nested entries. - **`remove($key)`** also removes a key and returns its value. - **`flush()`** empties the whole store, including the CSRF token, so use it sparingly. Regenerating or invalidating the session identifier on login and logout is a separate concern owned by the authentication flow. ## Checking for keys The store distinguishes **present** from **non-null**: 1. `has('quote.vehicle')` is true only when the value exists **and is not `null`**; several keys require all to be non-null. 2. `exists('quote.vehicle')` is true when the key is present, even if its value is `null`. 3. `missing($key)` is the negation of `exists()`. 4. `hasAny([...])` is true when at least one key holds a non-null value. If the wizard stores `quote.discount_code => null` to mean "the user explicitly chose none", `has()` returns false and `exists()` returns true. ## When changes are persisted The store is loaded when the session middleware starts the request and written back once, after your controller has produced a response. Consequences worth knowing: - values you `put()` are visible immediately within the same request; - nothing reaches the driver if the request dies before the session is saved; - the whole payload is written at the end, so two concurrent requests on the same session can overwrite each other's changes, which is what route-level session blocking addresses. ## Keeping the wizard's session small Session data is loaded on every request that starts the session, so keep it lean: - store identifiers and small scalars (`quote.vehicle_id`), not whole models or large arrays; - remove the wizard state with `forget('quote')` once the quote is priced; - consider a draft row in the database for long or valuable wizards, with only its ID in the session. ## Enum keys and defaults Two smaller details come up in code review: - **Enum keys.** `get()`, `put()`, `pull()`, `forget()` and `flash()` accept enum cases as keys and use their value, so a `SessionKey::Quote` backed enum can replace scattered string literals. - **Lazy defaults.** `get('quote.vehicle', fn () => $this->emptyVehicle())` evaluates the closure only when the key is absent, which keeps expensive defaults off the common path. A stored `null` is not "absent" here, because the store checks key presence before using the default.
- The wizard stores quote.discount_code as null when the user picks 'no code'. Why does has('quote.discount_code') return false, and what should you use?The session store's `has()` treats a `null` value as absent: it checks `is_null($this->get($key))`. `exists('quote.discount_code')` compares against a placeholder instead, so it is true for a present `null`. Use `exists()` when an explicit null is meaningful, and `has()` when you need a usable value.
- What is the difference between pull() and get() followed by forget()?Functionally nothing: `pull($key, $default)` calls `Arr::pull`, which reads the value (or the default) and then removes the key from the in-memory store. It is simply one call instead of two, which makes read-once values such as a pending quote ID harder to leave behind by mistake.
saying these in an interview costs you the question
- Session changes are written to the store immediately on put()
- has() is true for a key whose value is null
- put('quote.vehicle', ...) stores a key with a literal dot
- session(['step' => 2]) reads the step key
- flush() only removes flash data