Root view dan middleware Inertia
URL panel pertama mengembalikan 500, biasanya View [app] not found atau error Inertia tentang root template. Setiap layar panel merupakan response Inertia, dan ada dua file milik aplikasi yang harus tersedia agar layar dapat dirender. Gunakan halaman ini ketika melakukan instalasi baru pada aplikasi yang bukan Laravel Vue starter kit, atau setelah root view diganti nama maupun ditulis ulang.
Mulai dari sini
php artisan panel:install --no-panel --no-user --no-interaction WARN 2 thing(s) this package cannot do for your application:
1. This application is missing an Inertia root view at resources/views/app.blade.php.
Every panel screen is an Inertia response and will 500 without it — a Laravel Vue
starter kit has both already.
2. This application is missing Inertia's middleware (php artisan inertia:middleware).
Every panel screen is an Inertia response and will 500 without it — a Laravel Vue
starter kit has both already.2
3
4
5
6
7
8
9
php artisan inertia:middlewaredan buat root view di resources/views/app.blade.php.
Pemeriksaan melalui PHP
use PandaPanel\Support\Installer\FrontendRequirements;
FrontendRequirements::missingInertia();
// ['an Inertia root view at resources/views/app.blade.php',
// "Inertia's middleware (php artisan inertia:middleware)"]2
3
4
5
| Method | Signature | Mengembalikan |
|---|---|---|
missingInertia | static missingInertia(): array | list<string> — daftar yang belum tersedia dalam bentuk penjelasan; kosong jika semuanya tersedia |
Dua path diperiksa, dan keduanya hanya diperiksa keberadaan filenya:
| Path | Fungsinya |
|---|---|
resources/views/app.blade.php | root view tempat Inertia merender halaman |
app/Http/Middleware/HandleInertiaRequests.php | middleware yang dibuat oleh generator Inertia |
Pemeriksaan ini hanya memeriksa file, bukan apakah middleware sudah diregistrasikan. HandleInertiaRequests.php yang tersedia tetapi tidak pernah ditambahkan ke group web tetap lolos pemeriksaan ini dan panel tetap bermasalah — lihat bagian middleware di bawah.
Root view
Kebutuhan minimum Inertia adalah sebuah dokumen yang memiliki @inertia dan bundle yang sudah dibangun:
{{-- resources/views/app.blade.php --}}
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" @class(['dark' => ($appearance ?? 'system') === 'dark'])>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title inertia>{{ config('app.name') }}</title>
@routes
@vite([
'resources/css/app.css',
'resources/js/app.ts',
"resources/js/pages/{$page['component']}.vue",
])
@inertiaHead
</head>
<body>
@inertia
</body>
</html>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Itulah bentuk yang disediakan Laravel Vue starter kit. Kebutuhan panel darinya sebenarnya cukup spesifik:
| Directive | Dibutuhkan karena |
|---|---|
@inertia | menyediakan elemen halaman tempat Inertia melakukan mount; tanpanya response merender dokumen kosong |
@vite([...]) | komponen panel berupa Vue SFC yang dibangun oleh Vite milik aplikasi |
"resources/js/pages/{$page['component']}.vue" | entry per halaman, jika starter kit Anda memisahkan halaman dengan cara ini; komponen halaman panel berada di resources/js/pages/panel/** dan resources/js/pages/Panels/** |
@inertiaHead | hanya diperlukan jika aplikasi menggunakan SSR atau komponen <Head> |
Nama file yang berbeda tidak masalah. Root view Inertia dapat dikonfigurasi (Inertia::setRootView() atau config inertia.root_view), dan panel tidak pernah mengacu langsung pada nama file tersebut — panel memanggil Inertia::render() seperti kode Inertia lainnya. Installer hanya memeriksa path konvensional, sehingga root view yang diganti nama akan dilaporkan tidak tersedia meskipun sebenarnya tidak menjadi masalah.
Asset panel di root view
Entrypoint dari Panel::assets() dikeluarkan di sini, dan tidak di tempat lain:
@vite([
'resources/css/app.css',
'resources/js/app.ts',
"resources/js/pages/{$page['component']}.vue",
...(panel()?->getAssets() ?? []),
])2
3
4
5
6
panel() bernilai null di luar panel, sehingga spread tersebut tidak menambahkan apa pun. Baris ini berada di root view aplikasi, bukan disisipkan oleh package, karena root view merupakan file milik aplikasi.
Panel yang mendeklarasikan entrypoint yang tidak pernah diminta Vite untuk dibangun akan gagal dengan manifest error, bukan sekadar kehilangan style — path tersebut juga harus tercantum pada input di vite.config.ts.
Middleware
php artisan inertia:middleware// bootstrap/app.php
use App\Http\Middleware\HandleInertiaRequests;
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
HandleInertiaRequests::class,
]);
})2
3
4
5
6
7
8
Membuat class hanyalah separuh pekerjaan; meregistrasikannya adalah separuh berikutnya, dan pemeriksaan installer tidak dapat melihat bagian kedua. Ada dua gejala umum ketika class tersedia tetapi tidak diregistrasikan:
- Shared props tidak tersedia.
auth,errors, dan data lain yang dibagikan aplikasi tidak pernah sampai ke frontend. - Submit form mengulang write request. Inertia membutuhkan
303setelah redirect dariPUT,PATCH, atauDELETE; tanpa middleware, response tetap302biasa dan browser mengulang method awal ke target redirect. Route edit panel menggunakanPUT, sehingga gejalanya terlihat seperti update yang berulang atau looping.
Props milik panel tidak berada di middleware aplikasi Anda
PandaPanel\Http\Middleware\SharePanelData membagikan semua data yang dibaca komponen panel:
| Prop | Digunakan oleh |
|---|---|
panel | shell |
navigation | sidebar |
panels | panel switcher pada header |
broadcasting | toast listener |
search | command palette |
notifications | notification bell |
tenancy | tenant switcher |
Data dibagikan melalui Inertia::share(), yang melakukan merge, sehingga HandleInertiaRequests milik aplikasi tidak disentuh dan auth, errors, serta data lainnya tetap tersedia. Data ini ditempatkan di package, bukan di middleware aplikasi, dengan alasan yang sama seperti komponen panel: prop baru pada versi berikutnya tidak seharusnya merusak setiap aplikasi hanya karena developer lupa menyalinnya secara manual.
Setiap value berupa closure, sehingga request yang tidak pernah masuk ke panel tidak membayar biaya komputasi untuk data tersebut.
Middleware ini diregistrasikan pada group web oleh service provider:
// config/panda-panel.php
'register_web_middleware' => true,2
Nonaktifkan hanya jika Anda ingin meregistrasikannya sendiri di bootstrap/app.php, dengan urutan berikut:
use PandaPanel\Http\Middleware\RedirectPanelHome;
use PandaPanel\Http\Middleware\ResetPanelContext;
use PandaPanel\Http\Middleware\ShareFlashToast;
use PandaPanel\Http\Middleware\SharePanelData;
$middleware->web(append: [
ResetPanelContext::class,
RedirectPanelHome::class,
ShareFlashToast::class,
SharePanelData::class,
]);2
3
4
5
6
7
8
9
10
11
Middleware tersebut ditambahkan melalui kernel setelah kernel selesai di-resolve, bukan langsung ke router. bootstrap/app.php mengonfigurasi group web melalui hook afterResolving pada kernel, yang kemudian dapat menimpa state yang sebelumnya berada di router — package yang langsung mendorong middleware ke router berisiko kehilangan middleware tersebut secara diam-diam selama proses boot.
HTTP 200 tetapi menggunakan shell yang salah
Ini adalah jenis kegagalan berbeda dengan akar masalah yang sama — boundary antara panel dan entry Inertia milik aplikasi.
Setiap halaman panel mendeklarasikan layout-nya sendiri:
defineOptions({ layout: PanelLayout }); // and PanelBlankLayout for the auth pagesAssignment tanpa fallback di resources/js/app.ts menggantinya setelah halaman menetapkan layout:
page.default.layout = AppLayout; // wrong — panel renders inside your shell
page.default.layout ??= AppLayout; // right
page.default.layout ||= AppLayout; // also right2
3
use PandaPanel\Support\Installer\FrontendRequirements;
FrontendRequirements::layoutOverrides();
// [['file' => 'resources/js/app.ts', 'line' => 12, 'code' => 'page.default.layout = AppLayout;']]2
3
4
| Method | Signature | Mengembalikan |
|---|---|---|
layoutOverrides | static layoutOverrides(): array | list<array{file: string, line: int, code: string}> |
Method ini membaca resources/js/app.ts, app.js, ssr.ts, dan ssr.js, lalu melaporkan setiap baris yang melakukan assignment ke .layout tanpa fallback. panel:install menampilkan file, nomor baris, dan bentuk penggantinya, karena boundary ini merupakan salah satu hal yang tidak dapat diperbaiki package dari dalam: hasilnya tetap HTTP 200, tetapi yang muncul adalah sidebar aplikasi, bukan navigasi panel, tanpa error di log.
Resolve komponen halaman panel
Halaman panel merupakan nama komponen Inertia yang di-resolve oleh resolver milik aplikasi. Ada tiga lokasi, dan pemisahan ini diperlukan karena @inertiajs/vite hanya melakukan glob terhadap resources/js/pages/**:
| Lokasi | Peran | Dapat di-resolve Inertia |
|---|---|---|
resources/js/panel/** | layout, komponen, renderer, composable, registry, dan type | tidak |
resources/js/pages/panel/** | halaman bawaan framework | ya |
resources/js/pages/Panels/{Panel}/** | halaman panel dan custom widget milik aplikasi | ya |
Error Page not found: panel/resources/List di browser berarti halaman hasil publish tidak berada di bawah resources/js/pages, atau build belum dijalankan ulang sejak file tersebut dipublish:
php artisan vendor:publish --tag=panda-panel-assets
npm run build2
Catatan
- Hanya Inertia 3.
inertiajs/inertia-laravel2.x tidak didukung: form panel menggunakan komponen<Form>milik Inertia 3 dan event routerflash, dan tidak tersedia shim untuk keduanya. - Aplikasi yang hanya menggunakan Blade tidak dapat menjadi host panel. Ini bukan opsi konfigurasi yang dapat diaktifkan — seluruh layar panel merupakan response Inertia.
missingInertia()memeriksa file, bukan perilaku runtime. Root view yang diganti nama dapat dilaporkan tidak tersedia padahal tetap bekerja; middleware yang tersedia tetapi tidak diregistrasikan dilaporkan tersedia padahal tidak bekerja.SharePanelDataberjalan pada seluruh groupweb, bukan hanya route group panel. Redirect keluar dari panel tetap membutuhkanShareFlashToast, sementaraResetPanelContextharus berjalan pada request yang bahkan tidak pernah mencapai panel — itulah alasan middleware ini berada di groupweb.ResetPanelContextmenjaga Octane tetap aman. Current panel, parent record, dan tenant hidup di scoped container binding yang dibersihkan pada awal setiap request.- SSR bukan sesuatu yang dikonfigurasi package.
layoutOverrides()membacassr.tskarena kesalahan yang sama bisa terjadi di sana, tetapi panel tidak mewajibkan maupun melarang SSR. @routesadalah directive milik Ziggy, bukan Wayfinder. Panel tidak menggunakan keduanya untuk URL internal panel — setiap href panel berasal dari server — tetapi halaman milik starter kit mungkin tetap membutuhkannya.