Skip to content

BelongsTo

A record of another model for a foreign key column, e.g. the customer of an invoice. The browser only ever sees UUIDs; the internal id is resolved on the server.

use Anvil\Schema\Fields\BelongsTo;
BelongsTo::make('customer_id')->label('Customer')->relationship(CustomerModel::class, 'name')->required(),
// Large tables: search while typing
BelongsTo::make('contact_id')->label('Contact')->relationship(ContactModel::class, 'name')->searchable(),
// Only some records
BelongsTo::make('manager_id')
->relationship(PersonModel::class, 'last_name', fn (PersonModel $people) => $people->where('exit_date', null)),
Option Effect Default
relationship(Model::class, 'title', $scope) the related model, the column shown as title, an optional closure that restricts the records required
searchable(true, 20) loads matching records while typing instead of all options; number of hits 1 to 100 all options
placeholder('…') label of the empty option, or placeholder of the search “—” / “Search…”

Plus the options of every field. A BelongsTo without relationship() throws a LogicException when it is used.

  • If the panel has a resource for the model: the records the user may see there – tenant, its scopeQuery(), the view permission and policies. Without the view permission the select is empty.
  • Without a resource: the tenant scoped records of the model.
  • A model without tenant scope needs a resource in the panel or an explicit scope; otherwise it would offer the records of all tenants, and Anvil throws a LogicException instead.
  • The closure of relationship() restricts the records further.

Options are ordered by the title. Relationship columns and SelectFilter of tables follow the same rules.

With searchable() the field is a combobox: typing searches the title (contains, case as the database collation; % and _ are matched literally), the focus shows the first records. Arrow keys move through the hits, Enter chooses, Escape closes; text that was not chosen is discarded when leaving the field, and clearing the field clears the value.

The search is an action of the component that renders the form, named lookup-<action> (e.g. lookup-save), and requires the permission of submitting that form: users who may not save the form cannot query the related table through it. It only returns records within the scope described above. It needs a form rendered with an endpoint – resource forms and action dialogs are; elsewhere all options are rendered. The select in the Attach dialog of relations is searchable.

The internal id of the chosen record, null when none is chosen.

Without search: in_list[…] of the UUIDs that may be chosen. With searchable(): valid_uuid, and when saving the UUID is looked up again within the scope – an unknown or foreign UUID fails with “The selected entry for … is not available.” (422).

The title of the related record. History entries show the title as well, never the id.

A string with format: uuid (the UUID of the related record, never its id). Requests send the UUID.