Let's create a menu item and a table/form to manage products.
Every menu item in Filament is called a Resource, a visual representation of the Eloquent Model. Imagine we have this simple Model with two fields:
app/Models/Product.php:
class Product extends Model{ use HasFactory; protected $fillable = ['name', 'price'];}
Then we generate a Filament Resource for this model:
php artisan make:filament-resource Product
It generates a folder app/Filament/Resources/ProductResource
with these files:
And you know what? The menu item "Products" already works just like that! If we log in and refresh our dashboard, we see a new menu item on the left. And if we click it, we will see the table of three products I have pre-seeded in the DB.
Filament tries to list them but doesn't know which columns to show. This is what we will need to do next.
Let's take a look at the main file for the Filament Resource.
app/Filament/Resources/ProductResource.php:
class ProductResource extends Resource{ protected static ?string $model = Product::class; protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; public static function form(Form $form): Form { return $form ->schema([ // ]); } public static function table(Table $table): Table { return $table ->columns([ // ]) ->filters([ // ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]) ->defaultSort('price', 'desc') ->emptyStateActions([ Tables\Actions\CreateAction::make(), ]); } public static function getRelations(): array { return [ // ]; } public static function getPages(): array { return [ 'index' => Pages\ListProducts::route('/'), 'create' => Pages\CreateProduct::route('/create'), 'edit' => Pages\EditProduct::route('/{record}/edit'), ]; }}
It sounds like a lot, but hey, just read this file: it's all understandable in plain English language.
You can configure how your CRUD works by changing the values of arrays or class properties.
For basic needs, it's enough to work with form()
and table()
methods and fill them with values.
Remember that under the hood, Filament is not a single package but a set of packages. So, "Table Builder" and "Form Builder" can also be used separately, outside of Filament Resource, or even in non-Filament projects if you use Livewire. Even in the documentation, they are separated into Form Builder and Table Builder sections.
For now, let's focus on the table()
method and the array of columns
. To show columns in the table, we just need to add the column objects to that array.
Filament has a lot of different column types, each is described with the method XxxxxColumn::make('[column_name]')
.
use Filament\Tables;use Filament\Tables\Table; // ... public static function table(Table $table): Table{ return $table ->columns([ Tables\Columns\TextColumn::make('name'), Tables\Columns\TextColumn::make('price'), ]) // ... other methods}
Just add those two lines, and refresh the table:
See? It's that easy to add columns. You can browse the docs and see what other columns they have. We will add some later during the course. For now, I just want you to understand the principle.
Now, see that button on the top-right of the table in the screenshot above? Yeah, that already shows the form to add a product!
But, similarly to the table, Filament doesn't know which input fields to show in the form. Let's specify exactly that in the form()
method.
The tables operate with XxxxxColumn::make()
, and the forms follow the same syntax of Xxxxx::make()
.
So, let's add two simple TextInput
fields:
use Filament\Forms;use Filament\Forms\Form; // ... public static function form(Form $form): Form{ return $form ->schema([ Forms\Components\TextInput::make('name'), Forms\Components\TextInput::make('price'), ]);}
Refresh the page, and we have the fields!
And that's it: we can now create and edit the products, and both forms work.
Yes, exactly, we just specify the table columns and form fields, and Filament generates everything else for us.
Of course, we can add much more functionality to tables/forms, and this is exactly what we will explore for the rest of the course.
One thing that surprised me personally about Filament: after create/edit form submission, it doesn't redirect you back to the table.
Create form redirects you to the edit form of the new record, and the Edit form redirects you back to the Edit form with a success notification on the top-right:
But you can easily customize this behavior and override the redirect logic.
For that, we need to look at other files in the Filament Resource folder. In addition to the main ProductResource
file, there's a separate file for each page if you want to customize them:
In many cases, you will likely not touch them at all, but in our case, let's override the redirect by adding one identical method to both the CreateProduct
and EditProduct
pages.
app/Filament/Resources/ProductResource/Pages/CreateProduct.php:
class CreateProduct extends CreateRecord{ // ... protected function getRedirectUrl(): string { return $this->getResource()::getUrl('index'); } }
app/Filament/Resources/ProductResource/Pages/EditProduct.php:
class EditProduct extends EditRecord{ // ... protected function getRedirectUrl(): string { return $this->getResource()::getUrl('index'); } }
And now, our forms will redirect back to the table after submission.
Another interesting UX decision by the Filament team was to "hide" the Delete button from the table.
Personally, I'm used to having "Edit" and "Delete" near each other for every record in the table. But in Filament, if you want to delete the record, you have two options:
Option 1. Go to the Edit page and click the Delete button on top-right.
Option 2. Select checkboxes and choose Bulk Actions -> Delete Selected.
But, as in most Filament features, you can easily customize this.
Have you noticed the method actions()
in the Table description of the Resource?
By default, it contains only Edit action, but we can add Delete there, as well.
return $table ->columns([ // ... ]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make(), ])
And then we have a typical Delete button/link in the table, with the confirmation modal before deleting.
So yeah, that's all the basics you need to know about using Filament as a CRUD builder.
Your job is just to fill in the columns and form fields, and Filament will take care of the rest.
In the following lessons, we will explore many more customization options for various field types.