Controller adalah kelas yang mengelompokkan penanganan beberapa rute terkait dalam satu berkas, sehingga logika penanganan permintaan tidak lagi tersebar sebagai closure panjang pada routes/web.php. Topik ini membahas pembuatan controller dengan Artisan, pengaitan rute ke metode aksinya, pola resource controller untuk CRUD, dan dependency injection atau injeksi dependensi pada metode aksi, semuanya diterapkan pada kelas HalamanController di proyek contoh.
Controller dasar#
Controller baru dihasilkan perintah php artisan make:controller, dan keluarannya berupa kelas kosong yang siap diisi metode aksi. Perintah berikut membuat controller HalamanController untuk proyek contoh, tetapi tidak dijalankan ulang di sini karena kelasnya sudah ada.
# tidak-dijalankan
php artisan make:controller HalamanControllerKeluaran asli perintah tersebut saat pertama kali dijalankan pada proyek contoh adalah sebagai berikut.
INFO Controller [app/Http/Controllers/HalamanController.php] created successfully. Kelas yang dihasilkan berada di app/Http/Controllers dengan namespace App\Http\Controllers dan mewarisi kelas dasar Controller. Isi berkas setelah tiga metode aksi ditambahkan adalah sebagai berikut.
// tidak-dijalankan
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class HalamanController extends Controller
{
/**
* Menampilkan halaman beranda aplikasi.
*/
public function index(): View
{
return view('beranda', ['judul' => 'Beranda aplikasi catatan']);
}
/**
* Menampilkan halaman tentang aplikasi.
*/
public function tentang(): View
{
return view('tentang', ['judul' => 'Tentang aplikasi catatan']);
}
/**
* Menjawab permintaan dengan menyebut jalurnya.
*/
public function sapa(Request $request): string
{
return 'Permintaan untuk ' . $request->path();
}
}Setiap metode publik dapat menjadi aksi rute. Dua metode pertama mengembalikan view beserta datanya dalam bentuk array pada argumen kedua view(), sedangkan metode sapa digunakan pada bagian injeksi dependensi di bawah. Metode aksi dikaitkan ke rute dengan sintaks [Kelas::class, 'metode'] seperti berikut.
// tidak-dijalankan
<?php
use App\Http\Controllers\HalamanController;
use Illuminate\Support\Facades\Route;
Route::get('/', [HalamanController::class, 'index']);
Route::get('/tentang', [HalamanController::class, 'tentang']);
Route::get('/sapa', [HalamanController::class, 'sapa']);Dua view yang dirujuk oleh metode index() dan tentang() berisi templat singkat berikut.
<h1>{{ $judul }}</h1>
<p>Tempat menyimpan catatan pribadi.</p><h1>{{ $judul }}</h1>
<p>Contoh aplikasi untuk jalur tutorial Laravel.</p>Tempatkan keempat berkas di atas pada lokasinya, yaitu controller di app/Http/Controllers, rute di routes/web.php, dan kedua view di resources/views. Daftar rute kemudian menampilkan nama metode aksi sebagai penangan setiap URI.
# tempatkan berkas controller, rute, dan view pada proyek uji
cp HalamanController.php ~/aplikasi-catatan/app/Http/Controllers/HalamanController.php
cp web.php ~/aplikasi-catatan/routes/web.php
mkdir -p ~/aplikasi-catatan/resources/views
cp beranda.blade.php ~/aplikasi-catatan/resources/views/beranda.blade.php
cp tentang.blade.php ~/aplikasi-catatan/resources/views/tentang.blade.php
cd ~/aplikasi-catatan
# siapkan routes/api.php minimal agar keluaran route:list tetap sama
cat > routes/api.php <<'PHP'
<?php
use Illuminate\Support\Facades\Route;
Route::get('/status', function () {
return response()->json(['status' => 'ok']);
});
PHP
# tampilkan rute beserta aksi controllernya
php artisan route:list --except-vendor GET|HEAD / ................................... HalamanController@index
GET|HEAD api/status ................................. routes/api.php:5
GET|HEAD sapa ................................. HalamanController@sapa
GET|HEAD tentang ........................... HalamanController@tentang
Showing [4] routesKolom aksi kini memuat HalamanController@index alih-alih lokasi berkas, yang menandakan rute sudah ditangani controller. Baris berawalan api/ berasal dari routes/api.php yang dibuat pada blok perintah di atas dan tidak terkait controller yang baru ditambahkan. Isi responsnya dapat diperiksa dengan permintaan sintetis ke halaman beranda.
cd ~/aplikasi-catatan
# kirim permintaan GET ke halaman beranda
php artisan tinker --execute='echo app(\Illuminate\Contracts\Http\Kernel::class)->handle(\Illuminate\Http\Request::create("/"))->getContent();'<h1>Beranda aplikasi catatan</h1>
<p>Tempat menyimpan catatan pribadi.</p>HTML di atas adalah hasil render beranda.blade.php dengan variabel $judul yang dikirim controller. Alurnya mengikuti siklus permintaan HTTP pada Laravel, yaitu router memanggil metode aksi controller, lalu nilai yang dikembalikan diubah menjadi respons.
Resource controller#
Operasi CRUD pada sebuah data mengikuti pola URI dan metode HTTP yang berulang untuk setiap sumber daya. Laravel menyediakan resource controller untuk pola ini, yaitu controller yang metode-metodenya sudah mengikuti pola tersebut. Perintah berikut menghasilkan controller tersebut, dan keluaran aslinya diperlihatkan sesudahnya.
# tidak-dijalankan
php artisan make:controller CatatanController --resource
INFO Controller [app/Http/Controllers/CatatanController.php] created successfully. Kelas hasil perintah tersebut berisi tujuh metode kosong yang masing-masing melayani satu operasi pada sumber daya bernama catatan.
| Metode | URI | Metode HTTP | Nama rute |
|---|---|---|---|
index | /catatan | GET | catatan.index |
create | /catatan/create | GET | catatan.create |
store | /catatan | POST | catatan.store |
show | /catatan/{catatan} | GET | catatan.show |
edit | /catatan/{catatan}/edit | GET | catatan.edit |
update | /catatan/{catatan} | PUT/PATCH | catatan.update |
destroy | /catatan/{catatan} | DELETE | catatan.destroy |
Seluruh rute pada tabel itu terdaftar sekaligus dengan satu baris Route::resource('catatan', CatatanController::class); pada routes/web.php. Pola ini menyeragamkan URI aplikasi, dan daftar rutenya selalu dapat diperiksa ulang melalui php artisan route:list.
Injeksi dependensi pada metode aksi#
Metode aksi dapat menuliskan type hint dependensi pada daftar parameternya, dan service container Laravel menyuntikkannya secara otomatis ketika rute dieksekusi. Mekanisme inilah yang digunakan metode sapa() pada controller di atas, yaitu menuliskan Illuminate\Http\Request sebagai type hint agar objek permintaan yang sedang berjalan tersedia di dalam metode tanpa perlu dibuat manual.
Nilai path() pada objek Request adalah URI permintaan tanpa nama domain. Respons metode tersebut menunjukkan bahwa injeksi tersebut benar-benar terjadi.
cd ~/aplikasi-catatan
# kirim permintaan ke rute yang aksinya menyuntikkan Request
php artisan tinker --execute='echo app(\Illuminate\Contracts\Http\Kernel::class)->handle(\Illuminate\Http\Request::create("/sapa"))->getContent();'Permintaan untuk sapaTeks sapa pada keluaran berasal dari path() pada objek Request yang disuntikkan ke metode. Injeksi yang sama berlaku untuk dependensi lain, termasuk parameter rute yang diletakkan setelah dependensi pada daftar parameter, serta constructor injection, yaitu penyuntikan dependensi saat controller dibuat.
Kesalahan umum#
Kesalahan pertama adalah rute menunjuk view yang belum dibuat. Pemanggilan view() dengan nama yang tidak ada melempar exception yang menyebut nama view tersebut, sebagaimana terekam pada percobaan berikut.
cd ~/aplikasi-catatan
# coba render view yang tidak ada
php artisan tinker --execute='view("tidak-ada")->render();' InvalidArgumentException View [tidak-ada] not found.Pesan View [tidak-ada] not found. berarti nama pada view() harus cocok dengan nama berkas di resources/views, tanpa akhiran .blade.php. Kesalahan kedua adalah lupa mengimpor kelas controller pada routes/web.php, sehingga router gagal membuat kelasnya dan melaporkan error Class "..." not found ketika rute diakses. Perbaikannya adalah menambahkan use App\Http\Controllers\HalamanController; di bagian atas berkas rute. Kesalahan ketiga adalah menempatkan logika domain yang panjang ke dalam controller. Controller yang bertugas hanya membaca masukan, memanggil lapisan lain, dan mengembalikan respons lebih mudah diuji daripada controller yang menampung seluruh logika aplikasi.
Kesalahan keempat adalah menempatkan pekerjaan berat pada constructor controller, misalnya membaca data dari database. Constructor dijalankan pada setiap permintaan yang menuju controller tersebut, sebelum aksi tujuan diketahui, sehingga pekerjaan yang sebenarnya hanya diperlukan satu aksi tetap dieksekusi oleh aksi lainnya. Data semacam itu diambil di dalam metode aksinya sendiri, sedangkan constructor hanya digunakan untuk menyimpan dependensi ringan seperti layanan yang diinjeksi melalui type hint.
Ringkasan#
- Controller dibuat dengan
php artisan make:controller, berada diapp/Http/Controllers, dan aksinya dikaitkan ke rute dengan sintaks[Kelas::class, 'metode']. - Resource controller dari
make:controller --resourcemenyediakan tujuh metode CRUD yang seluruh rutenya didaftarkan olehRoute::resource(). - Metode aksi menerima dependensi yang dituliskan sebagai type hint pada parameternya melalui injeksi otomatis dari service container, misalnya objek
Request. - Nama pada
view()merujuk berkas diresources/viewstanpa akhiran.blade.php, dan nama yang salah melemparInvalidArgumentExceptionberisi nama view tersebut.
Referensi#
- Controllers — Laravel — Dokumentasi resmi controller yang menjadi dasar pembahasan aksi dasar, resource controller, dan injeksi dependensi pada topik ini.
- Routing — Laravel — Bagian parameter rute dan urutan penyuntikannya yang dirujuk pada penjelasan injeksi dependensi.