skip to content

In Laravel, how do you store, read and remove session data with get(), put(), push(), pull() and forget()?

level: juniorimportance: must knowfreq 66%

answer

  1. three entry points to one store
  2. dot keys nest arrays
  3. default may be a closure
  4. pull() reads then deletes
  5. has() is false for null values

basics

~20 s

Through $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 s

Laravel'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
<?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 removed

go deeper

for a junior

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.

for a middle

Explain dot-key nesting, lazy closure defaults, has() versus exists() for null values, and why changes persist only when the response is sent.

for a senior

Keep session payloads small and scoped, clean up wizard state, and recognise when state belongs in a database draft rather than the session.

for a principal

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