Aplikasi sungguhan jarang berisi lima route. Dashboard admin saja bisa memuat puluhan route yang semuanya berawalan URI /admin, semuanya dijaga middleware yang sama, dan semuanya dinamai dengan awalan admin.. Menulis ulang atribut yang identik pada setiap route membosankan dan rawan lupa — satu route yang luput dari middleware menjadi celah keamanan. Route group mengatasi masalah ini: kita mendeklarasikan atribut bersama satu kali, lalu seluruh route di dalam grup mewarisinya secara otomatis. Chapter ini membahas group untuk prefix, name, middleware, dan controller, termasuk group yang bersarang.

Group dengan Prefix dan Name Prefix #

Bayangkan area admin dengan dua route: /admin/dasbor bernama admin.dasbor dan /admin/laporan/bulanan bernama admin.laporan.bulanan. Tanpa group, URI dan awalan nama ditulis manual di setiap baris. Dengan group, keduanya menjadi milik bersama:

php
<?php

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

// Group: middleware + prefix + name prefix sekaligus
Route::middleware('admin')
    ->prefix('admin')
    ->name('admin.')
    ->group(function () {
        Route::get('/dasbor', fn () => 'Ini dasbor admin.')->name('dasbor');

        // Nested group: prefix dan name bertumpuk
        Route::prefix('laporan')->name('laporan.')->group(function () {
            Route::get('/bulanan', fn () => 'Laporan bulanan admin.')->name('bulanan');
        });
    });

Tiga method perantai membawa tiga aturan bersama: ->prefix('admin') menempelkan awalan URI, ->name('admin.') menempelkan awalan nama, dan ->middleware('admin') memasang middleware pada seluruh anggota. Route di dalamnya cukup menulis sisa URI (/dasbor) dan sisa nama (dasbor), lalu Laravel menggabungkannya menjadi admin/dasbor dan admin.dasbor. Group laporan di dalamnya menunjukkan group bersarang: prefix dan name bertumpuk lapis — admin. + laporan. + bulanan menghasilkan nama admin.laporan.bulanan untuk URI /admin/laporan/bulanan. Menjalankan route('admin.dasbor') dan route('admin.laporan.bulanan') di aplikasi uji menghasilkan dua URL berikut:

text
http://localhost:8000/admin/dasbor
http://localhost:8000/admin/laporan/bulanan

Middleware Bersama untuk Satu Kelompok #

Alias admin yang dipakai group di atas tentu bukan bawaan Laravel — kita buat sendiri sebagai contoh nyata middleware bersama. Buat middleware-nya lewat Artisan, lalu isi berkas app/Http/Middleware/PastikanAdmin.php yang dihasilkan:

bash
php artisan make:middleware PastikanAdmin
php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class PastikanAdmin
{
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->header('X-Admin-Token') !== 'TOKEN-CONTOH') {
            abort(403, 'Akses khusus admin.');
        }

        return $next($request);
    }
}

Middleware ini memeriksa header X-Admin-Token dan membatalkan request dengan status 403 bila token tidak cocok. Agar bisa dipanggil dengan nama pendek admin, daftarkan alias-nya di bootstrap/app.php:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'admin' => App\Http\Middleware\PastikanAdmin::class,
    ]);
})

Efeknya langsung terlihat saat menguji request dengan dan tanpa token:

php
<?php

// uji-admin.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', '/admin/dasbor', []],
    ['GET', '/admin/dasbor', ['HTTP_X_ADMIN_TOKEN' => 'TOKEN-CONTOH']],
    ['GET', '/admin/ringkasan', ['HTTP_X_ADMIN_TOKEN' => 'TOKEN-CONTOH']],
    ['GET', '/admin/laporan/bulanan', ['HTTP_X_ADMIN_TOKEN' => 'TOKEN-CONTOH']],
];

foreach ($permintaan as [$method, $uri, $server]) {
    $response = $kernel->handle(
        Request::create('http://localhost:8000'.$uri, $method, [], [], [], $server)
    );
    echo str_pad("{$method} {$uri}", 30)
        ." → HTTP ".$response->getStatusCode().PHP_EOL;
}

Output yang Dihasilkan:

text
GET /admin/dasbor              → HTTP 403
GET /admin/dasbor              → HTTP 200
GET /admin/ringkasan           → HTTP 200
GET /admin/laporan/bulanan     → HTTP 200

Baris pertama membuktikan middleware benar-benar menjaga group: request tanpa token ditolak 403 sebelum menyentuh handler. Tiga baris berikutnya menunjukkan seluruh route anggota group — termasuk route /admin/ringkasan yang akan kita definisikan sebentar lagi — lolos dengan token yang sama. Satu deklarasi ->middleware('admin') menjaga semuanya.

Satu Controller untuk Satu Group #

Ketika satu controller menampung banyak route, menulis [TugasController::class, 'index'] berulang-ulang juga berulang. Method ->controller() memungkinkan kita menyebut controller sekali, lalu tiap route cukup menuliskan nama method dalam bentuk string. Tambahkan dua group berikut ke routes/web.php:

php
// Group dengan satu controller: cukup tulis nama method
Route::controller(TugasController::class)->group(function () {
    Route::get('/tugas', 'index');
    Route::post('/tugas', 'simpan');
});

// Group controller area admin
Route::controller(DasborController::class)
    ->prefix('admin')
    ->name('admin.')
    ->middleware('admin')
    ->group(function () {
        Route::get('/ringkasan', 'ringkasan')->name('ringkasan');
        Route::get('/pengguna', 'pengguna')->name('pengguna');
    });

Group kedua menggabungkan seluruh materi chapter ini sekaligus: prefix URI, awalan nama, middleware, dan controller utuh dalam satu rantai. Controller-nya sendiri dibuat dengan php artisan make:controller Admin/DasborController dan isinya sederhana:

php
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;

class DasborController extends Controller
{
    public function ringkasan()
    {
        return 'Ringkasan area admin.';
    }

    public function pengguna()
    {
        return 'Daftar pengguna (khusus admin).';
    }
}

Membaca Hasil Pengelompokan di route:list #

Keseluruhan struktur group tadi terbaca jelas dari route:list — flag -v menambahkan kolom middleware:

bash
php artisan route:list --except-vendor

Output yang Dihasilkan:

text
  GET|HEAD   admin/dasbor ....................................... admin.dasbor › routes/web.php:12
  GET|HEAD   admin/laporan/bulanan ..................... admin.laporan.bulanan › routes/web.php:16
  GET|HEAD   admin/pengguna ................. admin.pengguna › Admin\DasborController@pengguna
  GET|HEAD   admin/ringkasan .................. admin.ringkasan › Admin\DasborController@ringkasan
  GET|HEAD   tugas ......................................................... TugasController@index
  POST       tugas ........................................................ TugasController@simpan

                                                                                Showing [6] routes

Output tersebut memperlihatkan hasil penggabungan prefix URI dan name prefix: admin/laporan/bulanan berjalan sebagai admin.laporan.bulanan, tepat seperti bukti helper route() di awal chapter. Dengan flag -v, middleware tiap route ikut terlihat:

bash
php artisan route:list -v --except-vendor --path=admin

Output yang Dihasilkan:

text
  GET|HEAD       admin/dasbor ................................... admin.dasbor › routes/web.php:12
                 ⇂ web
                 ⇂ App\Http\Middleware\PastikanAdmin
  GET|HEAD       admin/laporan/bulanan ................. admin.laporan.bulanan › routes/web.php:16
                 ⇂ web
                 ⇂ App\Http\Middleware\PastikanAdmin
  GET|HEAD       admin/pengguna ................. admin.pengguna › Admin\DasborController@pengguna
                 ⇂ web
                 ⇂ App\Http\Middleware\PastikanAdmin
  GET|HEAD       admin/ringkasan .............. admin.ringkasan › Admin\DasborController@ringkasan
                 ⇂ web
                 ⇂ App\Http\Middleware\PastikanAdmin

                                                                                Showing [4] routes

Setiap route admin menjalankan dua lapis middleware: grup web bawaan untuk route di routes/web.php, lalu PastikanAdmin yang diwarisi dari group. Inilah janji route group yang benar-benar tuntas — tidak ada satu pun route yang tertinggal.

Group menyelesaikan masalah struktur, tetapi satu kelemahan parameternya masih kita bawa sejak chapter Route Parameters : {id} menerima nilai apa pun, termasuk abc. Route /tugas/abc hari ini tetap cocok dan menjalankan handler seolah-olah ID valid. Chapter berikutnya, Route Constraints , membatasi nilai parameter dengan regular expression dan helper bawaan seperti whereNumber().