Di chapter Package Development
, paket budi/kopitools terpasang dan provider-nya langsung aktif — padahal kita tidak pernah menyentuh daftar provider di bootstrap/providers.php. Mekanisme di baliknya bernama package discovery: Laravel membaca metadata semua paket composer yang terpasang, membangun manifest provider dan alias, lalu memuat semuanya saat booting secara otomatis. Chapter ini membedah manifest tersebut dari berkas sungguhan, membedah extra.laravel dari sisi paket, lalu menguji cara mematikan discovery dan jebakan paket dev. Referensi resminya tetap dokumentasi packages
.
Sebelum Auto-Discovery #
Pada aplikasi Laravel 5.x, memasang paket selalu diikuti ritual manual di config/app.php (contoh tampilan, sudah tidak dipakai di Laravel 11+):
'providers' => [
/*
* Package Service Providers...
*/
Laravel\Tinker\TinkerServiceProvider::class,
Barryvdh\DomPDF\ServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
],
'aliases' => [
'PDF' => Barryvdh\DomPDF\Facade::class,
'Kas' => Budi\Kopitools\Facades\Kas::class,
],Ritual ini punya dua biaya tersembunyi. Pertama, langkah pemasangan mudah terlewat — paket ter-composer require tapi provider-nya tidak pernah booting. Kedua, berkas config/app.php yang sama diedit semua orang, sehingga setiap upgrade framework berpotensi konflik merge.
Manifest di bootstrap/cache/packages.php #
Sejak Laravel 5.5, pekerjaan pendaftaran diambil alih composer. Skeleton aplikasi memiliki script post-autoload-dump yang berjalan setiap kali autoload dibangun ulang:
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
}Perintah package:discover memindai metadata seluruh paket terpasang lalu menuliskan hasilnya ke bootstrap/cache/packages.php. Inilah isi nyata berkas tersebut di aplikasi latihan kita, dipangkas ke tiga paket:
<?php return array (
'budi/kopitools' =>
array (
'providers' =>
array (
0 => 'Budi\\Kopitools\\KopitoolsServiceProvider',
),
'aliases' =>
array (
'Kas' => 'Budi\\Kopitools\\Facades\\Kas',
),
),
'laravel/sanctum' =>
array (
'providers' =>
array (
0 => 'Laravel\\Sanctum\\SanctumServiceProvider',
),
),
'nesbot/carbon' =>
array (
'providers' =>
array (
0 => 'Carbon\\Laravel\\ServiceProvider',
),
),
);Saat booting, Application membaca manifest ini dan mendaftarkan semua provider serta alias tanpa perlu satu baris konfigurasi dari kita. Manifest dibangun dari vendor/composer/installed.json — inilah sumber kebenarannya.
extra.laravel di Sisi Paket #
Dari mana isi manifest berasal? Dari field extra.laravel di composer.json setiap paket. Sanctum misalnya hanya menyumbang provider:
{
"name": "laravel/sanctum",
"extra": {
"laravel": {
"providers": [
"Laravel\\Sanctum\\SanctumServiceProvider"
]
}
}
}Paket budi/kopitools kita menyumbang provider plus alias facade Kas. Alur lengkapnya: extra.laravel di composer.json paket → dibaca oleh package:discover → dituangkan ke manifest → dimuat saat booting. Karena manifest berupa berkas cache, menghapusnya tidak berbahaya — composer dump-autoload akan membangunnya kembali.
Menutup Discovery: dont-discover #
Kadang satu paket harus dimatikan discovery-nya: paket yang ingin didaftarkan manual dengan konfigurasi khusus, atau paket yang hanya dipakai di lingkungan tertentu. Kuncinya ada di composer.json root aplikasi, di bawah extra.laravel.dont-discover — framework membaca key ini persis tanpa apostrof:
"extra": {
"laravel": {
"dont-discover": ["laravel/sanctum"]
}
}Kita uji langsung di sandbox: setelah menambahkan baris tersebut dan menjalankan composer dump-autoload, manifest dibangun ulang dan laravel/sanctum lenyap darinya. Efeknya nyata — provider Sanctum tidak pernah booting, jadi route bawaannya ikut menghilang dari route:list:
Output yang Dihasilkan:
sebelum: GET|HEAD sanctum/csrf-cookie sanctum.csrf-cookie › Laravel\Sanctum › CsrfC…
sesudah: (tidak ada route sanctum — grep mengembalikan 0 baris)Setelah dont-discover dikosongkan kembali dan dump-autoload diulang, route Sanctum kembali hadir. Nilai "*" bahkan mematikan semua discovery — kita coba dan manifest menjadi array () kosong total, lalu pulih setelah dibatalkan.
Jebakan Paket Dev #
Berkas installed.json mencatat paket produksi dan paket dev sekaligus, dan manifest builder tidak membedakannya. Kita buktikan dengan paket kecil budi/devmarker yang hanya masuk require-dev. Di mesin pengembangan, paket itu ditemukan dan route /devmarker hidup:
Output yang Dihasilkan:
GET|HEAD devmarker packages/budi/devmarker/src/DevmarkerServiceProvider.ph…
paket di packages.php: budi/devmarker, budi/kopitools, laravel/sanctum, ...Sekarang kita simulasikan deployment produksi dengan composer install --no-dev:
Output yang Dihasilkan:
Package operations: 0 installs, 0 updates, 34 removals
paket non-dev yang tersisa: budi/kopitools, laravel/sanctum, nesbot/carbon
budi/devmarker masih ada? False
(route devmarker: 0 baris)Tiga puluh empat paket dev dihapus, budi/devmarker lenyap dari manifest, route-nya hilang, dan composer install (dengan dev) memulihkan semuanya. Pelajarannya tegas: paket dev tidak boleh menanggung fitur runtime. Paket debugging, generator, atau alat bantu boleh hidup di require-dev; apa pun yang dipakai request produksi wajib masuk require.
Discovery menjelaskan bagaimana satu kode dipakai banyak aplikasi. Chapter berikutnya melihat dari sisi sebaliknya: satu aplikasi yang melayani banyak pelanggan — arsitektur multi-tenancy.