Entries
Entry adalah satu informasi yang ditampilkan infolist tentang sebuah record: bagaimana entry mendeskripsikan dirinya ke frontend, dan bagaimana record diubah menjadi nilai yang dapat diserialisasi. Pilih tipe entry berdasarkan jenis nilai yang ingin ditampilkan, karena tipe tersebut menjadi discriminant yang dipakai renderer Vue untuk menentukan komponen yang dirender.
Semua tipe mewarisi PandaPanel\Infolists\Components\Entry, sehingga seluruh API pada halaman ini tersedia untuk semuanya. Opsi khusus tiap tipe dijelaskan di Entry reference.
Kumpulan entry minimal
use PandaPanel\Infolists\Components\BadgeEntry;
use PandaPanel\Infolists\Components\BooleanEntry;
use PandaPanel\Infolists\Components\DateTimeEntry;
use PandaPanel\Infolists\Components\TextEntry;
use PandaPanel\Infolists\InfolistSchema;
use PandaPanel\Tables\Enums\BadgeColor;
return $schema->columns(2)->schema([
TextEntry::make('reference'),
TextEntry::make('customer.name')->label('Customer'),
BadgeEntry::make('status')->colors([
'open' => BadgeColor::Info,
'done' => BadgeColor::Success,
]),
BooleanEntry::make('paid')->labels('Paid', 'Unpaid'),
DateTimeEntry::make('created_at')->label('Placed')->since(),
]);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
make() dan nama entry
public static function make(string $name): staticNama entry adalah attribute pada record dan dibaca menggunakan data_get(). Dot notation dapat menelusuri relation dan nested array, sehingga author.name cukup dideklarasikan sebagai entry tanpa harus menulis formatter:
TextEntry::make('author.name'); // $record->author->name
TextEntry::make('meta.plan'); // a key inside a JSON column2
Constructor-nya adalah final public function __construct(string $name), sehingga new TextEntry('name') juga valid. Contoh dokumentasi menggunakan make() karena bentuk ini lebih nyaman untuk chaining.
Entry yang membaca relation akan membacanya per record. Pada halaman view yang hanya menampilkan satu record, itu berarti satu query tambahan; pada table dengan puluhan row, kebutuhan yang sama biasanya harus ditangani dengan $with agar tidak menjadi N+1.
Label
public function label(string $label): static
public function getLabel(): string2
Default label berasal dari Str::headline() terhadap nama entry, dengan titik diubah menjadi spasi:
TextEntry::make('email_address')->getLabel(); // 'Email Address'
TextEntry::make('author.name')->getLabel(); // 'Author Name'
TextEntry::make('name')->label('Full name')->getLabel(); // 'Full name'2
3
API bersama
| Method | Signature | Default |
|---|---|---|
make() | static make(string $name): static | — |
getName() | getName(): string | Nama yang diberikan ke make() |
label() | label(string $label): static | Str::headline() dari nama |
getLabel() | getLabel(): string | — |
placeholder() | placeholder(string $placeholder): static | null — renderer menampilkan — |
helperText() | helperText(string $helperText): static | null |
columnSpan() | columnSpan(int $columnSpan): static | 1, minimum 1 |
columnSpanFull() | columnSpanFull(): static | Mengatur span menjadi 'full' |
formatUsing() | formatUsing(Closure $callback): static | null |
visible() | visible(Closure $callback): static | null — selalu visible |
isVisible() | isVisible(Model $record): bool | true |
action() | action(Action $action): static | null |
getAction() | getAction(): ?Action | — |
type() | type(): EntryType | Abstract; setiap class mengembalikan case-nya sendiri |
toValue() | toValue(Model $record): mixed | Abstract; nilai yang diserialisasi |
entries() | entries(): list<Entry> | [$this] |
toArray() | toArray(Model $record): ?array | Definisi entry, atau null jika hidden |
Membentuk nilai
formatUsing() berada di antara attribute yang berhasil di-resolve dan proses milik tipe entry itu sendiri:
public function formatUsing(Closure $callback): static
// Closure(mixed $value, Model $record): mixed2
use Illuminate\Database\Eloquent\Model;
use PandaPanel\Infolists\Components\TextEntry;
TextEntry::make('total')
->formatUsing(static fn (mixed $value, Model $record): string => number_format((float) $value, 2));2
3
4
5
Closure menerima attribute yang sudah di-resolve dan record, lalu mengembalikan nilai yang akan diproses tipe entry berikutnya — bukan HTML final. Apa yang dimaksud dengan "berikutnya" bergantung pada tipe entry:
| Tipe | Nilai yang sebaiknya dikembalikan formatUsing() |
|---|---|
TextEntry | String atau scalar yang ingin ditampilkan, sebelum limit() |
BadgeEntry | Label yang juga dipakai sebagai key lookup colors() |
IconEntry | Key yang dipakai icons() dan colors() |
BooleanEntry | Nilai apa pun; kemudian dicast dengan (bool) |
DateTimeEntry | DateTimeInterface atau string yang dapat diparse |
KeyValueEntry | Array; selain itu tidak ada pasangan yang dirender |
CodeEntry | Scalar atau struktur yang akan di-pretty-print |
RepeatableEntry | Item — model atau row |
ImageEntry, ColorEntry | String; keduanya memvalidasi nilainya sebelum digunakan |
Jadi badge yang warnanya bergantung pada relation cukup menggunakan format callback:
use PandaPanel\Infolists\Components\BadgeEntry;
use PandaPanel\Tables\Enums\BadgeColor;
BadgeEntry::make('role')
->formatUsing(static fn (mixed $value, Model $record): string => $record->is_admin
? 'Administrator'
: 'Member')
->colors(['Administrator' => BadgeColor::Info]);2
3
4
5
6
7
8
Closure dievaluasi di server. Hanya hasil akhirnya yang diserialisasi; closure tidak pernah dikirim ke frontend.
Nilai kosong dan placeholder
public function placeholder(string $placeholder): staticSebagian besar tipe mengembalikan null ketika nilai null atau string kosong. Renderer kemudian menampilkan placeholder, atau em dash jika placeholder tidak ditentukan. Area kosong terlihat seperti bug rendering, sehingga placeholder digunakan untuk membuat kondisi kosong tetap jelas:
TextEntry::make('email_verified_at')->placeholder('Never');
DateTimeEntry::make('last_used_at')->since()->placeholder('Never used');2
BooleanEntry adalah pengecualian: nilainya selalu dicast dan selalu menghasilkan boolean nyata. Karena itu placeholder tidak pernah dipakai pada tipe ini; gunakan labels() untuk menjelaskan kondisi true/false.
Helper text
public function helperText(string $helperText): staticTeks penjelas di bawah nilai dengan style muted:
TextEntry::make('api_key')->helperText('Rotate this if it has ever been shared.');Lebar
public function columnSpan(int $columnSpan): static // floored at 1
public function columnSpanFull(): static // the whole row2
Span dihitung terhadap jumlah kolom container tempat entry berada — root grid schema, section, grid, atau tab:
use PandaPanel\Infolists\Layouts\Section;
Section::make('Notes')->columns(3)->schema([
TextEntry::make('title'),
TextEntry::make('body')->prose()->columnSpanFull(),
]);2
3
4
5
6
'full' diserialisasi sebagai string 'full', bukan angka, karena lebar numeriknya berbeda di tiap breakpoint. Nilai ini dipetakan ke col-span-full, yaitu seluruh row berapa pun jumlah kolom grid pada viewport tersebut.
Span yang lebih lebar dari container akan di-clamp saat render, bukan overflow. Contohnya columnSpan(3) di dalam columns(4) menjadi dua kolom pada md ketika grid hanya selebar dua kolom, lalu menjadi tiga pada lg.
Visibility
public function visible(Closure $callback): static // Closure(Model): bool
public function isVisible(Model $record): bool2
Dievaluasi di server untuk setiap record. toArray() dari entry yang hidden mengembalikan null dan schema membuangnya, sehingga nilainya bukan sekadar disembunyikan oleh CSS — nilainya memang tidak pernah diserialisasi:
use Illuminate\Database\Eloquent\Model;
TextEntry::make('internal_note')
->visible(static fn (Model $record): bool => auth()->user()?->is_admin === true);2
3
4
Section, grid, atau tab yang akhirnya kosong karena semua child entry hidden tidak akan merender heading kosong. Lihat Layouts.
Tidak ada pasangan hidden() dan tidak ada visibleOn(): infolist tidak memiliki konsep "halaman lain tempat entry disembunyikan", karena infolist itu sendiri adalah halaman view.
Action di samping nilai
public function action(Action $action): static
public function getAction(): ?Action2
use PandaPanel\Actions\Action;
TextEntry::make('email')->action(
Action::make('verify')
->icon('check')
->requiresConfirmation()
->action(static fn (Model $record) => $record->markEmailAsVerified()),
);2
3
4
5
6
7
8
Action yang digunakan sama dengan Action pada table row atau page header dan diserialisasi terhadap record ini. Karena itu action yang tidak boleh dijalankan pengguna tidak dikirim, bukan ditampilkan lalu menghasilkan 403. Lihat Actions in infolists.
Tipe entry
| Class | type() | Nilai yang diserialisasi |
|---|---|---|
TextEntry | text | string|null |
BadgeEntry | badge | {label, color}|null |
BooleanEntry | boolean | bool |
DateTimeEntry | datetime | string|null |
KeyValueEntry | key-value | list<{key, value}> |
IconEntry | icon | {icon, color, label}|null |
ImageEntry | image | string|null |
ColorEntry | color | string|null |
CodeEntry | code | string|null |
RepeatableEntry | repeatable | list<{label, schema}> |
CustomEntry | custom | Apa pun yang dikembalikan state() atau attribute |
Nilai type() berasal dari case PandaPanel\Infolists\Enums\EntryType. Semua opsi tambahan milik tiap tipe dijelaskan di Entry reference.
Data yang dikirim ke frontend
toArray() mengembalikan null untuk entry yang hidden, dan untuk entry visible menghasilkan definisi:
[
'component' => 'entry',
'name' => 'email',
'label' => 'Email',
'type' => 'text',
'value' => 'grace@example.com',
'placeholder' => null,
'helperText' => null,
'columnSpan' => 1,
'action' => null,
// plus whatever the type adds — 'prose' here
]2
3
4
5
6
7
8
9
10
11
12
Hanya scalar, array, dan null yang melewati boundary ini. Model, query, atau closure tidak pernah muncul di payload — batas yang sama juga diterapkan table column.
Gotcha
formatUsing()berjalan sebelum tipe entry memproses nilainya. Mengembalikan markup final tidak membantu:TextEntrytetap meng-cast-nya menjadi string dan renderer akan melakukan escaping.- Entry tanpa attribute yang cocok bukan error.
data_get()mengembalikan null, tipe mengembalikan null, lalu placeholder ditampilkan. Karena itu typo pada nama entry terlihat sama persis dengan kolom yang memang kosong. toValue()bersifat public dan menerima record. Ini adalah cara termurah untuk menguji entry tanpa page atau request:expect(TextEntry::make('name')->toValue($record))->toBe('Grace Hopper').- Action bernilai null pada row repeatable. Wrapped row tidak memiliki key, sehingga action yang menunjuk kepadanya akan menamai record yang tidak dapat ditemukan endpoint.
Entry::toArray()memeriksa$record->existssebelum menyerialisasi action — model yang belum disimpan juga tidak menampilkan action. columnSpan()hanya memiliki floor, bukan clamp maksimum.columnSpan(0)menjadi 1;columnSpan(9)tetap 9 di payload lalu di-clamp terhadap lebar container saat render.- Tidak ada
sortable(),searchable(), atautoggleable(). Itu adalah konsep table column. Infolist hanya menampilkan satu record dan tidak memiliki sesuatu yang perlu diurutkan.