Files
filament/packages/forms/docs/03-fields/03-select.md
T
2022-11-01 22:19:11 +00:00

8.7 KiB

title
title
Select

The select component allows you to select from a list of predefined options:

use Filament\Forms\Components\Select;

Select::make('status')
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])

You may enable a search input to allow easier access to many options, using the searchable() method:

use Filament\Forms\Components\Select;

Select::make('authorId')
    ->label('Author')
    ->options(User::all()->pluck('name', 'id'))
    ->searchable()

If you have lots of options and want to populate them based on a database search or other external data source, you can use the getSearchResultsUsing() and getOptionLabelUsing() methods instead of options().

The getSearchResultsUsing() method accepts a callback that returns search results in $key => $value format.

The getOptionLabelUsing() method accepts a callback that transforms the selected option $value into a label.

Select::make('authorId')
    ->searchable()
    ->getSearchResultsUsing(fn (string $search) => User::where('name', 'like', "%{$search}%")->limit(50)->pluck('name', 'id'))
    ->getOptionLabelUsing(fn ($value): ?string => User::find($value)?->name),

You can prevent the placeholder from being selected using the disablePlaceholderSelection() method:

use Filament\Forms\Components\Select;

Select::make('status')
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])
    ->default('draft')
    ->disablePlaceholderSelection()

Multi-select

The multiple() method on the Select component allows you to select multiple values from the list of options:

use Filament\Forms\Components\Select;

Select::make('technologies')
    ->multiple()
    ->options([
        'tailwind' => 'Tailwind CSS',
        'alpine' => 'Alpine.js',
        'laravel' => 'Laravel',
        'livewire' => 'Laravel Livewire',
    ])

These options are returned in JSON format. If you're saving them using Eloquent, you should be sure to add an array cast to the model property:

use Illuminate\Database\Eloquent\Model;

class App extends Model
{
    protected $casts = [
        'technologies' => 'array',
    ];

    // ...
}

Instead of getOptionLabelUsing(), the getOptionLabelsUsing() method can be used to transform the selected options' $values into labels.

Dependant selects

Commonly, you may desire "dependant" select inputs, which populate their options based on the state of another.

Some of the techniques described in the advanced forms section are required to create dependant selects. These techniques can be applied across all form components for many dynamic customization possibilities.

Populating automatically from a relationship

You may employ the relationship() method of the Select to configure a BelongsTo relationship to automatically retrieve and save options from:

use Filament\Forms\Components\Select;

Select::make('authorId')
    ->relationship('author', 'name')

The multiple() method may be used in combination with relationship() to automatically populate from a BelongsToMany relationship:

use Filament\Forms\Components\Select;

Select::make('technologies')
    ->multiple()
    ->relationship('technologies', 'name')

To set this functionality up, you must also follow the instructions set out in the field relationships section. If you're using the admin panel, you can skip this step.

If you'd like to populate the options from the database when the page is loaded, instead of when the user searches, you can use the preload() method:

use Filament\Forms\Components\Select;

Select::make('authorId')
    ->relationship('author', 'name')
    ->preload()

You may customize the database query that retrieves options using the third parameter of the relationship() method:

use Filament\Forms\Components\Select;
use Illuminate\Database\Eloquent\Builder;

Select::make('authorId')
    ->relationship('author', 'name', fn (Builder $query) => $query->withTrashed())

If you'd like to customize the label of each option, maybe to be more descriptive, or to concatenate a first and last name, you should use a virtual column in your database migration:

$table->string('full_name')->virtualAs('concat(first_name, \' \', last_name)');
use Filament\Forms\Components\Select;

Select::make('authorId')
    ->relationship('author', 'full_name')

Alternatively, you can use the getOptionLabelFromRecordUsing() method to transform the selected option's Eloquent model into a label. But please note, this is much less performant than using a virtual column:

use Filament\Forms\Components\Select;
use Illuminate\Database\Eloquent\Model;

Select::make('authorId')
    ->relationship('author', 'first_name')
    ->getOptionLabelFromRecordUsing(fn (Model $record) => "{$record->first_name} {$record->last_name}")

Handling MorphTo relationships

MorphTo relationships are special, since they give the user the ability to select records from a range of different models. Because of this, we have a dedicated MorphToSelect component which is not actually a select field, rather 2 select fields inside a fieldset. The first select field allows you to select the type, and the second allows you to select the record of that type.

To use the MorphToSelect, you must pass types() into the component, which tell it how to render options for different types:

use Filament\Forms\Components\MorphToSelect;

MorphToSelect::make('commentable')
    ->types([
        MorphToSelect\Type::make(Product::class)->titleAttribute('name'),
        MorphToSelect\Type::make(Post::class)->titleAttribute('title'),
    ])

The titleAttribute() is used to extract the titles out of each product or post. You can choose to extract the option labels using getOptionLabelFromRecordUsing instead if you wish:

use Filament\Forms\Components\MorphToSelect;

MorphToSelect::make('commentable')
    ->types([
        MorphToSelect\Type::make(Product::class)
            ->getOptionLabelFromRecordUsing(fn (Product $record): string => "{$record->name} - {$record->slug}"),
        MorphToSelect\Type::make(Post::class)->titleAttribute('title'),
    ])

You may customize the database query that retrieves options using the modifyOptionsQueryUsing() method:

use Filament\Forms\Components\MorphToSelect;
use Illuminate\Database\Eloquent\Builder;

MorphToSelect::make('commentable')
    ->types([
        MorphToSelect\Type::make(Product::class)
            ->titleAttribute('name')
            ->modifyOptionsQueryUsing(fn (Builder $query) => $query->whereBelongsTo($this->team)),
        MorphToSelect\Type::make(Post::class)
            ->titleAttribute('title')
            ->modifyOptionsQueryUsing(fn (Builder $query) => $query->whereBelongsTo($this->team)),
    ])

Many of the same options in the select field are available for MorphToSelect, including searchable(), preload(), allowHtml(), and optionsLimit().

Creating new records

You may define a custom form that can be used to create a new record and attach it to the BelongsTo relationship:

use Filament\Forms\Components\Select;
use Illuminate\Database\Eloquent\Model;

Select::make('authorId')
    ->relationship('author', 'name')
    ->createOptionForm([
        Forms\Components\TextInput::make('name')
            ->required(),
        Forms\Components\TextInput::make('email')
            ->required()
            ->email(),
    ]),

The form opens in a modal, where the user can fill it with data. Upon form submission, the new record is selected by the field.

Since HTML does not support nested <form> elements, you must also render the modal outside the <form> in the view. If you're using the admin panel, this is included already:

<form wire:submit.prevent="submit">
    {{ $this->form }}

    <button type="submit">
        Submit
    </button>
</form>

{{ $this->formsModal }}