Aplikasi kecil bisa menampung seluruh controller dalam satu direktori datar. Namun begitu fitur bertambah, app/Http/Controllers berubah menjadi deretan puluhan berkas dengan nama seperti AdminDashboardController dan AdminPenggunaExportController yang saling bertabrakan secara konsepsi. Solusi yang disediakan Laravel sekaligus standar PHP adalah sub-namespace: mengelompokkan controller ke dalam sub-direktori yang mencerminkan domain aplikasi. Chapter ini membangun area admin sebagai contoh — direktori, namespace, route, sampai feature test-nya.
Membuat Controller di Sub-Direktori #
Generator menerima path relatif dengan pemisah garis miring. Perintah berikut membuat controller di dalam direktori Admin:
php artisan make:controller Admin/DashboardControllerOutput yang Dihasilkan:
INFO Controller [app/Http/Controllers/Admin/DashboardController.php] created successfully. Isi berkas hasil generator memperlihatkan satu perubahan kunci dibanding controller yang selama ini kita buat:
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class DashboardController extends Controller
{
//
}Baris namespace App\Http\Controllers\Admin; menjadikan nama lengkap kelas App\Http\Controllers\Admin\DashboardController. Generator juga otomatis menambahkan use App\Http\Controllers\Controller; agar extends Controller tetap menunjuk base controller yang benar, bukan kelas Controller di dalam namespace Admin yang tidak ada.
Pemetaan direktori ke namespace ini diatur PSR-4 di composer.json — aturan autoload yang sudah kita bahas pada Section Fondasi PHP untuk Laravel:
| Lokasi berkas | Namespace | Nama lengkap kelas |
|---|---|---|
Controllers/DashboardController.php | App\Http\Controllers | App\Http\Controllers\DashboardController |
Controllers/Admin/DashboardController.php | App\Http\Controllers\Admin | App\Http\Controllers\Admin\DashboardController |
Controllers/Admin/Laporan/PenjualanController.php | App\Http\Controllers\Admin\Laporan | App\Http\Controllers\Admin\Laporan\PenjualanController |
Routing ke Controller Bersarang #
Pendaftaran route memakai nama kelas lengkap lewat import. Kita lengkapi area admin dengan controller pengguna beserta satu invokable, lalu daftarkan semuanya dalam satu prefix group — pola yang sudah kita kenal dari chapter Route Groups:
<?php
use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\Admin\PenggunaController;
use App\Http\Controllers\Admin\PenggunaRingkasanController;
use Illuminate\Support\Facades\Route;
Route::prefix('admin')->name('admin.')->group(function () {
Route::get('/dasbor', [DashboardController::class, 'index'])->name('dasbor');
Route::get('/pengguna', [PenggunaController::class, 'index'])->name('pengguna');
Route::get('/pengguna/ringkasan', PenggunaRingkasanController::class)->name('pengguna.ringkasan');
});Tiga baris di dalam group menggabungkan seluruh materi Section ini: prefix admin. pada nama route, handler array untuk controller biasa, dan nama kelas polos untuk controller invokable. Kita periksa hasil pendaftarannya:
php artisan route:list --except-vendor --path=adminOutput yang Dihasilkan:
GET|HEAD admin/dasbor ................................................... admin.dasbor › Admin\DashboardController@index
GET|HEAD admin/pengguna ................................................ admin.pengguna › Admin\PenggunaController@index
GET|HEAD admin/pengguna/ringkasan ......................... admin.pengguna.ringkasan › Admin\PenggunaRingkasanController
Showing [3] routesKolom paling kanan kini menampilkan namespace Admin\ sebelum nama kelasnya — bukti visual bahwa kita memanggil controller yang berbeda dari controller area publik, meskipun namanya sama sekalipun. Justru di sinilah kekuatan namespace: App\Http\Controllers\Admin\DashboardController dan App\Http\Controllers\DashboardController adalah dua kelas yang sah dan boleh hidup berdampingan, sehingga tiap area bebas memakai nama paling natural tanpa perlu awalan seperti AdminDashboardController yang bertele-tele.
Menjaga Struktur Saat Membesar #
Sub-direktori paling natural mengikuti cara tim kita bicara tentang aplikasi. Area admin mengelola pengguna, area publik menampilkan konten, dan endpoint API melayani client — tiga dunia yang berhak punya folder sendiri. Batasi kedalaman nesting ke dua tingkat saja; direktori Controllers/Admin/Laporan/ masih terbaca wajar, tetapi rantai lima folder biasanya tanda bahwa domainnya perlu dipecah menjadi modul tersendiri:
| Domain | Direktori | Contoh isi |
|---|---|---|
| Admin panel | Controllers/Admin/ | DashboardController, PenggunaController |
| Halaman publik | Controllers/ (root) | PostController, KontakController |
| API | Controllers/Api/ | PostApiController (generator --api) |
| Tugas sekali jalan | Controllers/Admin/ dst. | PenggunaRingkasanController (invokable) |
Feature test memastikan seluruh struktur tersebut benar-benar terjangkau lewat prefix URI:
<?php
namespace Tests\Feature;
use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AdminAreaTest extends TestCase
{
use RefreshDatabase;
public function test_controller_admin_terjangkau_lewat_prefix(): void
{
Post::create(['title' => 'Belajar Controller', 'content' => 'Isi pertama.']);
User::create([
'name' => 'Budi',
'email' => '[email protected]',
'password' => 'rahasia-dummy',
]);
$this->get('/admin/dasbor')->assertOk()->assertSee('total post: 1');
$this->get('/admin/pengguna')->assertOk()->assertSee('total: 1');
$this->get('/admin/pengguna/ringkasan')->assertOk()->assertSee('Ringkasan pengguna: 1');
}
}Output yang Dihasilkan:
PASS Tests\Feature\AdminAreaTest
✓ controller admin terjangkau lewat prefix 0.19s
Tests: 1 passed (6 assertions)
Duration: 0.27sEnam assertion menegaskan bahwa namespace bersarang tidak mengubah cara kerja controller sama sekali — route, dependency injection, dan response semuanya berperilaku identik; yang berubah hanya alamat kelasnya. Struktur controller pun idealnya tercermin pada routes/web.php: satu blok prefix group per area, sehingga urutan route file mengikuti urutan folder controller dan keduanya mudah dibaca berdampingan. Konvensi selengkapnya, termasuk sub-direktori default Laravel lainnya, terdokumentasi di dokumentasi controllers
.
Struktur direktori menjawab di mana controller ditempatkan. Satu pertanyaan terakhir yang lebih mendasar menunggu: apa saja yang boleh ditulis di dalamnya? Chapter Separation of Concerns di Controller — penutup Section ini — merangkai validasi dan logika bisnis ke lapisan yang tepat, menjadikan controller koordinator sejati.