Courses

Filament 3 From Scratch: Practical Course

Select Dropdown with/without belongsTo

Summary of this lesson:
- Implementing select dropdowns
- Creating radio button alternatives
- Managing belongsTo relationships in forms
- Handling relationship data in tables

In this lesson, let's review one of the most popular field types used in forms: dropdown selects. The data for the options may or may not come from another DB table as a relationship.

We will add two fields to our Products form:

  • select to choose product status: "in stock", "sold out", "coming soon"
  • select to choose product category: from another Category model

Simple Select Dropdown

Let's say that product may have one of the defined statuses, but we don't want to save them in a separate table.

Instead, we have an Enum field in the database:

Schema::table('products', function (Blueprint $table) {
$table->enum('status',
['in stock', 'sold out', 'coming soon'])
->default('in stock');
});

Let's make this field fillable in the Product Model.

app/Models/Product.php:

class Product extends Model
{
use HasFactory;
 
protected $fillable = ['name', 'price', 'status'];
}

And then, we can show the value in the table, just as a simple column:

app/Filament/Resources/ProductResource.php:

return $table
->columns([
Tables\Columns\TextColumn::make('name')
->sortable(),
Tables\Columns\TextColumn::make('price')
->sortable()
->money('usd')
->getStateUsing(function (Product $record): float {
return $record->price / 100;
}),
Tables\Columns\TextColumn::make('status'),
])

In the form, we add that field as a Select::make(), with options as an array of key-value pairs:

app/Filament/Resources/ProductResource.php:

return $form
->schema([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('price')->required(),
Forms\Components\Select::make('status')
->options([
'in stock' => 'in stock',
'sold out' => 'sold out',
'coming soon' => 'coming soon',
]),
]);

This is the result in the form:

And this is the result in the table:

Notice: of course, the repeating values of the options could be optimized and loaded from elsewhere: you could store them in the Model or create a PHP Enum class. But that's outside of the scope of this tutorial. My goal here is just to show you how Select fields work with Filament.


Radio Instead of Select?

Another visual option, if you have a set of values, is to use Radio and not Select.

For that, we don't change anything in the DB, and in the Filament Table, we just change one word in the Form definition:

app/Filament/Resources/ProductResource.php:

Forms\Components\Select::make('status')
Forms\Components\Radio::make('status')
->options([
'in stock' => 'in stock',
'sold out' => 'sold out',
'coming soon' => 'coming soon',
]),

And here's the visual result:

To me, personally, in most cases, the web user experience of a radio button is better than extra clicking on select to find out the possible option values.


Product Category: Select with belongsTo

Now, let's talk about relationships. What if you want to have Product Categories like this?

Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
 
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('price');
$table->foreignId('category_id')->nullable()->constrained();
});

For testing, I've added a few Categories behind the scenes:

And then, we define the relationship and add the fillable field:

app/Models/Product.php:

use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Product extends Model
{
// ...
 
protected $fillable = [
'name',
'price',
'status',
'category_id'
];
 
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
}

Now, how do we get the relationship in the Table and the Form?

In the Form, you can just provide a relationship() method in the chain with two parameters:

  • Relationship method name: in our case, the method in the Model is called category()
  • Relationship field name to be visible as a dropdown option value: in our case, name from categories.name DB column

app/Filament/Resources/ProductResource.php:

return $form
->schema([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('price')->required(),
Forms\Components\Radio::make('status')
->options([
'in stock' => 'in stock',
'sold out' => 'sold out',
'coming soon' => 'coming soon',
]),
Forms\Components\Select::make('category_id')
->relationship('category', 'name'),

And that's it. Here's the updated form:

The value of category_id will be successfully saved in the database:

Now we need to show it in the table.

To do that, we add another TextColumn, and all we need to do to load the data from the relationship is to use a dot-notation name of relationship.column.

app/Filament/Resources/ProductResource.php:

return $table
->columns([
// ... other columns
Tables\Columns\TextColumn::make('status'),
Tables\Columns\TextColumn::make('category.name'),
])

That's it. The values are shown in the table!

And before you ask about N+1 query and eager loading: don't worry. Filament takes care of it automatically. You don't need to specify Product::with('category') anywhere.

If we install Laravel Debugbar and take a look at the queries, everything is fine: one query for the products and one query for the categories:


So yeah, this is how simple it is to deal with the belongsTo relationship fields in the forms/tables of Filament.

Previous: Money Column: Modify Before/After Form
avatar

Hi great tutorial,

I am coming across an issue that I hope someone in the community could help with lets say for example I have the following

Users Organisations Teams

And I now want to create a User and I want to have a drop down of organisations I can pick, thats rather easy I can do that, however the issue im facing is lets say for example, there are specific Teams in a Organisation, how can I populate a drop down with those Teams and then multi select those teams.

I have created something like this

                Forms\Components\Section::make('Teams')->schema([
                    Forms\Components\CheckboxList::make('teams')
                        ->options(fn(Forms\Get $get): Collection => $get('organisation_id') ?
                            Team::whereHas('organisations', function ($subquery) use ($get) {
                                $subquery->where('id', $get('organisation_id'));
                            })->pluck('name', 'id') :
                            collect([]))
                ]),

Which seems to work however on the edit if I select the dropdown again of oganisations I would get an error, Ive tried looking most places however as we know you are the man to come to :D

Thanks in advance to anyone that could assist

avatar

This question is quite specific and individual, so I can't answer without reproducing the project, playing around and debugging.

Have you tried to ask on the official Fliament discord?

If that didn't help, you could maybe invite me to your GitHub (username PovilasKorop) and I could try to play around, but my time is extremely limited if I want to put out the course soon.

avatar

You say that you get an error but you don't say what error. Error message might say a lot. But yes, as Povilas said, try asking in the official Filament discord server.

avatar
You can use Markdown
avatar

Wow that is so insanly easy compaired to some data tables libraries I have used. Love it! Will see how it goes handling many to many relations later I assume :)

Thanks for the awesome tutorials as always.

avatar
You can use Markdown
avatar
You can use Markdown