Chapter sebelumnya menunjukkan bahwa satu route adalah pasangan HTTP method dan URI yang menunjuk satu handler. Dalam praktiknya, aplikasi kita membutuhkan banyak route dengan beragam method: halaman dibaca dengan GET, form dikirim dengan POST, data diubah dengan PUT atau PATCH, dan dihapus dengan DELETE. Chapter ini membahas seluruh method tersebut satu per satu, cara menunjuk route ke controller, jenis response yang bisa dikembalikan handler, serta shortcut untuk view, redirect, dan route fallback. Semua contoh diuji langsung pada aplikasi Laravel 13.

Method HTTP dan Semantiknya #

Setiap HTTP method membawa makna yang disepakati secara luas, dan router Laravel menyediakan satu method pendaftaran untuk masing-masing. Tabel berikut merangkum semantik standarnya:

MethodMaknaIdempotenPemakaian umum
GETmembaca datayahalaman, daftar, hasil pencarian
POSTmembuat data barutidaksubmit form, penyimpanan
PUTmengganti seluruh resourceyaupdate menyeluruh
PATCHmengubah sebagian resourceyaupdate sebagian
DELETEmenghapus resourceyapenghapusan
OPTIONSmenanyakan method yang tersediayapreflight request CORS

Kolom idempoten berarti request yang sama boleh diulang berkali-kali tanpa mengubah hasil akhir — DELETE /tugas yang dijalankan dua kali tetap berakhir pada kondisi yang sama: tugasnya hilang. Semantik ini bukan sekadar gaya penulisan: HTTP client, cache, dan proxy memakainya untuk mengambil keputusan.

Kita bangun endpoint /tugas lengkap dengan lima method utama, plus satu route OPTIONS yang biasa dipakai browser untuk pra-penerbangan CORS. Buat terlebih dahulu controller-nya dengan php artisan make:controller TugasController, lalu isi app/Http/Controllers/TugasController.php:

php
<?php

namespace App\Http\Controllers;

class TugasController extends Controller
{
    // GET /tugas — daftar seluruh tugas
    public function index()
    {
        return response()->json([
            ['id' => 1, 'judul' => 'Belajar routing Laravel'],
            ['id' => 2, 'judul' => 'Kirim laporan mingguan'],
        ]);
    }

    // POST /tugas — simpan tugas baru
    public function simpan()
    {
        return response()->json(['status' => 'tersimpan'], 201);
    }

    // PUT /tugas — perbarui kumpulan tugas
    public function perbarui()
    {
        return response()->json(['status' => 'diperbarui']);
    }

    // PATCH /tugas — tandai selesai sebagian
    public function tandaiSelesai()
    {
        return response()->json(['status' => 'selesai']);
    }

    // DELETE /tugas — hapus tugas
    public function hapus()
    {
        return response()->noContent();
    }
}

Selanjutnya daftarkan route-nya di routes/web.php. Handler ditulis dalam bentuk array [NamaController::class, 'method'] — bentuk alternatif dari closure yang kita pakai di chapter sebelumnya:

php
<?php

use App\Http\Controllers\TugasController;
use Illuminate\Support\Facades\Route;

// Route paling sederhana: closure mengembalikan string
Route::get('/halo', function () {
    return 'Halo, Laravel!';
});

// Route yang menunjuk ke method controller
Route::get('/tugas', [TugasController::class, 'index']);
Route::post('/tugas', [TugasController::class, 'simpan']);
Route::put('/tugas', [TugasController::class, 'perbarui']);
Route::patch('/tugas', [TugasController::class, 'tandaiSelesai']);
Route::delete('/tugas', [TugasController::class, 'hapus']);
Route::options('/tugas', function () {
    return response()->noContent();
});

Perintah route:list memperlihatkan keenam route tersebut terdaftar rapi di URI yang sama namun method berbeda:

bash
php artisan route:list --except-vendor --path=tugas

Output yang Dihasilkan:

text
  ANY       daftar-tugas ................................. Illuminate\Routing › RedirectController
  GET|HEAD  tugas .......................................................... TugasController@index
  POST      tugas ......................................................... TugasController@simpan
  PUT       tugas ....................................................... TugasController@perbarui
  PATCH     tugas .................................................. TugasController@tandaiSelesai
  DELETE    tugas .......................................................... TugasController@hapus
  OPTIONS   tugas .............................................................. routes/web.php:17

                                                                                Showing [7] routes

Dua hal menarik dari output di atas. Filter --path=tugas menyaring URI yang mengandung teks tugas, sehingga route /daftar-tugas ikut tampil. Kolom method pada baris pertama menunjukkan ANY karena route redirect merespons semua method — kita bahas redirect beberapa paragraf ke bawah.

Shortcut view, redirect, dan fallback #

Laravel menyediakan tiga shortcut untuk kasus yang sangat sering muncul. Route::view() langsung mengembalikan sebuah view tanpa closure, Route::redirect() memindahkan request lama ke URI baru dengan status 302, dan Route::fallback() menjadi handler terakhir saat tidak ada route yang cocok. Tambahkan baris berikut ke routes/web.php:

php
Route::view('/selamat', 'selamat', ['nama' => 'Budi']);

Route::redirect('/daftar-tugas', '/tugas');
Route::permanentRedirect('/tasks', '/tugas');

Route::fallback(function () {
    return 'Maaf, halaman yang Anda cari tidak ditemukan.';
});

Shortcut Route::view('/selamat', 'selamat', ...) me-render berkas resources/views/selamat.blade.php dan meneruskan data nama, sehingga Blade <h1>Selamat datang, {{ $nama }}!</h1> di dalamnya menampilkan heading “Selamat datang, Budi!”. Route::permanentRedirect() berbeda dari Route::redirect() pada status code-nya: 301 memberi tahu browser dan mesin pencari bahwa perpindahan bersifat permanen, sedangkan 302 bersifat sementara. Kita jalankan skrip uji kecil dari chapter sebelumnya terhadap URI-URI tersebut:

php
<?php

// uji-route.php — jalankan dari root proyek Laravel
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;

require __DIR__.'/vendor/autoload.php';
$app = require __DIR__.'/bootstrap/app.php';

$kernel = $app->make(Kernel::class);

$permintaan = [
    ['GET', '/halo'],
    ['GET', '/tugas'],
    ['GET', '/daftar-tugas'],
    ['GET', '/tasks'],
    ['GET', '/tidak-ada'],
];

foreach ($permintaan as [$method, $uri]) {
    $response = $kernel->handle(
        Request::create('http://localhost:8000'.$uri, $method)
    );
    $isi = $response->headers->get('Location') ?? trim($response->getContent());
    echo str_pad("{$method} {$uri}", 18)
        ." → HTTP ".$response->getStatusCode()
        .' | '.mb_substr($isi, 0, 90).PHP_EOL;
}

Output yang Dihasilkan:

text
GET /halo          → HTTP 200 | Halo, Laravel!
GET /tugas         → HTTP 200 | [{"id":1,"judul":"Belajar routing Laravel"},{"id":2,"judul":"Kirim laporan mingguan"}]
GET /daftar-tugas  → HTTP 302 | /tugas
GET /tasks         → HTTP 301 | /tugas
GET /tidak-ada     → HTTP 200 | Maaf, halaman yang Anda cari tidak ditemukan.

Output tersebut memperlihatkan tiga jenis response sekaligus: teks biasa dari closure /halo, JSON dari response()->json() di TugasController@index, dan redirect 302/301 beserta URI tujuannya di header Location. Baris terakhir menunjukkan fallback bekerja untuk URI tak dikenal.

Menguji Route dengan Feature Test #

Method tulis seperti POST dan DELETE tidak bisa diuji dengan skrip runner begitu saja, karena route di routes/web.php dilindungi middleware ValidateCsrfToken — request tulis tanpa token CSRF akan dijawab 419 Page Expired (perilaku ini sudah kita verifikasi langsung). Cara paling praktis menguji route tulis adalah feature test, karena environment testing membuat middleware CSRF membiarkan request lewat. Buat tests/Feature/TugasRouteTest.php:

php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class TugasRouteTest extends TestCase
{
    public function test_daftar_tugas_mengembalikan_json(): void
    {
        $response = $this->get('/tugas');

        $response->assertStatus(200);
        $response->assertJson([
            ['id' => 1, 'judul' => 'Belajar routing Laravel'],
        ]);
    }

    public function test_simpan_tugas_mengembalikan_201(): void
    {
        $response = $this->post('/tugas');

        $response->assertStatus(201);
        $response->assertJson(['status' => 'tersimpan']);
    }

    public function test_tandai_selesai_mengembalikan_200(): void
    {
        $response = $this->patch('/tugas');

        $response->assertStatus(200);
    }

    public function test_hapus_tugas_mengembalikan_204(): void
    {
        $response = $this->delete('/tugas');

        $response->assertNoContent();
    }
}

Lalu jalankan berkas test tersebut:

bash
php artisan test tests/Feature/TugasRouteTest.php

Output yang Dihasilkan:

text
  PASS  Tests\Feature\TugasRouteTest
  ✓  daftar tugas mengembalikan json
  ✓  simpan tugas mengembalikan 201
  ✓  tandai selesai mengembalikan 200
  ✓  hapus tugas mengembalikan 204

  Tests:    4 passed (9 assertions)

Keempat test hijau membuktikan seluruh route tulis berfungsi: POST menghasilkan 201, PATCH menghasilkan 200, dan DELETE menghasilkan 204 tanpa konten. Kita akan membedah fitur pengujian Laravel jauh lebih dalam di Section Testing Laravel ; untuk sekarang, pola $this->get()/$this->post() plus assertStatus() sudah cukup untuk memverifikasi route.

Satu hal masih mengganjal: route /tugas kita selalu mengembalikan daftar yang sama, sementara aplikasi nyata perlu menampilkan tugas tertentu — tugas nomor 5, milik pengguna tertentu, pada kategori tertentu. Kebutuhan itulah yang diselesaikan oleh route parameters , topik chapter berikutnya.