K

Formuláře

BelongsToSelect

Select nad vztahem `belongsTo`: možnosti z navázané tabulky, hledané na serveru a ukládané jako cizí klíč.

Na této stránce

BelongsToSelect je select pole vědomé si relace pro belongsTo asociace.

Základní použití

use NyonCode\WireForms\Components\BelongsToSelect;
 
BelongsToSelect::make('company_id')
->relationship('company', 'name')
->label('Company')
->searchable()

Možnosti resolvuje ze souvisejícího modelu, místo aby vyžadoval ruční pole options().

searchable() select bez preload() hledá v související tabulce na serveru, jak uživatel píše (párováním title atributu, omezeno na 50 výsledků), a resolvuje popisek vybrané hodnoty jedním klíčovaným lookupem — plný seznam možností se nikdy neposílá klientovi. Přidejte preload() pro načtení celého seznamu předem a filtrování na straně klienta. Explicitní callback getSearchResultsUsing() (zděděný ze Select) přepíše vestavěné hledání.

Běžné volby

BelongsToSelect::make('company_id')
->relationship('company', 'name')
->searchable()
->preload()
->required()
Metoda Účel
relationship('company', 'name') Resolvovat možnosti z relace a sloupce s názvem
searchable() Hledat v související tabulce na serveru, jak uživatel píše
preload() Načíst plný seznam možností okamžitě a filtrovat na straně klienta
modifyOptionsQueryUsing() Zúžit nebo seřadit dotaz souvisejícího modelu
createOptionForm() Zobrazit inline create formulář pro nový související záznam (automaticky vytvoří přes relaci)
createOptionUsing() Přizpůsobit, jak se nová možnost ukládá
editOptionForm() Editovat vybraný související záznam — automaticky naplní a zapíše zpět přes jeho relace
fillEditOptionUsing() / updateOptionUsing() Plná kontrola nad naplněním / uložením editu (každý dostává resolvnutý $record)

Zúžené možnosti

BelongsToSelect::make('company_id')
->relationship('company', 'name')
->modifyOptionsQueryUsing(fn ($query) => $query->where('active', true))

Inline create

use NyonCode\WireForms\Components\TextInput;
 
BelongsToSelect::make('company_id')
->relationship('company', 'name')
->createOptionForm([
TextInput::make('name')->required(),
])

Vytvoření na místě používá stejný průběh modalu jako Select: tlačítko „+ Create“ v panelu comboboxu otevře modal s formulářem a při uložení se nový záznam vytvoří na relaci (nebo přes createOptionUsing()), vybere a sloučí do rozbalovacího seznamu bez obnovení stránky. editOptionForm() ze Select zde funguje také.

S nastavenou relací createOptionForm() automaticky vytvoří související model i bez createOptionUsing() — introspektuje belongsTo relaci, najde související třídu a vytvoří záznam z dat modalu. Pole dostává rodičovský záznam formu od runtime, což je to, co umožňuje tohle (a každý write-back níže) bez jakéhokoli drátování.

Write-back přes relace

editOptionForm() jen nezobrazí modal — čte z a zapisuje do souvisejícího záznamu přes jeho vlastní relace. Co související model vlastní, se zapíše automaticky; co sdílí, se nechává na explicitním callbacku. Každý callback (fillEditOptionUsing, updateOptionUsing) dostává resolvnutý související $record, takže můžete číst i zapisovat přes libovolnou relaci bez znovu-načítání.

Automatické vyplnění

Edit modal se naplní z vybraného souvisejícího záznamu a vytáhne jen sloupce, které edit formulář deklaruje — nikdy id, timestampy ani nesouvisející sloupce:

BelongsToSelect::make('author_id')
->relationship('author', 'name')
->editOptionForm([
TextInput::make('name'),
TextInput::make('email'),
])

Tečkový název pole projde vnořenou relaci při fillu (prázdná relace → null):

TextInput::make('company.name') // naplní z $author->company->name

Matice

Při uložení rozhoduje o způsobu zápisu druh relace, na kterou tečkové nebo repeater pole cílí:

Relace Tvar pole Automatický write-back Explicitní alternativa přes updateOptionUsing($record)
belongsTo, morphTo tečkové origin.name jen fill (sdílený rodič) $record->origin?->update(...), associate(), dissociate()
hasOne, morphOne tečkové profile.bio updateOrCreate (vytvoří když chybí) $record->profile()->save($model)
hasMany, morphMany Repeater->relationship() sync: create / update / delete $record->books()->saveMany([...])
belongsToMany, morphToMany, morphedByMany Repeater->relationship() + pivot sloupce pivot sync: attach / detach / update syncWithoutDetaching(), toggle(), updateExistingPivot()
hasManyThrough, hasOneThrough tečkové / repeater jen fill (read-through) zapiš intermediate sám

Polymorfní varianty (morphOne, morphMany, morphTo, morphToMany, morphedByMany) se chovají přesně jako jejich nepolymorfní báze a nastaví polymorfní *_type/*_id samy.

Vlastněné 1:1 — hasOne / morphOne

Tečkové pole se zapíše zpět přes relaci, updatne se in-place nebo vytvoří když chybí:

BelongsToSelect::make('author_id')
->relationship('author', 'name')
->editOptionForm([
TextInput::make('name'),
TextInput::make('profile.bio'), // hasOne
TextInput::make('avatar.url'), // morphOne
])

Pro plnou kontrolu si ukládejte sami přes save() relace:

->updateOptionUsing(function ($record, array $data) {
$avatar = $record->avatar ?? new Avatar;
$avatar->url = data_get($data, 'avatar.url');
$record->avatar()->save($avatar); // u nového záznamu nastaví morph type
$record->update(['name' => $data['name']]);
})

Vlastněné 1:N — hasMany / morphMany

Edituj kolekci přes relationship-backed Repeater; write-back ji synchronizuje (create nové, update matchnuté, delete odebrané):

use NyonCode\WireForms\Components\Repeater;
 
BelongsToSelect::make('author_id')
->relationship('author', 'name')
->editOptionForm([
TextInput::make('name'),
Repeater::make('books')
->relationship('books')
->schema([TextInput::make('title')]),
])

Pro nedestruktivní zápis (create/update, nikdy delete) použijte saveMany() v callbacku:

->updateOptionUsing(function ($record, array $data) {
$books = collect($data['books'])->map(function (array $row) {
$book = ! empty($row['id']) ? Book::find($row['id']) : new Book;
$book->title = $row['title'];
return $book;
})->all();
$record->books()->saveMany($books);
})

Many-to-many s pivotem — belongsToMany / morphToMany / morphedByMany

Každý repeater item nese klíč souvisejícího modelu plus pivot sloupce. Fill přečte pivot z každého načteného řádku; write-back ho sync()ne (attach / detach / update pivot sloupců):

BelongsToSelect::make('author_id')
->relationship('author', 'name')
->editOptionForm([
TextInput::make('name'),
Repeater::make('tags')
->relationship('tags')
->schema([
TextInput::make('id'), // klíč souvisejícího tagu
TextInput::make('role'), // pivot sloupec (deklarovaný přes ->withPivot('role'))
]),
])

Funguje beze změny jak custom pivot model (->using(...), takže se aplikují jeho casty), tak ad-hoc pivot (jen ->withPivot(...), základní Pivot/MorphPivot), protože flow běží na úrovni relace. Pro jinou pivot strategii sáhni do callbacku:

->updateOptionUsing(function ($record, array $data) {
$sync = collect($data['tags'])
->mapWithKeys(fn ($row) => [$row['id'] => ['role' => $row['role']]])
->all();
 
$record->tags()->syncWithoutDetaching($sync); // attach/update, nikdy detach
// $record->tags()->toggle([$id1, $id2]); // flip členství uvedených id
// $record->tags()->updateExistingPivot($id, [...]); // jen pivot sloupce připojeného řádku
})

Sdílený rodič — belongsTo / morphTo

belongsTo/morphTo ukazuje na záznam, který související model nevlastní (mnoho autorů může sdílet jednu company), takže se nikdy nemění automaticky — fill ho načte k zobrazení a ty ho zapíšete explicitně:

BelongsToSelect::make('author_id')
->relationship('author', 'name')
->editOptionForm([
TextInput::make('name'),
TextInput::make('company.name'),
])
->updateOptionUsing(function ($record, array $data) {
$record->update(['name' => $data['name']]);
$record->company?->update(['name' => data_get($data, 'company.name')]);
})

Re-asociace je buď edit vlastního sloupce (foreign key je reálný sloupec, takže ho auto write-back zvládne) nebo idiomatické associate() / dissociate():

->updateOptionUsing(function ($record, array $data) {
$record->company()->associate(Company::find($data['company_id'])); // re-point
// $record->company()->dissociate(); // odpojit
$record->save();
})

U morphTo associate() re-pointuje na jiný typ (nastaví *_type i *_id); dissociate() vynuluje oba.

Read-through — hasManyThrough / hasOneThrough

Read-through relace nemá jednoznačný write-back (který intermediate rodič by vlastnil nový vzdálený řádek, nelze určit), takže fill ji stále načte k zobrazení, ale nikdy se nezapíše automaticky — žádné osiřelé ani smazané řádky. Zapiš ji explicitně přes intermediate v updateOptionUsing($record), když potřebujete.

Uvnitř Repeateru

Protože runtime propaguje rodičovský záznam k relačním polím všude, všechno výše funguje stejně i pro BelongsToSelect vnořený uvnitř položky Repeateru, pro každou položku zvlášť.

Související dokumentace