Key Value
PandaPanel\Forms\Components\KeyValue mengedit flat map berupa pasangan nama/value. Key dan value sama-sama free text, sehingga batas yang dijaga field ini adalah shape, bukan content: berapa banyak pasangan yang boleh ada, apakah row dapat ditambah atau dihapus, dan apakah key dapat diedit. Gunakan untuk metadata open-ended seperti attribute produk, SEO tags, atau settings blob. Jika daftar key sudah diketahui, gunakan field yang eksplisit daripada KeyValue.
Contoh minimal
use PandaPanel\Forms\Components\KeyValue;
use PandaPanel\Forms\FormSchema;
FormSchema::make()->schema([
KeyValue::make('meta')
->labels('Attribute', 'Value')
->columnSpanFull(),
]);2
3
4
5
6
7
8
Value berupa associative array, jadi cast attribute pada model:
protected function casts(): array
{
return ['meta' => 'array'];
}2
3
4
Method
public function labels(string $key, string $value): self // defaults: 'Key', 'Value'
public function maxPairs(int $max): self // default: null, clamped to >= 1
public function addable(bool $addable = true): self // default: true
public function deletable(bool $deletable = true): self // default: true
public function editableKeys(bool $editable = true): self // default: true2
3
4
5
| Method | Default | Dampak |
|---|---|---|
labels() | 'Key' / 'Value' | heading di atas dua input |
maxPairs() | null | menambahkan max:n pada rules dan menghentikan tombol Add setelah limit tercapai |
addable() | true | menampilkan tombol “Add row” |
deletable() | true | menampilkan tombol remove pada setiap row |
editableKeys() | true | jika false, key input di-disable |
use PandaPanel\Forms\Components\KeyValue;
KeyValue::make('meta')
->labels('Attribute', 'Value')
->maxPairs(20)
->helperText('Shown on the product page, in the order given.');2
3
4
5
6
Contoh settings dengan key tetap tetapi value dapat berubah:
use PandaPanel\Forms\Components\KeyValue;
KeyValue::make('limits')
->labels('Limit', 'Value')
->default(['requests_per_minute' => '60', 'burst' => '10'])
->editableKeys(false)
->addable(false)
->deletable(false);2
3
4
5
6
7
8
Validasi
use PandaPanel\Forms\Components\KeyValue;
use PandaPanel\Forms\FormSchema;
FormSchema::make()
->schema([KeyValue::make('meta')->maxPairs(10)])
->validationRules();
// ['meta' => ['nullable', 'array', 'max:10']]2
3
4
5
6
7
8
Itulah seluruh built-in rules. max: pada array menghitung jumlah entry, sehingga maxPairs(10) berarti maksimal sepuluh pasangan. Tidak ada per-value rule karena field tidak mendeklarasikan elementRules(), sehingga tidak ada rule yang dibuat pada meta.*.
Jika value membutuhkan constraint, tambahkan rule sendiri. Closure rule menerima seluruh map:
use Closure;
use PandaPanel\Forms\Components\KeyValue;
KeyValue::make('meta')->rules([
static function (string $attribute, mixed $value, Closure $fail): void {
foreach ((array) $value as $key => $entry) {
if (! is_string($entry) || mb_strlen($entry) > 255) {
$fail("The {$key} value must be a string of at most 255 characters.");
}
}
},
]);2
3
4
5
6
7
8
9
10
11
12
Hydration
protected function castForForm(mixed $value): arrayNormalization saat value masuk cukup ketat dan memang disengaja:
| Stored value | Hasil |
|---|---|
| array | setiap entry disimpan sebagai (string) $key => (string) $entry |
| JSON string | di-decode lebih dulu, lalu diproses dengan aturan yang sama |
| selain itu | [] |
| entry dengan key kosong | dibuang karena tidak dapat di-address dengan jelas |
| entry dengan value non-scalar | dibuang karena control hanya memiliki satu text input per value |
use PandaPanel\Forms\Components\KeyValue;
// $record->meta holds ['size' => 42, '' => 'orphan', 'tags' => ['a', 'b']]
KeyValue::make('meta')->formValue($record);
// ['size' => '42']2
3
4
5
6
Branch JSON string membuat field dapat digunakan pada column text yang menyimpan JSON tanpa cast, maupun attribute yang sudah di-cast ke array.
Behavior control
resources/js/panel/forms/fields/KeyValueField.vue mengedit ordered list of pairs tetapi submit sebagai map. Bentuk list diperlukan selama editing karena map tidak dapat memiliki dua entry dengan key yang sama. Saat user mengganti key, akan ada state sementara ketika key bentrok atau kosong; jika map dibangun ulang pada setiap keystroke, row dapat hilang ketika masih sedang diedit.
Konsekuensinya:
- row dengan key kosong tetap terlihat di UI tetapi tidak disubmit sampai key diisi;
- dua row dengan key sama akan menjadi satu saat submit, dan row terakhir menang;
- order row mengikuti object key order, yang dipertahankan JSON dan PHP array.
Data yang dikirim ke frontend
interface KeyValueFieldDefinition extends BaseFieldDefinition {
type: 'key_value';
keyLabel: string;
valueLabel: string;
maxPairs: number | null;
addable: boolean;
deletable: boolean;
editableKeys: boolean;
}2
3
4
5
6
7
8
9
Hal yang perlu diperhatikan
Value masuk ke control sebagai string, tetapi tidak ada cast balik khusus saat keluar. castForForm() mengubah setiap value menjadi string untuk control. Submitted value dari control bawaan memang berupa string, tetapi jika membutuhkan jaminan eksplisit, normalisasikan sendiri:
use Illuminate\Database\Eloquent\Model;
use PandaPanel\Forms\Components\KeyValue;
KeyValue::make('meta')->mutateUsing(
static fn (mixed $value, ?Model $record): array => array_map(
strval(...),
array_filter(is_array($value) ? $value : [], is_scalar(...)),
),
);2
3
4
5
6
7
8
9
editableKeys(false) adalah browser control, bukan server constraint. Key input memang disabled di browser, tetapi rule array tidak membatasi nama key. Crafted request tetap dapat mengirim key berbeda. Kunci key di server juga, misalnya melalui closure rule atau mutateUsing() yang membangun ulang map dari known list.
Nested structure tidak bertahan round trip. Stored value seperti ['tags' => ['a', 'b']] dibuang saat hydration, sehingga membuka lalu menyimpan record dapat menghapus structure tersebut. Gunakan Repeater untuk structured entries atau Code Editor dengan CodeLanguage::Json jika document shape harus dipertahankan.
required() pada empty map. Empty array gagal rule required, yang biasanya sesuai untuk requirement “minimal satu pair”. Kombinasikan dengan maxPairs() untuk upper bound. Tanpa required(), rules dimulai dengan nullable dan empty map dapat diterima.
Key selalu diperlakukan sebagai string di UI/wire. PHP dapat mengubah numeric string key menjadi integer dalam array. castForForm() mengubahnya kembali menjadi string untuk display dan JSON key juga berupa string. Jangan bergantung pada integer key tetap menjadi integer.
Lihat juga
- Repeater — list structured entries, bukan flat map
- Code Editor — JSON document yang dipertahankan sebagai teks
- Tags — list values tanpa key
- Validation
- State Lifecycle —
mutateUsing()danformatUsing() - Forms and Schemas