Advanced Usage of Select Fields in Filament: A Comprehensive Guide
Ali Mousavi
Senior Backend Developer | PHP, Laravel | 10+ Years Experience | Proficient in Vue.js & Nuxt.js
Filament's select fields offer powerful features beyond basic dropdowns. Let's explore advanced techniques to maximize their potential in your Laravel applications.
Basic Setup with Advanced Options
use Filament\Forms\Components\Select;
Select::make('status')
->options([
'draft' => 'Draft',
'published' => 'Published',
'archived' => 'Archived',
])
->searchable()
->preload()
->multiple()
->default('draft')
Dynamic Options with Relationships
Here's how to handle relationships elegantly:
Select::make('category_id')
->relationship('category', 'name')
->createOptionForm([
Forms\Components\TextInput::make('name')
->required(),
Forms\Components\TextInput::make('slug')
->required(),
])
->createOptionAction(function (CreateAction $action) {
$action->modalHeading('Create Category');
})
->searchable()
->preload()
->editOptionForm([
Forms\Components\TextInput::make('name')
->required(),
])
Custom Search Logic
Implement sophisticated search functionality:
Select::make('user_id')
->searchable()
->getSearchResultsUsing(function (string $search) {
return User::query()
->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%")
->limit(50)
->pluck('name', 'id')
->toArray();
})
->getOptionLabelUsing(fn ($value): ?string =>
User::find($value)?->name
)
Dependent/Chained Selects
Create cascading select fields:
Select::make('country_id')
->label('Country')
->options(Country::pluck('name', 'id'))
->reactive()
->afterStateUpdated(fn (callable $set) => $set('city_id', null)),
Select::make('city_id')
->label('City')
->options(function (callable $get) {
$country = Country::find($get('country_id'));
if (!$country) {
return [];
}
return $country->cities->pluck('name', 'id');
})
->disabled(fn (callable $get) => !$get('country_id'))
Asynchronous Loading
Optimize performance with async loading:
Select::make('products')
->multiple()
->searchable()
->getSearchResultsUsing(function (string $search) {
return Product::query()
->where('name', 'like', "%{$search}%")
->whereActive(true)
->limit(50)
->get()
->mapWithKeys(fn (Product $product) => [
$product->id => $this->formatProductOption($product)
])
->toArray();
})
->getOptionLabelUsing(function ($value) {
$product = Product::find($value);
return $this->formatProductOption($product);
})
->loadingMessage('Loading products...')
->noSearchResultsMessage('No products found.')
->searchingMessage('Searching products...')
Custom Option Formatting
Create rich select options with HTML:
Select::make('product_id')
->options(function () {
return Product::all()->mapWithKeys(function ($product) {
return [
$product->id => new HtmlString("
<div class='flex items-center gap-2'>
<div>{$product->name}</div>
<span class='text-gray-500'>({$product->sku})</span>
<span class='badge-{$product->stock_status}'>
{$product->stock_count}
</span>
</div>
")
];
});
})
->allowHtml()
Validation and Constraints
Implement complex validation rules:
领英推荐
Select::make('roles')
->multiple()
->minItems(1)
->maxItems(3)
->options(Role::pluck('name', 'id'))
->required()
->exists('roles', 'id')
->unique(ignorable: fn ($record) => $record)
->rules([
function () {
return function (string $attribute, $value, Closure $fail) {
if (in_array(1, $value) && count($value) > 1) {
$fail('Admin role cannot be combined with other roles.');
}
};
},
])
Event Handling
Handle select events effectively:
Select::make('permissions')
->multiple()
->options(Permission::pluck('name', 'id'))
->afterStateUpdated(function ($state, callable $set) {
// Handle after state update
if (in_array('admin', $state)) {
$set('is_admin', true);
}
})
->beforeStateDehydrated(function ($state) {
// Prepare data before saving
return array_unique($state);
})
Dynamic Option Groups
Organize options with groups:
Select::make('product_id')
->options(function () {
return Category::with('products')
->get()
->mapWithKeys(function ($category) {
return [
$category->name => $category->products
->pluck('name', 'id')
->toArray()
];
});
})
->optionsLimit(100)
->searchable()
->preload()
Advanced Loading States
Handle loading states gracefully:
Select::make('users')
->multiple()
->searchable()
->loadingMessage('Finding users...')
->searchingMessage('Searching...')
->noSearchResultsMessage('No users found.')
->placeholder('Select users')
->loadingState([
'html' => '<div class="spinner"></div>',
'class' => 'loading-state'
])
Best Practices and Tips
Select::make('status')
->options($this->getStatusOptions())
->preload()
2 . Use searchable for large datasets:
Select::make('user_id')
->searchable()
->preload(false)
3. Implement proper error handling:
Select::make('category_id')
->relationship('category', 'name')
->searchable()
->preload()
->exists()
->required()
->helperText(function ($state, $error) {
if ($error) {
return 'Please select a valid category';
}
return 'Select a category from the list';
})
These advanced techniques will help you build more sophisticated and user-friendly forms in your Filament applications. Remember to consider performance implications when implementing these features, especially with large datasets.