Field Text
PandaPanel\Forms\Components\TextInput adalah control teks satu baris dan menjadi bentuk dasar yang banyak field lain variasikan. Gunakan untuk nama, judul, slug, alamat email, URL, atau value lain yang berupa satu baris karakter. Halaman ini juga membahas tiga field yang memiliki bentuk serupa dan tidak mempunyai halaman referensi tersendiri: Textarea, PasswordInput, dan HiddenInput.
Form minimal
<?php
declare(strict_types=1);
namespace App\Panels\Admin\Resources\Posts\Forms;
use PandaPanel\Forms\Components\TextInput;
use PandaPanel\Forms\FormSchema;
final class PostForm
{
public static function configure(FormSchema $schema): FormSchema
{
return $schema->schema([
TextInput::make('title')
->required()
->maxLength(255)
->placeholder('How we shipped it'),
]);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Deklarasi tersebut merender satu input, memvalidasi title sebagai required|string|max:255, lalu mempersist nilainya ke column title. Rendering, validation, dan persistence semuanya diturunkan dari deklarasi yang sama, sehingga ketiganya tidak dapat memiliki definisi yang saling bertentangan.
Apa yang dihasilkan field
TextInput::make('title')->required()->maxLength(255) diserialisasi menjadi data berikut, dan browser merender control berdasarkan payload tersebut:
| Key | Value | Sumber |
|---|---|---|
type | 'text' | FieldType::Text |
name | 'title' | make() |
label | 'Title' | Str::headline($name) kecuali label() dipanggil |
value | title milik record, di-cast menjadi string | formValue() |
inputType | 'text' atau 'email' | email() |
maxLength | 255 | maxLength() |
validation | { required: true, max: 255 } | subset rule yang dapat diperiksa browser |
maxLength sampai ke browser dalam dua bentuk: sebagai attribute maxlength agar user tidak dapat mengetik melewati batas melalui control biasa, dan sebagai validation hint. Keduanya bukan authority. Rule max:255 di server tetap menjadi pemeriksaan yang sebenarnya.
Method
make(string $name): static
Nama field sekaligus menjadi attribute pada model, key pada payload, dan key pada validation rules. Nama kosong akan melempar PandaPanel\Exceptions\PanelSchemaException.
use PandaPanel\Forms\Components\TextInput;
TextInput::make('slug');2
3
email(bool $email = true): static
Menambahkan rule Laravel email dan merender <input type="email">.
TextInput::make('email')
->label('Email address')
->email()
->required();2
3
4
Rule menjadi required|string|email|max:255. Browser juga menerima hint email dan memeriksanya dengan pattern yang sengaja lebih permisif daripada Laravel, sehingga frontend tidak menolak alamat yang sebenarnya akan diterima server.
maxLength(?int $length): static
Default 255. Menambahkan max:N dan membatasi attribute maxlength pada control. Kirim null untuk menghapus limit sepenuhnya.
TextInput::make('summary')->maxLength(500);
// No length rule at all, for a column that is genuinely unbounded.
TextInput::make('anything')->maxLength(null);2
3
4
Ini adalah field yang memiliki non-null default untuk panjang maksimal. String tanpa batas hampir selalu merupakan kesalahan ketika column database adalah varchar(255), sehingga default mengikuti limit column umum daripada membiarkannya unbounded.
minLength(?int $length): static
Default null. Menambahkan min:N.
TextInput::make('code')->minLength(6)->maxLength(6);Rule yang dihasilkan tipe field
Secara berurutan, validationRules() menghasilkan:
requiredataunullable, darirequired().string.email, jikaemail()dipanggil.max:N, jikamaxLengthbukan null.min:N, jikaminLengthbukan null.- Rule tambahan dari
rules()danrulesUsing().
Artinya hampir semua rule Laravel dapat ditambahkan melalui satu rules() call:
use Illuminate\Validation\Rule;
use Illuminate\Database\Eloquent\Model;
TextInput::make('website')
->rules(['url'])
->helperText('Include the scheme.');
TextInput::make('email')
->email()
->required()
// Unique on create, unique-except-self on edit. Without the ignore,
// saving a record without changing its email fails against itself.
->rulesUsing(static fn (?Model $record): array => [
$record === null
? Rule::unique('users', 'email')
: Rule::unique('users', 'email')->ignore($record->getKey()),
]);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
url dan numeric termasuk rule yang juga dapat direpresentasikan sebagai browser hints. unique tidak, dan memang tidak seharusnya: frontend yang mencoba menebak uniqueness tanpa database akan memberikan jawaban yang tidak dapat dipercaya.
Textarea
PandaPanel\Forms\Components\Textarea adalah field teks yang sama tetapi menggunakan beberapa baris.
use PandaPanel\Forms\Components\Textarea;
Textarea::make('bio')
->rows(6)
->maxLength(1000)
->columnSpanFull();2
3
4
5
6
| Method | Default | Dampak |
|---|---|---|
rows(int $rows): self | 4 | Tinggi control. Nilai di bawah 1 di-clamp menjadi 1. |
maxLength(?int $length): self | null | Menambahkan max:N. Berbeda dari TextInput, tidak ada limit panjang kecuali Anda mengaturnya. |
Type rules adalah string, ditambah max:N jika limit panjang ditentukan. Field type pada wire adalah textarea.
PasswordInput
PandaPanel\Forms\Components\PasswordInput dirancang agar password dapat bersifat optional pada edit tanpa pernah menimpa hash yang tersimpan dengan blank password.
use PandaPanel\Forms\Components\PasswordInput;
use PandaPanel\Forms\FormSchema;
public static function configure(FormSchema $schema): FormSchema
{
$isCreate = $schema->getPage() === 'create';
return $schema->schema([
PasswordInput::make('password')
->confirmed()
->rules(['min:8'])
// Required on create, optional on edit, and never written back
// as an empty string.
->when(
$isCreate,
static fn (PasswordInput $field): PasswordInput => $field->required(),
static fn (PasswordInput $field): PasswordInput => $field->optionalWhenFilled(),
),
]);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| Method | Default | Dampak |
|---|---|---|
confirmed(bool $confirmed = true): self | false | Menambahkan rule Laravel confirmed dan meminta renderer menampilkan input kedua berlabel Confirm {label} dengan label di-lowercase. |
revealable(bool $revealable = true): self | true | Diserialisasi sebagai revealable. Bundled renderer saat ini selalu menggambar tombol show/hide, sehingga flag ini sekarang terutama membawa intent untuk custom renderer. |
isConfirmed(): bool | — | Dibaca FormSchema, yang menambahkan rule {name}_confirmation. |
optionalWhenFilled(): self | — | Menjalankan required(false) ditambah dehydrateWhen() sehingga hanya non-empty string yang dipersist. |
Tiga konsekuensi penting:
password_confirmationbukan field yang Anda deklarasikan sendiri.confirmed()membuat renderer menggambar control kedua dan membuat schema menambahkan['nullable', 'string']untuk key tersebut, sehingga UI dan rule tidak drift.formValue()selalu mengembalikannull. Hash password yang sudah tersimpan tidak pernah dikirim kembali ke browser pada Page apa pun.- Field tidak melakukan hashing. Hashing adalah tanggung jawab model, dan Laravel sudah menyediakan cast untuk itu:
protected function casts(): array
{
return ['password' => 'hashed'];
}2
3
4
HiddenInput
PandaPanel\Forms\Components\HiddenInput membawa value melalui form tanpa menampilkannya. Class ini tidak menambahkan method sendiri.
use PandaPanel\Forms\Components\HiddenInput;
HiddenInput::make('source')->default('admin-panel');2
3
Hidden hanya merupakan keputusan rendering, bukan trust boundary. Value tetap datang dari browser dan tetap harus divalidasi dengan rules() yang Anda deklarasikan. Jika user tidak boleh mengatur value tersebut sama sekali, jangan taruh di hidden field. Inject value dari server melalui hook seperti beforeValidate() pada Page. Lihat lifecycle hooks.
Field::hidden() adalah konsep yang berbeda: method itu menghapus field dari Page. Lihat visibility.
Method yang diwarisi dari Field
Semua method berikut berada pada PandaPanel\Forms\Components\Field, sehingga berlaku untuk keempat field di halaman ini dan juga field type lainnya.
| Method | Default | Catatan |
|---|---|---|
label(string $label): static | Str::headline($name) | |
placeholder(string $placeholder): static | null | |
helperText(string $helperText): static | null | Dirender di bawah control. |
required(bool $required = true): static | false | Menambahkan required, atau nullable jika false. |
disabled(bool $disabled = true): static | false | Tetap dirender, tidak dapat diedit, dan tidak dipersist dari browser. |
default(mixed $default): static | null | Digunakan jika tidak ada record. |
columnSpan(int $span): static | 1 | Di-clamp minimal 1. |
columnSpanFull(): static | — | Menggunakan seluruh row terlepas dari jumlah column container. |
inlineLabel(bool $inline = true): static | false | Label ditempatkan di samping control, bukan di atasnya. |
rules(array $rules): static | [] | |
rulesUsing(Closure $callback): static | null | Menerima ?Model $record. |
live(bool $onBlur = false, ?int $debounce = null): static | off, 500 ms | Lihat live fields. |
visibleOn / hiddenOn / disabledOn(array $pages): static | — | Lihat visibility. |
visible / hidden(Closure|bool $condition = true): static | — | Dievaluasi satu kali di server. |
visibleWhen / hiddenWhen(string $field, ConditionOperator $operator, mixed $value): static | — | Dievaluasi ulang di browser. |
formatUsing / mutateUsing / dehydrateStateUsing(Closure $callback): static | — | Lihat hydration. |
dehydrated(Closure|bool $condition = true): static | melakukan dehydration | |
dehydrateTo(string $attribute): static | nama field | Persist ke column/attribute yang berbeda. |
dehydrateWhen(Closure $callback): static | — | Menentukan per submitted value apakah data dipersist. |
when / unless(...) | — | Dari Illuminate\Support\Traits\Conditionable. |
Hal yang perlu diperhatikan
maxLength()default ke 255 dan merupakan validation rule.TextInputyang terhubung ke columntexttetap akan menolak string di atas 255 sampai Anda memanggilmaxLength(null)atau menaikkan limit.minLength()danmaxLength()mengukur karakter, bukan nilai angka. Browser hint mengikuti Laravel:min/maxmengukur panjang string, value number, atau jumlah item collection.TextInputselalu merupakan string. Untuk angka gunakanNumberInput.email()mengubah input type sekaligus validation rule. Keyboard mobile juga berubah, dan itu memang tujuannya. Jika hanya membutuhkan rule tanpa mengubah input type, gunakan->rules(['email']).default()hanya berlaku ketika tidak ada record. Pada edit Page value berasal daridata_get($record, $name), jadi default bukan fallback untuk nullable column. GunakanformatUsing()jika perlu fallback saat hydration.- Password tidak pernah di-round-trip.
$field->formValue($record)padaPasswordInputselalu menghasilkannull, bahkan untuk saved record. Ini disengaja. - Dua field tidak boleh memiliki nama yang sama.
FormSchemamelemparPanelSchemaException::duplicateFields()karena hanya satu rule dan satu value yang dapat bertahan; field lainnya akan berpotensi diisi lalu dibuang secara silent jika duplikasi dibiarkan.