BAB 8: Requests
Mengakses data request: input pengguna, file upload, header, dan method HTTP di Laravel.
Di Bab 7 kita menulis method store() yang menerima Request $request sebagai parameter. Kita mengambil $request->judul dan $request->isi untuk disimpan ke database — tapi baru sebatas itu. Objek $request sebenarnya jauh lebih kaya dari sekadar dua properti itu. Ia membawa seluruh informasi dari request HTTP yang masuk: semua field form, query string, header, cookie, file yang diupload, hingga IP address pengirim.
Memahami objek Request secara menyeluruh membuat kode controller lebih tepat dan aman — kamu tahu persis cara mengambil data yang dibutuhkan, memvalidasi kehadirannya, dan menangani edge case seperti field kosong atau tipe data yang tidak sesuai.
Informasi Dasar Request
Sebelum menyentuh input, ada kalanya kita perlu tahu konteks request itu sendiri: dari URL mana ia datang, method apa yang digunakan, atau apakah path-nya cocok dengan pola tertentu.
<?php
// app/Http/Controllers/DiagnostikController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DiagnostikController extends Controller
{
public function info(Request $request)
{
// Path tanpa domain: "catatan/5/edit"
$path = $request->path();
// URL lengkap tanpa query string
$url = $request->url();
// URL lengkap dengan query string
$urlLengkap = $request->fullUrl();
// Method HTTP: GET, POST, PUT, DELETE, dll
$method = $request->method();
// Cek method secara kondisional
if ($request->isMethod('post')) {
// hanya untuk POST
}
// Cek path dengan wildcard
if ($request->is('catatan/*')) {
// cocok untuk /catatan/1, /catatan/2/edit, dst
}
// Cek nama route
if ($request->routeIs('catatan.*')) {
// cocok untuk semua route bernama catatan.index, catatan.show, dst
}
}
}
Metode-metode ini berguna di middleware atau saat kamu perlu membuat logika kondisional berdasarkan URL — misalnya menampilkan breadcrumb yang berbeda tergantung halaman yang sedang dibuka.
Mengambil Input
Cara paling umum mengambil data dari request adalah via method input(). Method ini mengambil nilai dari semua sumber sekaligus: form POST, query string, maupun JSON body.
<?php
// app/Http/Controllers/CatatanController.php
namespace App\Http\Controllers;
use App\Models\Catatan;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class CatatanController extends Controller
{
public function store(Request $request): RedirectResponse
{
// Mengambil input dengan nama field
$judul = $request->input('judul');
$isi = $request->input('isi');
// Dengan nilai default jika field tidak ada
$prioritas = $request->input('prioritas', 'normal');
// Cara ringkas via dynamic property (shorthand input())
$warna = $request->warna;
$catatan = Catatan::create([
'judul' => $judul,
'isi' => $isi,
'prioritas' => $prioritas,
'warna' => $warna,
]);
return redirect()->route('catatan.show', $catatan);
}
}
Query String vs Form Input
input() mengambil dari semua sumber. Jika kamu hanya ingin query string (parameter di URL setelah ?), gunakan query():
// URL: /catatan?urut=terbaru&halaman=2
$urutan = $request->query('urut', 'terbaru'); // "terbaru"
$halaman = $request->query('halaman', 1); // 2
$semuaQuery = $request->query(); // ['urut' => 'terbaru', 'halaman' => '2']
Input dengan Tipe Spesifik
PHP tidak memiliki tipe data form — semua nilai dari HTTP datang sebagai string. Laravel menyediakan method konversi yang bersih:
public function index(Request $request)
{
// Integer — aman untuk paginasi, ID, dsb
$perHalaman = $request->integer('per_halaman', 15);
// Boolean — "1", "true", "on", "yes" → true; lainnya → false
$tampilDraft = $request->boolean('tampil_draft');
// Stringable — bisa langsung di-chain dengan method string
$judul = $request->string('judul')->trim()->title();
// Array — cocok untuk checkbox multi-pilih
$tagDipilih = $request->array('tag');
// Carbon instance untuk field tanggal
$tenggat = $request->date('tenggat', 'd/m/Y');
}
Menggunakan tipe spesifik lebih ekspresif daripada casting manual, dan hasilnya lebih mudah diprediksi.
Mengambil Subset Input
Saat menyimpan atau mengupdate model, seringkali kita perlu mengambil beberapa field sekaligus. Gunakan only() untuk mengambil field tertentu, atau except() untuk mengambil semua kecuali beberapa field:
public function update(Request $request, Catatan $catatan): RedirectResponse
{
// Ambil hanya field yang diizinkan
$dataUpdate = $request->only(['judul', 'isi', 'prioritas']);
// Atau: ambil semua kecuali field yang sensitif
$dataUpdate = $request->except(['_token', '_method', 'user_id']);
$catatan->update($dataUpdate);
return redirect()->route('catatan.show', $catatan);
}
Jangan gunakan $request->all() langsung ke Model::create() atau $model->update(). Ini membuka celah mass assignment jika ada field yang tidak diinginkan ikut masuk. Gunakan only() untuk membatasi field yang boleh diproses.
Memeriksa Kehadiran Input
Sebelum memproses input, sering kali perlu dicek apakah field tersebut memang ada dan berisi nilai. Ada tiga skenario yang berbeda:
public function filter(Request $request)
{
// has() — field ada di request (meski nilainya kosong string "")
if ($request->has('kategori')) {
// field 'kategori' ada
}
// filled() — field ada DAN nilainya tidak kosong
if ($request->filled('kata_kunci')) {
// 'kata_kunci' ada dan bukan string kosong
}
// missing() — field tidak ada sama sekali
if ($request->missing('filter')) {
// 'filter' tidak dikirim
}
// Kombinasi: hanya proses jika beberapa field ada sekaligus
if ($request->has(['dari_tanggal', 'sampai_tanggal'])) {
// filter rentang tanggal
}
// hasAny() — cukup salah satu yang ada
if ($request->hasAny(['tag', 'kategori', 'penulis'])) {
// ada setidaknya satu filter aktif
}
}
Perbedaan has() dan filled() terlihat nyata ketika user mengirim form dengan field kosong — has() tetap true, tapi filled() akan false.
Header dan Informasi Klien
Header HTTP membawa metadata request: tipe konten yang diterima, token autentikasi, asal request, dan lainnya.
public function terima(Request $request)
{
// Ambil nilai header
$contentType = $request->header('Content-Type');
$userAgent = $request->header('User-Agent');
// Cek keberadaan header
if ($request->hasHeader('X-Permintaan-Khusus')) {
// header kustom ada
}
// Bearer token dari header Authorization
// Berguna untuk API authentication
$token = $request->bearerToken();
// IP address pengirim
$ipAddress = $request->ip();
}
Satu method yang sangat berguna saat membangun API adalah expectsJson(). Method ini memeriksa apakah klien mengirim header Accept: application/json — sinyal bahwa ia mengharapkan response JSON, bukan HTML:
public function ambilCatatan(Request $request, Catatan $catatan)
{
if ($request->expectsJson()) {
return response()->json($catatan);
}
return view('catatan.show', compact('catatan'));
}
Dengan satu controller method yang sama, kita bisa melayani browser biasa maupun klien API — tergantung apa yang diminta klien.
Old Input untuk Form yang Gagal
Ketika validasi form gagal dan user diredirect kembali ke form, nilai yang sudah diisi user seharusnya tetap tampil — bukan kosong lagi. Ini disebut old input.
Laravel menyediakan method flash() untuk menyimpan input ke session sementara, dan old() untuk mengambilnya kembali di view:
public function store(Request $request): RedirectResponse
{
// Validasi manual sederhana
if (empty($request->judul)) {
// Simpan semua input ke session sebelum redirect
$request->flash();
return redirect()->back()->withErrors(['judul' => 'Judul tidak boleh kosong.']);
}
Catatan::create($request->only(['judul', 'isi']));
return redirect()->route('catatan.index');
}
Di Blade template, ambil old input dengan helper old():
{{-- resources/views/catatan/create.blade.php --}}
<input
type="text"
name="judul"
value="{{ old('judul') }}"
placeholder="Judul catatan..."
>
<textarea name="isi">{{ old('isi') }}</textarea>
Cara yang lebih umum adalah menyertakan flash input langsung di redirect menggunakan withInput() — kita akan lebih sering melihat pola ini di Bab 12 saat membahas validasi secara menyeluruh:
return redirect()->back()->withInput();
// atau hanya field tertentu:
return redirect()->back()->withInput($request->except('password'));
old() di Blade dan $request->old() di controller mengambil dari session yang sama. Gunakan old() helper di Blade untuk kemudahan, dan $request->old() jika perlu mengakses di controller atau service class.
File Upload
Mengakses file yang diupload tidak berbeda jauh dari mengakses input biasa — bedanya hasil yang kita dapat adalah objek UploadedFile dengan method-method untuk memeriksa dan menyimpan file.
<?php
// app/Http/Controllers/LampiranController.php
namespace App\Http\Controllers;
use App\Models\Catatan;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class LampiranController extends Controller
{
public function store(Request $request, Catatan $catatan): RedirectResponse
{
// Cek apakah file benar-benar diupload
if (!$request->hasFile('lampiran')) {
return redirect()->back()->withErrors(['lampiran' => 'Pilih file terlebih dahulu.']);
}
$file = $request->file('lampiran');
// Verifikasi upload berhasil (tidak corrupt di tengah jalan)
if (!$file->isValid()) {
return redirect()->back()->withErrors(['lampiran' => 'Upload gagal, coba lagi.']);
}
// Informasi file
$namaAsli = $file->getClientOriginalName(); // nama file dari browser
$ekstensi = $file->extension(); // ekstensi berdasarkan MIME type
$ukuranKb = round($file->getSize() / 1024); // ukuran dalam KB
// Simpan ke storage/app/lampiran dengan nama auto-generated
$path = $file->store('lampiran');
// Atau simpan dengan nama tertentu
$path = $file->storeAs('lampiran', $catatan->id . '.' . $ekstensi);
// Simpan path ke database
$catatan->update(['path_lampiran' => $path]);
return redirect()->route('catatan.show', $catatan)
->with('sukses', "File {$namaAsli} berhasil diunggah.");
}
}
Untuk multiple file upload (field dengan nama files[]), iterasi hasilnya:
public function uploadBanyak(Request $request, Catatan $catatan): RedirectResponse
{
$pathList = [];
foreach ($request->file('gambar') as $gambar) {
if ($gambar->isValid()) {
$pathList[] = $gambar->store('gambar/catatan/' . $catatan->id);
}
}
// Simpan semua path sebagai JSON
$catatan->update(['galeri' => json_encode($pathList)]);
return redirect()->route('catatan.show', $catatan);
}
Latihan
Coba kerjakan latihan berikut menggunakan CatatanController yang sudah dibangun di bab-bab sebelumnya:
-
Filter dengan query string — Tambahkan logika di method
index()untuk memfilter catatan berdasarkan query string?prioritas=tinggidan?kata_kunci=lorem. Gunakanfilled()untuk memastikan hanya filter yang terisi yang diterapkan. -
Upload avatar — Buat form dan method controller untuk mengupload gambar cover catatan. Validasi bahwa file yang diupload adalah gambar (ekstensi jpg, png, webp) dan ukurannya tidak lebih dari 2MB. Simpan ke
storage/app/public/cover-catatan/. -
Response adaptif — Modifikasi method
show()diCatatanControlleragar mengembalikan JSON jika request membawa headerAccept: application/json, dan mengembalikan view HTML untuk browser biasa. Uji dengancurl -H "Accept: application/json".
Penutup Bab
Objek Request adalah jembatan antara dunia luar dan logika aplikasi. Dengan memahami cara mengakses input secara tepat — membedakan has() dan filled(), menggunakan konversi tipe, dan menangani file upload dengan benar — kamu bisa menulis controller yang lebih eksplisit dan tidak mudah kejutan.
Sejauh ini kita hanya membicarakan apa yang masuk ke aplikasi. Sisi lainnya adalah apa yang keluar — response yang dikirim kembali ke browser. Ada banyak bentuk response dalam Laravel: HTML dari view, JSON untuk API, redirect, file download, hingga response dengan header kustom. Semua itu yang akan kita bahas di bab berikutnya.