Sekarang kita tahu di mana data session tinggal dan bagaimana cookie menunjuknya. Pertanyaan berikutnya adalah soal harian: bagaimana kode controller membaca dan menulis data tersebut? Laravel menyediakan dua wajah untuk API yang sama — helper global session() yang ringkas, dan $request->session() yang berorientasi objek. Pada chapter ini kita genapi keduanya: menulis, membaca, menambah elemen array, memeriksa keberadaan kunci, sampai memutar session ID di tengah jalan.

Helper session() yang Ringkas #

Helper global session() menerima dua bentuk pemakaian. Dipanggil dengan satu argumen string, helper tersebut membaca nilai; dipanggil dengan array, helper tersebut menulis nilai:

php
session(['tema' => 'gelap']);          // tulis
$tema = session('tema');              // baca: 'gelap'
$temaLain = session('tema_lain');     // baca kunci tak ada: null
$temaLain = session('tema_lain', 'terang'); // baca dengan nilai default

Bentuk helper ini populer di dalam Blade — misalnya session('status') untuk memeriksa pesan — karena singkat dan tidak butuh variabel $request. Untuk kode controller, bentuk OOP memberi akses ke operasi yang lebih kaya.

Dua bentuk tersebut mengoperasikan object yang sama persis, jadi tidak ada perbedaan data di antara keduanya. Pilihan kita murni soal gaya: helper untuk akses cepat satu baris, $request->session() untuk operasi berantai. Satu hal yang perlu diperhatikan di sisi penulisan: kode di dalam route baru benar-benar tersimpan ke storage ketika request selesai diproses — middleware session menulis seluruh payload sekali saja di akhir, bukan pada setiap pemanggilan put().

API $request->session() #

Object Request yang di-inject ke controller membawa session store di dalamnya, dan store tersebut paham notasi titik untuk array bertingkat. Kita uji seluruh API dasar dalam satu route yang mencetak laporan:

php
Route::get('/sesi/uji-api', function (Request $request) {
    $report = [];

    session(['tema' => 'gelap']);
    $report[] = 'session_tema=' . session('tema');

    $request->session()->put('keranjang.buah', ['apel', 'mangga']);
    $request->session()->push('keranjang.buah', 'jeruk');
    $report[] = 'keranjang_buah=' . json_encode($request->session()->get('keranjang.buah'));

    $report[] = 'has(tema)=' . var_export($request->session()->has('tema'), true);
    $report[] = 'exists(nama)=' . var_export($request->session()->exists('nama'), true);
    $report[] = 'missing(nama)=' . var_export($request->session()->missing('nama'), true);

    $report[] = 'pull(tema)=' . $request->session()->pull('tema');
    $report[] = 'tema_setelah_pull=' . var_export($request->session()->has('tema'), true);

    $semua = $request->session()->all();
    $report[] = 'all_keys=' . implode(',', array_keys($semua));

    $idLama = $request->session()->getId();
    $request->session()->regenerate();
    $report[] = 'regenerate: id_berubah=' . var_export($idLama !== $request->session()->getId(), true)
        . ' keranjang_masih_ada=' . var_export($request->session()->has('keranjang'), true);

    $request->session()->forget('keranjang');
    $report[] = 'forget: keranjang=' . var_export($request->session()->get('keranjang'), true);

    return response(implode("\n", $report) . "\n", 200, ['Content-Type' => 'text/plain']);
});

Feature test-nya cukup mengunjungi route tersebut dan mencetak hasilnya:

php
public function test_api_session_menghasilkan_laporan_lengkap(): void
{
    $response = $this->get('/sesi/uji-api');

    echo "\n" . $response->getContent();
}

Output yang Dihasilkan:

text
session_tema=gelap
keranjang_buah=["apel","mangga","jeruk"]
has(tema)=true
exists(nama)=false
missing(nama)=true
pull(tema)=gelap
tema_setelah_pull=false
all_keys=_token,keranjang
regenerate: id_berubah=true keranjang_masih_ada=true
forget: keranjang=NULL

Mari kita bedah baris-baris penting laporan tersebut:

  1. put('keranjang.buah', [...]) — notasi titik membuat array bertingkat, sehingga payload menjadi ["keranjang" => ["buah" => [...]]]. Method push() kemudian menambah satu elemen ke array tersebut, terbukti dari munculnya "jeruk".
  2. has() vs exists() — has('tema') mengembalikan true karena kunci ada dan nilainya bukan null. Method exists() hanya memeriksa keberadaan kunci; keduanya berbeda saat sebuah kunci memang ada tetapi nilainya null. Method missing() adalah kebalikan exists() — cocok untuk menulis kondisi negatif yang mudah dibaca.
  3. pull('tema') — membaca sekaligus menghapus dalam satu langkah. Laporan membuktikannya: nilai 'gelap' kembali, lalu tema_setelah_pull=false.
  4. all() — mengembalikan seluruh isi session saat itu. Karena diakses di tengah route (sebelum middleware menambah kunci internal lain), yang tampak hanya _token dan keranjang.
  5. regenerate() — mengganti session ID dengan yang baru tanpa membuang data, terbukti dari id_berubah=true disertai keranjang_masih_ada=true. Rotasi ini penting untuk keamanan, topik yang kita dalami di chapter Session Security .

Data yang Bertahan Lintas Request #

Pembuktian terpenting adalah data yang bertahan antar request. Route keranjang berikut mengumpulkan item lewat push(), dan setiap request menambah satu item:

php
Route::post('/keranjang/{item}', function (Request $request, string $item) {
    $request->session()->push('keranjang', $item);

    return 'ditambahkan: ' . $item;
});

Route::get('/keranjang/lihat', function (Request $request) {
    return 'keranjang=' . json_encode($request->session()->get('keranjang', []));
});

Feature test-nya memainkan tiga request berturut-turut:

php
$this->post('/keranjang/buku-laravel')->assertSee('ditambahkan: buku-laravel');
$this->post('/keranjang/mouse')->assertSee('ditambahkan: mouse');

$this->get('/keranjang/lihat')->assertSee('["buku-laravel","mouse"]', false);

Output yang Dihasilkan:

text
keranjang=["buku-laravel","mouse"]

Dua request POST menambah dua item ke kunci keranjang yang sama, dan request GET ketiga masih menemukan keduanya — persis perilaku keranjang belanja yang kita harapkan. Nilai default pada get('keranjang', []) juga layak dicatat: array kosong dikembalikan saat pengunjung baru pertama kali datang, sehingga json_encode tidak pernah menerima null.

Session ID dan Kapan Menggantinya #

Session ID — 40 karakter alfanumerik yang kita lihat di tabel pada chapter Konsep Session — bisa kita akses kapan pun lewat $request->session()->getId(). ID tersebut bukan data biasa: Laravel menolak cookie session yang formatnya tidak sah, dan mengganti ID tidak serta-merta mengosongkan data, seperti yang terbukti dari laporan regenerate di atas.

Rotasi ID adalah urusan keamanan, bukan manajemen data. Kapan pun hak akses berubah — login, eskalasi role — ID lama yang mungkin bocor harus dibuang. Karena topik ini berhubungan erat dengan serangan dan pertahanan, pembahasan penuhnya sengaja kita simpan untuk chapter Session Security ; di sini cukup satu kalimat praktis: regenerate() mempertahankan data, invalidate() memusnahkannya.

Dengan API baca-tulis ini di tangan, pola paling populer dalam aplikasi nyata tinggal satu langkah lagi: pesan yang tampil tepat sekali setelah redirect. Laravel menyebutnya flash data, dan itulah bintang utama chapter Flash Messages . Referensi API selengkapnya tersedia di dokumentasi Session .