Belajar Laravel #2: Routing & Controller Pertama — Dari URL ke View

Hari ini saya lanjut belajar Laravel ke episode kedua: routing dan controller pertama. Setelah minggu lalu berhasil membuat project dan menjalankan php artisan serve, sekarang saya penasaran — dari mana sebenarnya halaman awal itu muncul? Ternyata jawabannya ada di satu file kecil bernama routes/web.php. File inilah yang menjadi pintu masuk hampir semua request ke aplikasi web kita.

routes/web.php: pintu masuk aplikasi

Buka folder project lalu buka routes/web.php. Isinya singkat, hanya satu route default:

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Bisa dibaca seperti ini: “kalau ada request GET ke alamat /, jalankan fungsi ini, lalu kembalikan view welcome“. Di sinilah saya sadar perbedaan mendasar Laravel dengan PHP biasa: alih-alih membuat file index.php per halaman, semua alamat dipetakan lewat satu file route. Ini disebut front controller — semua request masuk lewat public/index.php, lalu Laravel yang memutuskan ke mana request itu diteruskan berdasarkan route yang cocok.

Route closure vs controller

Di contoh di atas, logika ditulis langsung di dalam route sebagai closure (fungsi anonim). Untuk halaman sekecil welcome, cara ini cukup. Tapi kalau semua logika ditumpuk di routes/web.php, file itu cepat sekali jadi penuh sesak. Solusi Laravel: pindahkan logika ke controller.

Buat controller lewat Artisan:

php artisan make:controller HalamanController

Perintah itu membuat file app/Http/Controllers/HalamanController.php. Isinya kelas kosong. Lalu saya tambahkan method sendiri:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HalamanController extends Controller
{
    public function index()
    {
        return view('welcome');
    }
}

Dan route-nya berubah menjadi:

Route::get('/', [HalamanController::class, 'index']);

Pola [NamaController::class, 'namaMethod'] adalah cara Laravel modern menunjuk controller di route. Hasilnya sama dengan versi closure, tapi logika sudah pindah ke tempat yang lebih rapi dan bisa diuji. Prinsipnya: route itu peta, controller itu pekerja.

View pertama

Method index() tadi mengembalikan view('welcome'). View adalah file template yang disimpan di resources/views dengan ekstensi .blade.php — jadi view('welcome') mencari resources/views/welcome.blade.php. Saya coba buat halaman sendiri, misalnya halaman tentang:

Route::get('/tentang', [HalamanController::class, 'tentang']);

Lalu di controller:

public function tentang()
{
    return view('tentang');
}

Dan buat file resources/views/tentang.blade.php berisi HTML sederhana. Setelah itu php artisan serve dan buka http://127.0.0.1:8000/tentang — halaman muncul. Kesannya ajaib padahal sederhana: route → controller → view. Tiga lapis yang akan terus kita pakai di semua episode berikutnya.

Kesalahan yang umum

  • Lupa menjalankan php artisan serve, lalu bingung kenapa halaman 404. Route tidak akan jalan tanpa server.
  • Nama view salah ketik: view('welcome') tapi file bernama welcome.blade.php — pastikan nama persis.
  • Menaruh route di bawah route lain yang polanya sama; di Laravel, route yang cocok pertama yang dipakai. Urutan route bisa penting.
  • Menulis logika bisnis menumpuk di route closure sampai file web.php ratusan baris — kebiasaan yang sebaiknya dihindari sejak awal dengan memakai controller.

Route Parameter: Menangkap Bagian Dinamis dari URL

Hari ini saya melanjutkan eksperimen dengan routing dan menemukan fitur yang langsung terasa penting: parameter route. Sering kali sebuah URL membutuhkan bagian dinamis, misalnya /mahasiswa/1, /mahasiswa/2, dan seterusnya untuk halaman detail. Tidak mungkin saya menulis satu route untuk setiap ID yang mungkin ada. Di Laravel, bagian dinamis itu cukup ditulis dengan kurung kurawal:

Route::get('/mahasiswa/{id}', function ($id) {
    return 'Menampilkan detail mahasiswa dengan ID: ' . $id;
});

Saat ada request ke /mahasiswa/7, Laravel menangkap potongan 7 dari URL lalu memasukkannya ke variabel $id di closure. Urutan parameternya mengikuti urutan segmen di URL, jadi kalau ada dua parameter seperti {tahun}/{bulan}, keduanya diterima sebagai dua argumen berurutan.

Parameter juga bisa dibuat opsional dengan tanda tanya, lengkap dengan nilai default:

Route::get('/sapa/{nama?}', function ($nama = 'Tamu') {
    return 'Halo, ' . $nama . '!';
});

Dengan {nama?}, URL /sapa tanpa segmen tambahan pun tetap valid dan menghasilkan “Halo, Tamu!”. Yang menarik bagi saya: satu definisi route kini melayani banyak variasi alamat sekaligus — halaman profil tanpa slug maupun dengan slug cukup ditangani satu baris.

Kebiasaan yang saya ambil dari sini: setiap kali tergoda menulis /mahasiswa/1, /mahasiswa/2, dan seterusnya secara manual, itu tanda URL-nya seharusnya punya parameter. Pola ini juga membuat tautan dari halaman daftar ke halaman detail menjadi alami, karena ID-nya tinggal dicetak di dalam loop di view.

Satu hal lagi yang membuat saya merasa lebih aman: constraint. Tanpa pengaman, /mahasiswa/abc tetap cocok dengan pola dan $id berisi teks “abc”. Laravel menyediakan cara ringkas untuk membatasinya:

Route::get('/mahasiswa/{id}', function ($id) {
    return 'Detail mahasiswa: ' . $id;
})->whereNumber('id');

whereNumber('id') memastikan parameter itu hanya boleh angka; permintaan /mahasiswa/abc langsung berakhir 404 tanpa perlu if manual di dalam closure. Ada juga whereAlpha() untuk huruf serta where('id', '[0-9]+') untuk pola kustom. Validasi dasar di level route — ini berarti bagi saya sebagai lapisan pertama sebelum logika controller.

Nama Route dan Redirect

Percobaan berikutnya: memberi nama pada route. Alih-alih menulis alamat secara harfiah di banyak tempat, saya menandai route dengan nama lalu memanggilnya lewat nama tersebut:

Route::get('/mahasiswa/{id}', function ($id) {
    return 'Detail mahasiswa: ' . $id;
})->name('mahasiswa.show');

Route::get('/contoh-lompat', function () {
    return redirect()->route('mahasiswa.show', ['id' => 7]);
});

redirect()->route('mahasiswa.show', ['id' => 7]) meminta Laravel mengarahkan pengunjung ke URL lengkap milik route bernama itu, lengkap dengan parameternya. Nama route juga berguna untuk membangun tautan di dalam view:

<a href="{{ route('mahasiswa.show', ['id' => $mhs['id']]) }}">
    Lihat detail {{ $mhs['nama'] }}
</a>

Manfaatnya baru terasa setelah beberapa hari: ketika saya mengubah alamat dari /mahasiswa/{id} menjadi /siswa/{id}, semua tautan dan redirect tetap berfungsi karena dibangun dari nama, bukan dari tulisan alamat yang tersebar di banyak file. Ini menyelamatkan saya dari pencarian-replace yang rawan ada yang terlewat.

Grup Route dan Prefix

Terakhir, soal kerapian. Semakin banyak route, file web.php mulai sulit dibaca. Laravel menyediakan grup untuk mengelompokkan route yang berbagi kesamaan — prefix URL, middleware, bahkan domain. Contoh nyata: semua halaman panel admin biasanya berawalan /admin dan wajib login:

Route::prefix('admin')->middleware('auth')->group(function () {
    Route::get('/dashboard', function () {
        return view('admin.dashboard');
    })->name('admin.dashboard');

    Route::get('/pengaturan', function () {
        return view('admin.pengaturan');
    })->name('admin.pengaturan');
});

Kedua route di atas menjadi /admin/dashboard dan /admin/pengaturan. Middleware auth berlaku untuk seluruh grup: siapa pun yang belum masuk otomatis dialihkan ke halaman login sebelum sempat menyentuh closure atau controller. Kalau suatu saat prefix berubah menjadi /panel, saya cukup mengedit satu baris, buka menyusuri setiap route.

Bagi saya, grup route ibarat daftar isi: sekali dibuka, struktur aplikasi terbaca dari pengelompokannya — mana area publik, mana area admin, mana API. Tiga hal yang saya pelajari hari ini (parameter, nama route, grup) semuanya masih bermain di file yang sama, routes/web.php, tetapi membuat file itu jauh lebih bermakna daripada sekadar daftar alamat.

Kesimpulan

Hari ini saya paham alur dasar aplikasi Laravel: request masuk ke routes/web.php, dicocokkan dengan pola URL, lalu diteruskan ke closure atau controller, dan akhirnya controller mengembalikan view Blade. Konsep ini kecil tapi fondasi — semua fitur berikutnya (form, database, login) menumpang di alur yang sama. Minggu depan saya lanjut ke Blade: layout, @extends, dan perulangan @foreach.

Sumber

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses