K

Formuláře

Wire Forms

Formulářový systém: schéma deklarované v PHP, navázané na Livewire hostitele nebo samostatné, a co se děje mezi odesláním a uloženým záznamem.

Wire Forms preview
Na této stránce

Samostatný systém formulářů pro Laravel Livewire. Funguje nezávisle nebo s Wire Table.

Potřebujete záznam zobrazit read-only místo editace? Viz Infolisty — stejné schéma a layout, jen se zobrazovacími entries místo vstupních polí.

Instalace

composer require nyoncode/wire-forms

Přidejte do Tailwind content cest:

export default {
content: [
// ...aktuální cesty aplikace
'./vendor/nyoncode/wire-core/resources/views/**/*.blade.php',
'./vendor/nyoncode/wire-forms/resources/views/**/*.blade.php',
]
}

Jak formuláře fungují

Definujte schéma Form na své Livewire komponentě, navažte ho na state path a vykreslete pomocí {{ $this->form }}.


Jeden formulář

use NyonCode\WireForms\Forms\Form;
use NyonCode\WireForms\Forms\WithForms;
use NyonCode\WireForms\Components\TextInput;
 
class CreateUser extends Component
{
use WithForms;
 
public ?array $data = [];
 
public function form(Form $form): Form
{
return $form
->statePath('data')
->model(User::class)
->schema([
TextInput::make('name')->required(),
TextInput::make('email')->email()->required(),
])
->successMessage('User created');
}
 
public function save(): void
{
$this->form->save();
}
}
<form wire:submit="save">
{{ $this->form }}
<button type="submit">Create</button>
</form>

Více formulářů

Metody končící na Form se automaticky detekují:

class UserSettings extends Component
{
use WithForms;
 
public array $profileData = [];
public array $passwordData = [];
 
public function profileForm(Form $form): Form
{
return $form
->statePath('profileData')
->model($this->user)
->schema([
TextInput::make('name')->required(),
TextInput::make('bio'),
]);
}
 
public function passwordForm(Form $form): Form
{
return $form
->statePath('passwordData')
->schema([
TextInput::make('current_password')->password()->required(),
TextInput::make('password')->password()->required()->rules(['confirmed']),
TextInput::make('password_confirmation')->password()->required(),
]);
}
 
public function saveProfile(): void
{
$this->profileForm->save();
}
 
public function savePassword(): void
{
$data = $this->passwordForm->validate();
$this->user->update(['password' => Hash::make($data['password'])]);
}
}
<form wire:submit="saveProfile">
{{ $this->profileForm }}
<button type="submit">Save Profile</button>
</form>
 
<form wire:submit="savePassword">
{{ $this->passwordForm }}
<button type="submit">Change Password</button>
</form>

Explicitní registrace formulářů

Alternativa k automatické detekci — vraťte názvy metod přesně tak, jak jsou definované:

protected function getForms(): array
{
return ['profileForm', 'passwordForm'];
}

Režimy modelu

// Create mód — Form::save() volá User::create($data)
$form->model(User::class);
 
// Edit mód — Form::save() volá $user->update($data)
$form->model($user);
 
// Žádný model — save() vyhodí chybu, ale validate() funguje
$form->model(null);

Introspekce:

$form->isCreating(); // true když je model řetězec třídy
$form->isEditing(); // true když je model instance
$form->getModel(); // instance modelu nebo null

Samostatné použití (bez Livewire)

Funguje pro server-side validaci a zpracování dat:

$form = Form::make()
->schema([
TextInput::make('name')->required()->maxLength(255),
TextInput::make('email')->email()->required(),
])
->state(['name' => 'John', 'email' => 'john@example.com']);
 
// Jen validace
$validated = $form->validate(); // při selhání vyhodí ValidationException
 
// Validace + uložení
$form->model(User::class)->save();

Reference Form API

Schéma a stav

->schema(array $components) // definice polí
->statePath(string $path) // název Livewire vlastnosti pro stav
->fill(array $data) // naplnit stav
->state(array $data) // alias pro fill()
->getState(): array // aktuální stav
->getValidationRules(): array // posbíraná pravidla
->validate(): array // zvalidovat a vrátit data

Model a uložení

->model(string|Model|null $model) // Eloquent model (třída pro create, instance pro edit)
->save(): mixed // celý životní cyklus uložení
->using(Closure $fn) // vlastní save callback (nahrazuje výchozí perzistenci)
->optimisticLock(?string $column = 'updated_at') // přeruší update, když se záznam změnil od fill

Ochrana proti souběžné editaci viz Životní cyklus ukládání → Optimistic locking.

Hooky životního cyklu uložení

->mutateDataBeforeSave(Closure $fn) // fn(array $data): array — transformovat data před perzistencí
->beforeSave(Closure $fn) // fn(array $data): void — běží před perzistencí
->afterSave(Closure $fn) // fn(Model|mixed $record): void — běží po perzistenci

Notifikace

->successMessage(string|Closure|null $msg) // vlastní text úspěšné notifikace; Closure dostane $record
->disableSuccessNotification() // žádná notifikace po uložení

Validace

->validationMessages(array $msgs) // vlastní validační zprávy

Stav

->disabled(bool $disabled = true) // udělat všechna pole read-only

Autorizace

->authorize(bool $usePolicy = true) // zapnout automatické resolvování policy modelu (create/update)
->authorizeUsing(?Closure $callback) // fn(User $user, $record = null): bool — vlastní auth kontrola
->canSave(): bool // zda aktuální uživatel smí uložit
->isReadOnly(): bool // true když autorizace zamítne uložení

Když je ->authorize() zapnuto, formulář se stane read-only (a skryje tlačítko uložit), pokud aktuální uživatel nemá create nebo update policy oprávnění na modelu.

Introspekce

->isCreating(): bool // model je řetězec třídy
->isEditing(): bool // model je instance
->getModel(): ?Model // aktuální instance modelu
->getFlatComponents(): array // všechny komponenty (ploše)

Rendering

->toHtml(): string // Blade výstup
(string) $form // __toString()
->nativeSubmit(bool $native = true) // vykreslit pole pro odeslání prohlížečem, ne pro Livewire
->submitsNatively(): bool // v jakém režimu pole jsou

Nativní odeslání

nativeSubmit() vykreslí pole pro odeslání formuláře samotným prohlížečem: každé nese name a value=old(…) místo wire:model a chyby se berou ze sdíleného bagu $errors. Sáhněte po něm tam, kde koncový bod není váš — přihlašovací obrazovka odesílající na routu Fortify je to, kvůli čemu vznikl.

Element <form> zůstává vám. Akce, @csrf i odesílací tlačítko jsou rozhodnutí stránky, takže objekt formuláře vykreslí pole a nic kolem nich:

<form method="POST" action="{{ route('login') }}">
@csrf
{{ $credentials }}
<button type="submit">{{ __('Sign in') }}</button>
</form>

Pole musí deklarovat, že to umí, implementací Contracts\SupportsNativeSubmit. Schéma obsahující pole, které to neumí, vyhodí při renderu FormConfigurationException a pojmenuje ho — pole navázané jen přes wire:model totiž nemá name, takže by za něj prohlížeč neodeslal nic a stránka by poslala prázdnou hodnotu, aniž by o tom kdekoli byla zmínka. Nekvalifikuje se nic, co potřebuje round trip uprostřed formuláře: live() a reaktivní pole, Select hledající na serveru, FileUpload, Repeater. Implementují to TextInput, Checkbox, Hidden a OtpInput — pole, ze kterých jsou složené odhlášené obrazovky.

Checkbox se váže jinak než input, a to záměrně: nese konstantní value="1" a odpovídá svou přítomností, protože nezaškrtnuté políčko neodešle vůbec žádný klíč. Zaškrtnutí se vrací z posledního odeslání, pokud nějaké bylo — samotné old() totiž nerozezná „odškrtnuto“ od „čerstvá stránka“, takže políčko zapnuté ve výchozím stavu by se po odškrtnutí tiše zaškrtlo znovu.

Režim se aplikuje na schéma, které se chystá vykreslit, až po doběhnutí form.configuring — takže pole přidané pluginem se přepne se zbytkem a to, které nativně odeslat nejde, je odmítnuté, ať bylo deklarované, nebo přidané. Viz ADR 0036.

Factory

Form::make() // statická factory přes container

Livewire vazba

->livewire(Component $component) // navázat na Livewire komponentu

Trait WithForms

Trait WithForms poskytuje:

  1. Automatickou detekci — skenuje metody končící na Form a registruje je
  2. Lazy resolvování — formuláře se staví až při prvním přístupu
  3. Cachování — instance formulářů jsou cachované po dobu requestu
  4. Magický přístup k vlastnosti$this->profileForm vyresolvuje formulář
class MyComponent extends Component
{
use WithForms;
 
// Přístup přes:
// $this->form → volá metodu form()
// $this->profileForm → volá metodu profileForm()
// $this->settingsForm → volá metodu settingsForm()
}

Typy polí

Vstupní pole

Layoutové komponenty

Layoutové a schema komponenty (Grid, Flex, Section, Fieldset, Tabs, Wizard, Callout, Empty State) žijí ve sdílené sekci Schema — stejný slovník používají formuláře, infolisty i modaly.

Zobrazovací komponenty

Postavte si vlastní

Sdílené API pole

Každé pole dědí:

->label(string|Closure $label)
->helperText(string|Closure $text)
->hint(string|Closure $hint)
->hintIcon(string $icon)
->required(bool|Closure $required = true)
->hidden(bool|Closure $hidden = true)
->visible(bool|Closure $visible = true)
->disabled(bool|Closure $disabled = true)
->size('sm'|'md'|'lg'|'xl')
->columnSpan(int|string $span) // šířka sloupce v gridu
->default(mixed $value) // výchozí hodnota (create mód / chybějící klíče)
->defaultOnNull(bool $condition = true) // doplnit výchozí hodnotu i pro null v edit módu
->extraAttributes(array $attrs) // HTML atributy
->live() // wire:model.live
->debounce(int $ms = 500) // přidá .debounce.{ms}ms k vazbě
->afterStateUpdated(Closure $callback) // reagovat na změny hodnoty (automaticky zapne live)
->rules(string|array $rules) // Laravel validační pravidla
->unique(?string $table, ?string $column, bool $ignoreRecord = true, ?Closure $modifyRuleUsing)
->validationMessages(array $messages) // vlastní validační zprávy
->formatStateUsing(Closure $fn) // fn ($state, $record) — uložená hodnota do stavu pole
->dehydrated(bool|Closure $condition = true) // false nechá hodnotu mimo záznam
->dehydrateStateUsing(Closure $fn) // fn ($state, $record) — hodnota na cestě ven

formatStateUsing() běží při plnění formuláře, dehydrateStateUsing() při ukládání, obojí až po vlastní transformaci typu pole. Co pole zapíše, popisuje Životní cyklus uložení, unique() pak Validace.

Closury visible(), hidden(), disabled() a afterStateUpdated() dostávají live state accessory ($get, $set, $state). Viz Reaktivní pole.

Úprava formuláře, který nevlastníte

Formulář z nainstalovaného modulu se staví uvnitř kódu, který aplikace nemá, takže tři plugin hooky kolem něj jsou ta cesta dovnitř — jeden na každou fázi a nejsou zaměnitelné:

Hook Kdy běží Sáhněte po něm, když chcete
form.configuring jednou, když se ze schématu stává config přidat nebo odebrat pole
form.filling když fill() naváže hodnoty změnit, s čím pole přijde
form.saving po validaci, před perzistencí změnit, co dorazí do záznamu
$manager->hook(Hook::FormConfiguring, function (FormConfiguringPayload $payload) {
$payload->schema = [...$payload->schema, TextInput::make('crm_id')];
 
return $payload;
}, for: 'users');

form.configuring je protějšek table.composing u tabulky: běží na jediném místě, kde se ze schématu stává config, a ten je memoizovaný — takže se spustí jednou na formulář, ne jednou na render. for: zúží callback na jeden formulář — registrovaný klíč resource, který stránka ukazuje, třída hostitelské komponenty, nebo model — a bez něj callback běží pro každý formulář v aplikaci.

Celý seznam je v Hooky, kde v pipeline sedí ty dva zbylé pak v Životní cyklus uložení.