Behavior Service Provider
PandaPanel\PandaPanelServiceProvider adalah pusat seluruh hal yang dilakukan package terhadap application saat boot:
- tujuh container binding;
- delapan boot step;
- empat publish group;
- tiga belas Artisan command.
Provider ini di-auto-discover oleh Composer, sehingga application tidak perlu mendaftarkannya secara manual.
Gunakan halaman ini ketika Anda perlu mengetahui:
- apa yang berjalan saat package boot;
- urutan prosesnya;
- config key mana yang benar-benar mematikan sebuah behavior;
- atau bagaimana menyalakan framework secara manual pada test harness/non-standard Laravel application.
Contoh minimal yang berfungsi
{
"extra": {
"laravel": {
"providers": ["PandaPanel\\PandaPanelServiceProvider"],
"aliases": { "PandaPanel": "PandaPanel\\Facades\\PandaPanel" }
}
}
}2
3
4
5
6
7
8
Itulah bagian dari composer.json milik package.
Jadi installation package cukup:
composer require chocoalano/panelLaravel package discovery akan mendaftarkan Service Provider dan facade alias.
Untuk memeriksa hasilnya:
use PandaPanel\Core\PanelManager;
app(PanelManager::class)->all(); // list<Panel>
app()->getLoadedProviders(); // includes PandaPanel\PandaPanelServiceProvider2
3
4
register()
register() berjalan sebelum method boot() milik provider mana pun.
Method ini melakukan dua hal:
- merge konfigurasi package;
- register container bindings.
$this->mergeConfigFrom(
$this->packagePath('config/panda-panel.php'),
'panda-panel'
);2
3
4
mergeConfigFrom() menggunakan shallow array_merge, dengan file config yang dipublish application memiliki prioritas.
Jika Laravel configuration sudah dicache, merge ini dilewati karena cached config sudah berisi hasil merge.
| Binding | Lifetime | Peran |
|---|---|---|
PandaPanel\Core\PanelRegistry | singleton | Menyimpan Panel yang terdaftar berdasarkan id. |
PandaPanel\Support\PanelContext | scoped | Menyimpan current Panel untuk request saat ini. Direset per request pada Octane. |
PandaPanel\Discovery\PanelDiscoverer | singleton | Melakukan scan terhadap discovery path Panel. |
PandaPanel\Cache\PanelManifest | singleton | Membaca cached class list atau menjalankan discovery jika cache tidak tersedia. |
PandaPanel\Core\PanelManager | singleton | Entry point utama seluruh operasi Panel. |
PandaPanel\Support\NavigationBuilder | singleton | Membangun navigation/sidebar tree untuk Panel dan path tertentu. |
PandaPanel\Routing\PanelRouteRegistrar | singleton | Mendaftarkan satu route group per Panel, menggunakan Registrar contract dan Panel Manager. |
PanelContext menggunakan lifetime scoped, bukan singleton, secara sengaja.
Class tersebut menyimpan request state. Pada Octane, singleton dapat membawa current Panel dari request sebelumnya ke request berikutnya.
ResetPanelContext menjaga invariant yang sama dari sisi HTTP middleware.
use PandaPanel\Core\PanelManager;
use PandaPanel\Support\PanelContext;
app(PanelManager::class); // same instance every time
app(PanelContext::class); // same instance within one request2
3
4
5
boot()
Ada delapan step, dalam urutan berikut. Urutannya penting:
public function boot(): void
{
$this->registerPanels();
$this->registerMiddleware();
$this->registerGuestRedirect();
$this->registerRoutes();
$this->registerIntegrations();
$this->registerMigrations();
$this->registerPublishing();
$this->registerCommands();
}2
3
4
5
6
7
8
9
10
11
| Step | Config key | Yang dilakukan |
|---|---|---|
registerPanels() | panels | Membangun seluruh configured provider, lalu memeriksa manifest staleness. |
registerMiddleware() | register_web_middleware | Selalu mendaftarkan empat alias; menambahkan empat middleware web kecuali dimatikan. |
registerGuestRedirect() | register_guest_redirect | Mengarahkan Authenticate::redirectUsing() dan AuthenticationException::redirectUsing() ke PanelLoginRedirect. |
registerRoutes() | register_routes | Menjalankan PanelRouteRegistrar::registerAll(). |
registerIntegrations() | — | Mendaftarkan model observer untuk setiap Resource yang mengaktifkan integrations. |
registerMigrations() | load_migrations | Menjalankan loadMigrationsFrom(package/database/migrations). |
registerPublishing() | — | Hanya pada console; mendaftarkan empat publish group. |
registerCommands() | — | Hanya pada console; mendaftarkan tiga belas command dan optimize hook. |
Panel harus diregistrasikan lebih dahulu karena step berikutnya membutuhkan informasi Panel.
Contohnya:
- Route Registrar membutuhkan path dan middleware Panel.
- Integration observer membutuhkan Resource Registry Panel.
registerPanels()
foreach ($this->configuredPanels() as $provider) {
if (! $manager->has((new $provider)->panelId())) {
$manager->registerProvider($provider);
}
}
$this->app
->make(PanelManifest::class)
->warnIfStale(
$this->app->make(
PanelRegistry::class
)
);2
3
4
5
6
7
8
9
10
11
12
13
Provider yang tercantum dua kali hanya diregistrasikan satu kali karena Panel Registry di-key berdasarkan Panel id.
Menjalankan registration yang sama dua kali hanya akan mengulang discovery tanpa mengubah hasil akhir.
configuredPanels() memfilter config agar hanya menerima entry yang:
- berupa string;
- merupakan subclass
PandaPanel\Core\PanelProvider.
Entry lain dilewati secara silent.
Pilihan ini disengaja. Class name yang sudah tidak dapat di-resolve dapat membuat fatal error saat boot, bahkan sebelum route mana pun tersedia untuk menjelaskan masalah tersebut.
php artisan panel:cache menampilkan jumlah Panel yang berhasil diproses, sehingga missing/skipped provider dapat terlihat dari jumlah yang lebih sedikit dari ekspektasi.
Staleness check dijalankan setelah seluruh Panel selesai diregistrasikan agar seluruh discovery path sudah diketahui.
Check tersebut menjadi no-op kecuali:
- manifest tersedia; dan
- environment
localatautesting, atau debug mode aktif.
Production tidak melakukan filesystem validation terhadap manifest pada setiap boot.
registerIntegrations()
foreach ($manager->all() as $panel) {
$registry = $manager->resources($panel);
foreach ($registry->all() as $resource) {
$settings = $resource::integrationSettings();
if (! $settings->enabled()) {
continue;
}
IntegrationObserver::register(
$resource::getModel(),
$panel->getId(),
$registry->slugFor($resource),
$settings,
);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Integration observer didaftarkan saat boot, bukan ketika sebuah Page dirender.
Alasannya, integration memang harus dapat dipicu oleh model write yang terjadi dari berbagai entry point:
HTTP request
Console command
Queued job2
3
Console command dan queued job mungkin tidak pernah merender Panel Page.
Resource yang tidak mengaktifkan integrations tidak mendaftarkan observer.
Jadi cost feature ini bagi Resource biasa hanya berupa satu kali pembacaan integration settings saat boot.
registerPublishing()
Method ini hanya berjalan ketika:
$this->app->runningInConsole()menghasilkan true.
vendor:publish memang hanya relevan pada console.
Konsekuensinya, test yang melakukan boot hanya melalui HTTP lalu membaca ServiceProvider::publishableGroups() tidak akan melihat publish group.
| Tag | Source | Destination |
|---|---|---|
panda-panel-config | config/panda-panel.php | config_path('panda-panel.php') |
panda-panel-migrations | database/migrations | database_path('migrations') |
panda-panel-stubs | stubs/panel | base_path('stubs/panel') |
panda-panel-assets | PublishedAssets::map() | resources/js/**, resources/css/panda-panel.css |
Tag berikut juga menjadi bagian dari umbrella tag:
panda-panel-config
panda-panel-migrations
panda-panel-assets2
3
Sedangkan panda-panel-stubs sengaja tidak dimasukkan.
Mempublish stubs mengubah template yang digunakan seluruh generator berikutnya. Perubahan tersebut terlalu besar untuk terjadi secara tidak sengaja hanya karena developer menjalankan umbrella publish.
Asset map berasal dari:
PandaPanel\Support\Installer\PublishedAssetsbukan ditulis ulang langsung di Service Provider.
Dengan demikian:
vendor:publish
dan
panel:assets2
3
menggunakan satu source of truth.
Jika kedua command memiliki daftar file sendiri, daftar tersebut akan drift saat package menambah directory atau component baru.
registerCommands()
Method ini hanya berjalan pada console.
Package mendaftarkan tiga belas command:
| Command | Class |
|---|---|
panel:cache | CachePanelsCommand |
panel:clear | ClearPanelsCommand |
panel:install | InstallPanelCommand |
make:panel | MakePanelCommand |
make:panel-resource | MakePanelResourceCommand |
make:panel-page | MakePanelPageCommand |
make:panel-relation-manager | MakePanelRelationManagerCommand |
panel:user | MakePanelUserCommand |
make:panel-widget | MakePanelWidgetCommand |
panel:assets | PanelAssetsCommand |
panel:plugins | PanelPluginsCommand |
panel:publish | PublishPanelAssetsCommand |
panel:icons | SyncPanelIconsCommand |
Package juga mendaftarkan optimize hook:
$this->optimizes(
optimize: 'panel:cache',
clear: 'panel:clear',
key: 'panels',
);2
3
4
5
Sehingga:
php artisan optimizejuga menjalankan panel:cache, dan:
php artisan optimize:clearmenjalankan panel:clear.
Deployment yang sudah menggunakan optimize otomatis mendapatkan Panel Manifest cache.
Dua hook afterResolving(Kernel::class)
Web middleware dan guest redirect sama-sama diregistrasikan melalui HTTP Kernel setelah Kernel di-resolve, bukan langsung ketika provider boot.
Alasannya sama untuk keduanya.
bootstrap/app.php memanggil withMiddleware(), yang memasang hook afterResolving(Kernel::class) milik Laravel.
Hook tersebut:
- membangun object
Middleware; - mengatur default guest redirect
fn () => route('login'); - memanggil
$kernel->setMiddlewareGroups(...).
Akibatnya, middleware atau static redirect yang ditetapkan package terlalu awal dapat ditimpa ketika Kernel selesai di-resolve.
PandaBear mendaftarkan hook lebih akhir agar setting-nya tetap bertahan.
Kedua registration method juga menangani kondisi Kernel sudah di-resolve lebih dahulu, misalnya pada test:
if ($this->app->resolved(Kernel::class)) {
$append(
$this->app->make(
Kernel::class
)
);
}
$this->app
->afterResolving(
Kernel::class,
$append
);2
3
4
5
6
7
8
9
10
11
12
13
appendMiddlewareToGroup() idempotent, sehingga callback yang dijalankan dua kali tetap tidak menghasilkan duplicate middleware.
Menyalakan framework secara manual
Normalnya Provider di-auto-discover.
Jika package discovery dimatikan atau Anda berada di custom harness, register provider secara manual.
// bootstrap/providers.php
return [
App\Providers\AppServiceProvider::class,
PandaPanel\PandaPanelServiceProvider::class,
];2
3
4
5
6
Jika package sengaja dikecualikan dari discovery:
{
"extra": {
"laravel": {
"dont-discover": ["chocoalano/panel"]
}
}
}2
3
4
5
6
7
protected function getPackageProviders($app): array
{
return [
Inertia\ServiceProvider::class,
Laravel\Fortify\FortifyServiceProvider::class,
PandaPanel\PandaPanelServiceProvider::class,
];
}2
3
4
5
6
7
8
Inertia bukan dependency yang dapat diabaikan.
Seluruh screen PandaBear merupakan Inertia response dan SharePanelData menjalankan Inertia::share() pada web request.
Mematikan step tertentu
| Config key | Jika false | Konsekuensi |
|---|---|---|
register_routes | Route group tidak diregistrasikan | Registry tetap dibangun, tetapi tidak ada URL Panel. Lihat Route Registration. |
register_web_middleware | Empat middleware web tidak ditambahkan | Middleware alias tetap ada. Lihat Middleware Registration. |
register_guest_redirect | Authenticate::redirectUsing() tidak diubah | Default Laravel route('login') berlaku. Lihat Guest Redirect. |
load_migrations | Package migrations tidak diload | Publish migration jika application ingin memilikinya. Lihat Migration Loading. |
Daftar panels selalu dibaca.
Tidak ada config key untuk "register package tetapi jangan register Panel sama sekali". Jika Panel ingin dihapus, hapus provider-nya dari list panels.
Hal yang perlu diperhatikan
boot()berjalan sekali per process. Pada Octane, Panel definition, route, dan observer dibangun saat worker start lalu digunakan kembali. PerlakukanPanelsebagai immutable configuration.- Discovery berjalan di
registerPanels()jika manifest belum ada. Cold boot tanpa manifest akan melakukan scan seluruh discovery path sebelum request pertama dicocokkan ke route. Karena itu production sebaiknya menjalankanpanel:cache. PanelRegistrationExceptionsaat boot bersifat fatal dan tidak ditangkap. Duplicate slug, id, atau colliding route lebih baik menggagalkan boot daripada menghasilkan framework yang ter-register setengah.registerPublishing()danregisterCommands()hanya berjalan pada console. Test yang memeriksa publish group atau command registration harus boot melalui console kernel.- Service Provider tidak me-resolve request-scoped service. Provider boot sebelum ada request. Current user tidak boleh di-resolve dari
PanelProvider::panel(). - Facade alias adalah
PandaPanel. Facade tersebut memproxyPanelManager, bukan Service Provider.