make:panel-resource
Menghasilkan sebuah Resource lengkap beserta class yang dibutuhkan agar langsung dapat bekerja: CRUD pages, table, dan form.
Gunakan command ini ketika sebuah Eloquent Model perlu ditampilkan dan dikelola sebagai CRUD screen di dalam Panel.
php artisan make:panel-resource User --panel=AdminOutput:
INFO Created [app/Panels/Admin/Resources/Users/UserResource.php]
INFO Created [app/Panels/Admin/Resources/Users/Pages/ListUsers.php]
INFO Created [app/Panels/Admin/Resources/Users/Pages/CreateUser.php]
INFO Created [app/Panels/Admin/Resources/Users/Pages/ViewUser.php]
INFO Created [app/Panels/Admin/Resources/Users/Pages/EditUser.php]
INFO Created [app/Panels/Admin/Resources/Users/Tables/UsersTable.php]
INFO Created [app/Panels/Admin/Resources/Users/Forms/UserForm.php]2
3
4
5
6
7
Tidak ada registration tambahan yang diperlukan. Path discoverResources() milik Panel sudah mencakup app/Panels/Admin/Resources, sehingga /admin/users tersedia pada request berikutnya.
Signature
make:panel-resource
{name : The resource name, for example User}
{--panel= : The panel it belongs to}
{--model= : The model class, defaults to App\Models\{name}}
{--simple : Generate only a list page, for modal-based editing}
{--no-view : Omit the view page}
{--soft-deletes : Reach trashed records, and add the trashed filter with restore and force-delete actions}
{--force}2
3
4
5
6
7
8
| Argument / option | Default | Efek |
|---|---|---|
name | required | Disingularisasi lalu diubah menjadi StudlyCase. users, User, dan user semuanya menghasilkan class User di directory Users. |
--panel= | required | Directory Panel tujuan. admin dan Admin sama-sama menjadi Admin. Jika tidak diberikan, command gagal. |
--model= | App\Models\{name} | Fully-qualified model class apa pun. |
--simple | off | Hanya membuat list page. Tidak membuat create, view, atau edit page, dan tidak membuat action yang mengarah ke page tersebut. |
--no-view | off | Tidak membuat view page dan menghapus ViewAction yang membuka page tersebut. |
--soft-deletes | off | Mendeklarasikan $softDeletes pada Resource dan membuat trashed filter beserta restore/force-delete action untuk row dan bulk. |
--force | off | Menimpa file yang sudah ada. |
php artisan make:panel-resource User --panel=Admin
php artisan make:panel-resource User --panel=Admin --simple
php artisan make:panel-resource User --panel=Admin --no-view
php artisan make:panel-resource User --panel=Admin --soft-deletes
php artisan make:panel-resource Account --panel=Admin --model="App\Models\User"2
3
4
5
Setiap flag benar-benar mengubah output. Generator tidak menerima option yang kemudian diabaikan karena itu akan memberi kesan feature didukung padahal tidak.
Struktur Directory yang Dibuat
app/Panels/Admin/Resources/Users/
UserResource.php Resource: model, navigation, pages()
Forms/UserForm.php Form schema di class terpisah
Tables/UsersTable.php Table schema di class terpisah
Pages/ListUsers.php
Pages/CreateUser.php
Pages/ViewUser.php
Pages/EditUser.php2
3
4
5
6
7
8
Directory menggunakan bentuk plural, sedangkan class menggunakan singular.
Table dan form ditempatkan pada class masing-masing karena keduanya cenderung berkembang. Dengan pemisahan ini, konfigurasi navigation pada Resource tetap mudah ditemukan walaupun form/table sudah menjadi panjang.
Resource
<?php
declare(strict_types=1);
namespace App\Panels\Admin\Resources\Users;
use App\Models\User;
use App\Panels\Admin\Resources\Users\Forms\UserForm;
use App\Panels\Admin\Resources\Users\Pages\CreateUser;
use App\Panels\Admin\Resources\Users\Pages\EditUser;
use App\Panels\Admin\Resources\Users\Pages\ListUsers;
use App\Panels\Admin\Resources\Users\Pages\ViewUser;
use App\Panels\Admin\Resources\Users\Tables\UsersTable;
use PandaPanel\Forms\FormSchema;
use PandaPanel\Resources\Resource;
use PandaPanel\Tables\TableSchema;
final class UserResource extends Resource
{
protected static string $model = User::class;
protected static ?string $navigationIcon = 'folder';
protected static int $navigationSort = 0;
public static function table(TableSchema $table): TableSchema
{
return UsersTable::configure($table);
}
public static function form(FormSchema $schema): FormSchema
{
return UserForm::configure($schema);
}
/**
* @return array<string, class-string>
*/
public static function pages(): array
{
return [
'index' => ListUsers::class,
'create' => CreateUser::class,
'view' => ViewUser::class,
'edit' => EditUser::class,
];
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Import block dirakit oleh command dan diurutkan. Ini penting karena Pint juga mengurutkan import dan generated code diuji agar lolos formatting project.
Key pada pages() adalah Route
Key pada pages() bukan label biasa. Setiap key memetakan ke route yang sudah ditentukan framework:
| Key | Base class | Route yang diregistrasikan |
|---|---|---|
index | PandaPanel\Resources\Pages\ListRecords | GET / |
create | PandaPanel\Resources\Pages\CreateRecord | GET create, POST create, POST create/step |
view | PandaPanel\Resources\Pages\ViewRecord | GET {record} |
edit | PandaPanel\Resources\Pages\EditRecord | GET {record}/edit, PUT {record}/edit, POST {record}/edit/step |
Generated Page sangat kecil karena behavior-nya berasal dari base class dan Resource:
<?php
declare(strict_types=1);
namespace App\Panels\Admin\Resources\Users\Pages;
use App\Panels\Admin\Resources\Users\UserResource;
use PandaPanel\Resources\Pages\ListRecords;
final class ListUsers extends ListRecords
{
protected static string $resource = UserResource::class;
}2
3
4
5
6
7
8
9
10
11
12
13
Table
<?php
declare(strict_types=1);
namespace App\Panels\Admin\Resources\Users\Tables;
use App\Panels\Admin\Resources\Users\UserResource;
use PandaPanel\Actions\DeleteAction;
use PandaPanel\Actions\DeleteBulkAction;
use PandaPanel\Actions\EditAction;
use PandaPanel\Actions\ViewAction;
use PandaPanel\Tables\Columns\DateTimeColumn;
use PandaPanel\Tables\Columns\TextColumn;
use PandaPanel\Tables\Enums\SortDirection;
use PandaPanel\Tables\TableSchema;
final class UsersTable
{
public static function configure(TableSchema $table): TableSchema
{
return $table
->columns([
TextColumn::make('id')
->label('ID')
->sortable()
->toggleable(false),
DateTimeColumn::make('created_at')
->label('Created')
->sortable(),
])
->filters([
//
])
->recordActions([
ViewAction::make(UserResource::class),
EditAction::make(UserResource::class),
DeleteAction::make(UserResource::class),
])
->bulkActions([
DeleteBulkAction::make(UserResource::class),
])
->defaultSort('created_at', SortDirection::Descending)
->emptyState(
heading: 'No records yet',
description: 'Records will appear here once they exist.',
);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Generator hanya membuat dua column:
id
created_at2
karena keduanya merupakan column yang secara umum tersedia.
Generator tidak membaca database schema. Jika generator menebak table berdasarkan schema saat ini, hasil tersebut bisa langsung menjadi salah setelah migration berikutnya.
Action yang dibuat mengikuti Page yang tersedia:
ViewActionhanya jika view page ada;EditActionhanya jika edit page ada.
Form
<?php
declare(strict_types=1);
namespace App\Panels\Admin\Resources\Users\Forms;
use PandaPanel\Forms\Components\TextInput;
use PandaPanel\Forms\FormSchema;
use PandaPanel\Forms\Layouts\Section;
final class UserForm
{
public static function configure(FormSchema $schema): FormSchema
{
return $schema
->columns(2)
->schema([
Section::make('Details')
->columns(2)
->schema([
TextInput::make('name')
->required()
->maxLength(255),
]),
]);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Generator memberikan satu field sebagai pola awal.
Jika model tidak memiliki column name, ubah field tersebut sebelum menggunakan create/edit agar data yang tersimpan sesuai schema Anda.
--simple
php artisan make:panel-resource User --panel=Admin --simpleHanya menghasilkan:
UserResource.php
Pages/ListUsers.php
Tables/UsersTable.php
Forms/UserForm.php2
3
4
pages():
public static function pages(): array
{
return [
'index' => ListUsers::class,
];
}2
3
4
5
6
Table hanya membawa:
DeleteAction
DeleteBulkAction2
karena tidak ada create/view/edit page yang dapat menjadi target action.
Form class tetap dibuat. Simple Resource memang menjadi dasar untuk modal-based editing, dan modal action menggunakan FormSchema yang sama.
--no-view
php artisan make:panel-resource User --panel=Admin --no-viewGenerator menghilangkan:
Pages/ViewUser.php;- key
'view'; ViewAction.
Record sub-navigation kemudian hanya memiliki satu tujuan. Satu link bukan navigation, sehingga tabs pada edit page juga menghilang.
Tidak ada --view karena view page dibuat secara default.
--soft-deletes
php artisan make:panel-resource User --panel=Admin --soft-deletesSatu flag mengubah tiga bagian yang memang harus bekerja bersama.
Resource:
final class UserResource extends Resource
{
protected static string $model = User::class;
protected static ?string $navigationIcon = 'folder';
protected static int $navigationSort = 0;
protected static bool $softDeletes = true;
// ...
}2
3
4
5
6
7
8
9
10
11
12
Table:
->filters([
TrashedFilter::make('trashed'),
])
->recordActions([
ViewAction::make(UserResource::class),
EditAction::make(UserResource::class),
DeleteAction::make(UserResource::class),
RestoreAction::make(UserResource::class),
ForceDeleteAction::make(UserResource::class),
])
->bulkActions([
DeleteBulkAction::make(UserResource::class),
RestoreBulkAction::make(UserResource::class),
ForceDeleteBulkAction::make(UserResource::class),
])2
3
4
5
6
7
8
9
10
11
12
13
14
15
Perannya berbeda:
$softDeletes = truemembuat record page dapat me-resolve trashed record;TrashedFiltermembuat deleted row dapat muncul pada table;- Restore/ForceDelete Action bekerja pada row tersebut.
Jika hanya salah satunya digenerate, Anda dapat berakhir dengan action yang tidak pernah bisa dijangkau.
Soft Delete behavior dinyatakan eksplisit, bukan dideteksi otomatis dari model. Model yang memakai SoftDeletes untuk alasan lain tidak otomatis membuat Panel menampilkan restore flow.
--model
php artisan make:panel-resource Account --panel=Admin --model="App\Models\User"Resource:
use App\Models\User;
final class AccountResource extends Resource
{
protected static string $model = User::class;2
3
4
5
Nama Resource berasal dari argument, sedangkan model berasal dari option.
Ini memungkinkan satu model memiliki beberapa Resource.
Contoh:
UserResource
→ untuk staff
AccountResource
→ untuk support2
3
4
5
keduanya dapat memakai:
App\Models\UserLeading backslash dibuang, sehingga:
\App\Models\User
App\Models\User2
dianggap sama.
Slug Default Berasal dari Model
Default Resource slug berasal dari model, bukan class Resource:
Str::of(class_basename($model))
->plural()
->kebab()2
3
Karena itu dua Resource yang menggunakan User sama-sama akan mencoba memakai:
usersDalam satu Panel, salah satunya harus memiliki slug berbeda:
protected static ?string $slug = 'accounts';Jika tidak, boot ditolak dengan:
PanelRegistrationException::duplicateResourceSlug()Custom Stubs
php artisan vendor:publish --tag=panda-panel-stubsEmpat stub digunakan command ini:
| Stub | Ditulis ke | Placeholder |
|---|---|---|
stubs/panel/resource.stub | {Plural}/{Class}Resource.php | panel, class, plural, model, modelBasename, imports, pageEntries, softDeletes |
stubs/panel/resource-page.stub | {Plural}/Pages/{PageClass}.php | panel, class, plural, pageClass, base |
stubs/panel/resource-table.stub | {Plural}/Tables/{Plural}Table.php | panel, class, plural, imports, filters, recordActions, bulkActions |
stubs/panel/resource-form.stub | {Plural}/Forms/{Class}Form.php | panel, class, plural |
Placeholder ditulis sebagai:
{{ name }}dengan spasi.
Exit Codes
| Hasil | Code |
|---|---|
| Minimal satu file dibuat | 0 |
| Semua file sudah ada dan dilewati | 1 |
--panel tidak diberikan | 1, dengan The --panel option is required. |
Run yang sebagian file-nya dilewati tetap exit 0 jika ada minimal satu file yang berhasil dibuat.
Hal yang Perlu Diperhatikan
- Tidak ada registration eksplisit. Resource ditemukan melalui discovery dan harus berada di path yang disebut
discoverResources(). - Jika directory dipindahkan keluar discovery path, Resource menghilang tanpa error.
- Cached manifest dapat menyembunyikan Resource baru sepenuhnya. Jalankan
php artisan panel:clear. - Nama disingularisasi sebelum StudlyCase.
Mediadapat menjadiMediumResourcekarenaStr::singular('Media')adalahMedium. Berikan singular yang memang Anda inginkan. - Generator tidak membaca database. Dua column dan satu form field hanyalah starting point.
- Model tidak diverifikasi keberadaannya saat generate. Typo pada
--modelbaru gagal saat request pertama mencoba memuat class tersebut. --simpletetap membuat Form class. Form digunakan modal create/edit.- Policy tidak digenerate. Authorization Resource tetap menggunakan policy model/application.
- Relation Manager dibuat dengan command terpisah. Gunakan
make:panel-relation-manager.