Ringkasan
PandaBear adalah framework panel admin untuk Laravel 12 dan 13, dibangun di atas Inertia 3, Vue 3, dan Tailwind 4. PHP memegang registrasi, routing, otorisasi, penyusunan query, validasi, dan metadata; Vue memegang rendering; Inertia adalah satu-satunya jembatan. Pakai framework ini ketika sebuah aplikasi membutuhkan CRUD resource, tabel dengan pencarian, sorting, filter, dan paginasi di sisi server, form dengan validasi sungguhan, dasbor, serta halaman mandiri — dan membutuhkan lebih dari satu panel semacam itu, masing-masing dengan path, navigasi, middleware, dan aturan aksesnya sendiri.
Panel dari nol
composer require chocoalano/panel
php artisan panel:install2
panel:install mem-publish config dan frontend, menyiapkan panel pertama, menuliskannya ke config/panda-panel.php, memeriksa apa yang masih dibutuhkan frontend, lalu menawarkan pembuatan akun yang bisa masuk. Berikut yang ditulisnya:
<?php
declare(strict_types=1);
namespace App\Panels\Admin;
use PandaPanel\Core\Panel;
use PandaPanel\Core\PanelProvider;
final class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->path('admin')
->name('Admin')
->icon('layout-grid')
->auth()
->discoverResources(app_path('Panels/Admin/Resources'))
->discoverPages(app_path('Panels/Admin/Pages'))
->discoverWidgets(app_path('Panels/Admin/Widgets'));
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Panel hasil generator belum punya ->canAccess(), jadi setiap pengguna yang sudah login boleh masuk. Tambahkan predikatnya sebelum panel itu memuat sesuatu yang layak dijaga:
use Illuminate\Contracts\Auth\Authenticatable;
use App\Models\User;
->canAccess(static fn (?Authenticatable $user): bool => $user instanceof User && $user->is_admin)2
3
4
// config/panda-panel.php
'panels' => [
App\Panels\Admin\AdminPanelProvider::class,
],2
3
4
Tambahkan satu resource, dan resource itu langsung ter-routing, bisa dinavigasi, serta diotorisasi tanpa suntingan lain:
php artisan make:panel-resource User --panel=Admin
npm run build2
Perintah itu menulis app/Panels/Admin/Resources/Users/UserResource.php beserta halaman list, create, view, dan edit-nya, tabelnya, dan form-nya. Panel menemukannya karena kelas itu berada di bawah path discovery yang sudah dideklarasikan. /admin/users kini ada.
Dua namespace
| Namespace | Isinya |
|---|---|
PandaPanel\* | framework-nya: Panel, Resource, TableSchema, FormSchema, Action, Widget, middleware, controller |
App\Panels\* | panel Anda: provider, resource, page, widget, relation manager |
Tidak ada bagian aplikasi Anda yang meng-extend controller atau mendaftarkan route untuk layar panel. Anda menulis deklarasi; framework yang me-routing-nya.
Panel adalah unitnya
Panel adalah satu area admin yang bisa dialamati. Ia punya id, path, domain opsional, tumpukan middleware, grup navigasi, branding, predikat akses, serta registry resource, page, dan widget-nya sendiri. Dua panel di /admin dan /app berbagi sesi dan model user, dan tidak berbagi apa pun lagi: resource yang terdaftar di salah satunya tidak punya route di yang lain, dan Resource::url() melempar exception ketika dimintai URL di panel yang tidak mendaftarkannya.
Satu panel provider mengonfigurasi tepat satu panel:
namespace PandaPanel\Core;
abstract class PanelProvider
{
abstract public function panel(Panel $panel): Panel;
public function panelId(): string; // AdminPanelProvider → 'admin'
public function build(): Panel; // panel(Panel::make($this->panelId()))
}2
3
4
5
6
7
8
9
10
panelId() menurunkan id dari nama kelas, jadi AdminPanelProvider adalah panel admin. Panggil ->id('back-office') di dalam panel() untuk menimpanya.
Panel didaftarkan di config, bukan ditemukan otomatis. Urutan pendaftarannya terlihat di satu tempat, dan urutan itulah yang menentukan ke mana pengguna yang sudah login mendarat ketika request tidak menyebut panel tertentu. Kelas-kelas di dalam panel ditemukan otomatis, karena mendaftar setiap resource satu per satu adalah boilerplate yang memang layak dihapus.
Menjangkau panel saat runtime
use PandaPanel\Core\Panel;
panel(); // Panel|null — panel untuk request ini, null di luar panel
panel('admin'); // Panel — melempar PanelRegistrationException bila tidak dikenal2
3
4
panel() di-autoload lewat files milik composer, jadi tidak perlu import. Untuk segala hal di luar "saya sedang di panel mana", facade-nya membungkus PandaPanel\Core\PanelManager:
use PandaPanel\Facades\PandaPanel;
PandaPanel::all(); // list<Panel>, terurut berdasarkan id
PandaPanel::has('admin'); // bool
PandaPanel::get('admin'); // Panel, melempar bila tidak dikenal
PandaPanel::currentPanel(); // Panel|null
PandaPanel::setCurrentPanel(panel('admin')); // void
PandaPanel::resolveFromRequest($request); // Panel|null, path terpanjang menang
PandaPanel::firstAccessibleTo($request->user());
PandaPanel::resources($panel); // ResourceRegistry
PandaPanel::pages($panel); // PageRegistry
PandaPanel::widgets($panel); // WidgetRegistry
PandaPanel::navigation($panel); // NavigationRegistry2
3
4
5
6
7
8
9
10
11
12
13
register(Panel $panel) dan registerProvider(string $provider) juga tersedia, untuk paket yang menambahkan panel dari service provider-nya sendiri alih-alih dari config aplikasi.
Apa yang terjadi pada sebuah request
request
↓ web middleware ResetPanelContext membersihkan panel sebelumnya
↓ grup route panel middleware panel, lalu ResolvePanel:{id}
↓ PanelContext panel saat ini, ber-scope request
↓ page / resource page otorisasi → bangun metadata → serialisasi
↓ Inertia props bersama (panel, navigasi) + props halaman
↓ Vue PanelLayout → komponen halaman → renderer2
3
4
5
6
7
Setiap route panel menunjuk ke controller sungguhan, tidak pernah ke closure, sehingga php artisan route:cache tetap berfungsi. Nama route-nya panel.{id}.*: panel.admin.dashboard, panel.admin.resources.users.index, panel.admin.actions.record. Arsitektur sekilas menelusuri seluruh jalurnya.
Konfigurasi
config/panda-panel.php, di-publish oleh panel:install atau oleh vendor:publish --tag=panda-panel-config:
| Kunci | Nilai default | Yang ditentukannya |
|---|---|---|
panels | [] | Panel provider yang didaftarkan, berurutan. |
register_routes | true | Mendaftarkan satu grup route per panel saat boot. |
register_web_middleware | true | Menambahkan empat middleware web milik paket ke grupnya. |
register_guest_redirect | true | Mengarahkan tamu yang membuka URL panel ke halaman login panel itu. |
home_redirect.enabled | true | Mengarahkan pengguna yang sudah login dari dasbor starter kit ke panel pertama yang boleh dimasukinya. |
home_redirect.paths | ['dashboard'] | Pola Request::is() yang dialihkan. |
load_migrations | true | Menjalankan migrasi paket dari dalam paket. |
integrations.allowed_hosts | [] | Daftar izin untuk request integrasi keluar. Kosong berarti tidak ada yang bisa dijangkau. |
integrations.block_private_networks | true | Menolak host yang me-resolve ke rentang privat, loopback, atau link-local. |
integrations.history.enabled | true | Mencatat satu baris per percobaan pengiriman. |
integrations.history.keep_per_integration | 50 | Batas keras jumlah baris yang disimpan per integrasi. |
integrations.history.retention_days | 30 | Rentang penyimpanan baris. 0 berarti hanya batas jumlah yang berlaku. |
frontend.panel_path | js/panel | Tempat vendor:publish meletakkan komponen panel. |
frontend.pages_path | js/pages/Panels | Tempat generator membuat komponen Vue. |
Selebihnya — path, domain, middleware, navigasi, branding, akses — dikonfigurasi di dalam kode, karena keputusan-keputusan itu mengandung logika.
Perintah
php artisan panel:install # publish, scaffold, daftarkan, verifikasi
php artisan make:panel Admin --path=back-office
php artisan make:panel-resource User --panel=Admin --soft-deletes
php artisan make:panel-page Reports --panel=Admin --component
php artisan make:panel-widget Revenue --panel=Admin --type=chart
php artisan make:panel-relation-manager posts --panel=Admin --resource=Users
php artisan panel:user --name=Ada --email=ada@example.com --panel=Admin
php artisan panel:cache # discovery sekali saat deploy
php artisan panel:clear
php artisan panel:icons # menulis ulang registry ikon dari ikon yang dideklarasikan
php artisan panel:plugins # apa yang terpasang, di panel mana, versi berapa
php artisan panel:assets # komponen ter-publish mana yang tertinggal versi
php artisan panel:publish # menyalin aset sebuah plugin ke dalam aplikasi2
3
4
5
6
7
8
9
10
11
12
13
14
panel:cache dan panel:clear terdaftar sebagai hook optimize, jadi deploy yang sudah menjalankan php artisan optimize otomatis mendapat manifest panel sekalian.
Tag publish
php artisan vendor:publish --tag=panda-panel-config
php artisan vendor:publish --tag=panda-panel-assets
php artisan vendor:publish --tag=panda-panel-migrations
php artisan vendor:publish --tag=panda-panel-stubs
php artisan vendor:publish --tag=panda-panel # config + migrasi + aset2
3
4
5
Frontend-nya di-publish, bukan diimpor dari paket, karena setiap registry komponen adalah allowlist import.meta.glob atas pohon berkas aplikasi itu sendiri — komponen yang tidak pernah dilihat proses build tidak bisa di-resolve. Itu membuat berkas Vue milik panel menjadi milik Anda: ada di repositori Anda, ikut di-build, dan boleh disunting. panel:assets yang menjaganya tetap mutakhir sesudahnya.
Persyaratan dalam satu baris
PHP 8.2+ (8.2 hanya sampai Laravel 12), Laravel 12 atau 13, inertiajs/inertia-laravel 3.x, Fortify 1.37.2+, Vue 3.5+, Vite 7, Tailwind 4.1+, dan sebuah Laravel Vue starter kit — atau sembilan belas modul frontend yang disediakannya. Matriks lengkapnya, termasuk apa yang sengaja tidak didukung, ada di Matriks Kompatibilitas.
Catatan
- Resource yang baru dibuat mengembalikan 403 sampai modelnya punya policy. Gate ditanya dan menjawab tidak. Panel yang menampilkan semua record hanya karena belum ada yang menulis aturan akan jauh lebih buruk.
- Panel provider yang terdaftar di config tetapi kelasnya sudah tidak ada akan dilewati, bukan membuat boot gagal total, sehingga kelas yang diganti nama tidak membuat seluruh aplikasi tidak bisa diakses.
panel:cachemelaporkan daftar yang sama di tempat kesalahannya benar-benar terlihat. - Panel yang terdaftar dua kali di
panelshanya didaftarkan sekali: registry-nya di-key berdasarkan id, dan menjalankan discovery lagi tidak mengubah apa pun. - Dua panel tidak boleh berbagi id, atau berbagi pasangan path/domain. Keduanya melempar
PandaPanel\Exceptions\PanelRegistrationExceptionsaat boot, alih-alih membiarkan satu route diam-diam menutupi route lainnya. panel()mengembalikannulldi luar panel — di dalam queued job, console command, atau route aplikasi biasa. Kode yang harus berjalan di dalam panel meminta panelnya berdasarkan id.
Baca juga
- Kenapa PandaBear — masalah yang dipecahkan bentuk ini
- Ikhtisar fitur — semua yang tersedia, disebut per nama kelas
- Arsitektur sekilas — jalur request secara detail
- Pendekatan Inertia dan Vue — apa yang menyeberang, dan apa yang merendernya
- Perbandingan dengan konsep Filament — apa yang dipinjam, apa yang tidak
- Batasan dan trade-off paket — biayanya, dinyatakan terang-terangan
- Tutorial delapan langkah — dari nol sampai panel yang jalan
- Mendefinisikan Panel — API
Panelselengkapnya - Membuat Resource