Pada chapter Route Dasar, kita sempat membuat TugasController secara sepintas untuk melihat cara route menunjuk ke method. Sekarang kita bahas proses tersebut secara penuh: membuat controller lewat Artisan, membaca isi berkas yang dihasilkan generator, lalu menghubungkannya ke route dengan dua bentuk sintaks. Kita juga berkenalan dengan varian invokable yang memakai method ajaib __invoke. Seluruh perintah dan output pada chapter ini dijalankan langsung di aplikasi uji Course, sehingga hasilnya dapat kita bandingkan baris per baris.
Mengapa repot pindah dari closure ke controller? Closure praktis untuk route dua baris, tetapi closure adalah fungsi anonim yang hidup menempel di routes/web.php — lambat laun berkas route berubah menjadi berisi logika yang tidak bisa diuji terpisah dan tidak bisa dipanggil ulang dari tempat lain. Kelas controller memberi nama, alamat berkas, dan rumah untuk logika penanganan request; seiring aplikasi membesar, keputusan ini menjaga routes/web.php tetap berupa daftar alamat yang ringkas, bukan gudang kode.
Membuat Kelas dengan make:controller #
Generator controller dijalankan lewat perintah Artisan make:controller yang diikuti nama kelas. Kita buat controller untuk mengelola daftar tulisan:
php artisan make:controller PostControllerOutput yang Dihasilkan:
INFO Controller [app/Http/Controllers/PostController.php] created successfully. Artisan membuat satu berkas baru di app/Http/Controllers/. Isi berkas hasil generator cukup pendek:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
//
}Mari kita bedah isi berkas tersebut. Baris namespace App\Http\Controllers; menempatkan kelas di dalam namespace bawaan untuk seluruh controller, sesuai pemetaan PSR-4 antara direktori dan namespace. Baris use Illuminate\Http\Request; mengimpor kelas request yang paling sering dibutuhkan method — generator menyertakannya lebih dulu supaya siap pakai. Kelas PostController mewarisi Controller dasar milik Laravel, dan blok komentar // menandai tempat kita menulis method.
Mengisi Method dan Mendaftarkan Route #
Kita isi dua method sederhana: index() untuk daftar post dan show() untuk satu post berdasarkan parameter URI:
<?php
namespace App\Http\Controllers;
class PostController extends Controller
{
public function index()
{
return 'Daftar semua post';
}
public function show(string $id)
{
return "Detail post nomor {$id}";
}
}Selanjutnya, route menunjuk ke method controller memakai bentuk array [NamaController::class, 'method'] — pola yang sudah kita pakai di chapter Route Dasar. Tambahkan dua route berikut ke routes/web.php:
<?php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/{id}', [PostController::class, 'show']);Kemudian periksa hasil pendaftarannya lewat route:list:
php artisan route:list --except-vendorOutput yang Dihasilkan:
GET|HEAD posts .................................................................................... PostController@index
GET|HEAD posts/{id} ................................................................................ PostController@show
Showing [2] routesKolom paling kanan kini menampilkan PostController@index — tanda bahwa handler bukan closure, melainkan method index di dalam PostController. Kita uji kedua endpoint dengan skrip uji-route.php dari Section Routing:
GET /posts → HTTP 200 | Daftar semua post
GET /posts/7 → HTTP 200 | Detail post nomor 7Parameter {id} dari URI otomatis mengalir menjadi argumen method show() — mekanisme yang sama dengan parameter closure route, tanpa konfigurasi tambahan.
Sintaks String versus Array #
Laravel juga menerima handler dalam bentuk string, tetapi ada jebakan penting. Sejak Laravel 11, route tidak lagi otomatis mengawali nama controller dengan namespace App\Http\Controllers, sehingga string pendek gagal ditemukan:
// GAGAL: Laravel mencari kelas global "PostController"
Route::get('/posts', 'PostController@index');
// BERHASIL: nama kelas lengkap beserta namespace
Route::get('/posts', 'App\Http\Controllers\PostController@index');Bentuk pendek di atas menghasilkan halaman error 500 dengan pesan Target class [PostController] does not exist. pada log aplikasi, karena PHP mencari kelas bernama PostController di namespace global. Bentuk kedua bekerja karena namespace ditulis penuh.
| Sintaks | Contoh | Penilaian |
|---|---|---|
| Array | [PostController::class, 'index'] | Standar modern; diikuti IDE, aman refactor |
| String FQCN | 'App\Http\Controllers\PostController@index' | Valid, tetapi verbose dan string mentah |
| String pendek | 'PostController@index' | Gagal di Laravel 11+ — jangan dipakai |
Varian Invokable #
Satu varian generator perlu kita kenal sekarang: php artisan make:controller DashboardController --invokable. Flag --invokable menghasilkan controller dengan satu method bernama __invoke() alih-alih kelas kosong. PHP memanggil method __invoke() secara otomatis saat objek dipanggil layaknya fungsi, sehingga route-nya pun ditulis tanpa nama method — cukup Route::get('/ringkasan', DashboardController::class). Kapan bentuk ini lebih tepat dibanding controller multi-method kita bahas tuntas di chapter Single Action Controller
.
Berbekal controller dua method ini, kita siap melangkah lebih jauh: tujuh method CRUD yang saling terkait, dengan satu baris Route::resource yang mendaftarkan semuanya sekaligus — topik chapter Resource Controller
berikutnya.