When building out resources for a Filament Admin panel the main resource classes can get rather long very quickly. When you have lots of data to model they can get unwieldily.
Initially, I set up my form()
fields with a primary area and a sidebar area, using the \Filament\Forms\Components\Grid
component. I split each grid column schema()
array to use new methods within the same resource class. This helped keep the components organised a little better.
<?php // UserResource.php
public static function form(Form $form): Form
{
return $form
->schema([
Grid::make(6)
->schema([
Grid::make()
->schema(self::primary())
->columnSpan(4),
Grid::make()
->schema(self::sidebar())
->columnSpan(2),
]),
]);
}
protected static function primary(): array
{
return [
// Primary components in here.
];
}
protected static function sidebar(): array
{
return [
// Sidebar components in here.
];
}
This helped separate the grid layout from the actual layout and form field components, but it still resulted in large resource classes. It also helped reduce the large indentation look of the arrays.
Separate Component Classes
To solve the length of resources classes, I moved each main section into separate classes. Each grid column (primary / sidebar) might have multiple layout components. Each of these could then contain multiple component configurations defined inside the schema()
for that layout component.
I created a new component class for each of the layout components I wanted to create. I started with extending the \Filament\Forms\Components\Section
, which generates a simple contained area. For each custom layout component, I added the appropriate form field components to the schema()
.
I defined some basic settings on how the layout component should render, such as how many columns()
should be used. I also inferred the layout component heading()
using the class name and some Laravel fluent string helpers.
<?php
namespace App\Filament\Components\User\Forms;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\TextInput;
use Illuminate\Support\Str;
class PersonalDetails extends Section
{
protected function setUp(): void
{
parent::setUp();
$this
->heading(
// Renders "Personal Details" as the heading.
Str::of(static::class)->classBasename()->headline()
)
->columns(2)
->schema([
TextInput::make('firstname')
->label('First Name')
->required(),
TextInput::make('lastname')
->label('Last Name')
->required(),
TextInput::make('email')
->email()
->required()
->columnSpanFull(),
]);
}
}
Common Components
As well as creating custom components for individual resources, generic custom components can be created. This allows their form field schemas can be reused across multiple resources. Many resources have created_at
and modified_at
timestamps and they might also have a status
flag. These can be grouped into a component and then integrated with multiple resources easily.
<?php
namespace App\Filament\Components\Forms;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Toggle;
use Illuminate\Support\Str;
class Status extends Section
{
protected function setUp(): void
{
parent::setUp();
$this
->heading(
Str::of(static::class)->classBasename()->headline()
)
->schema([
DatePicker::make('created_at')
->label('Created')
->inlineLabel()
->readOnly()
->disabled(),
DatePicker::make('modified_at')
->label('Modified')
->inlineLabel()
->readOnly()
->disabled(),
Toggle::make('active')
->inlineLabel()
->required(),
]);
}
}
Updating the Resource
In the resource, I then replaced the primary()
and sidebar()
schema calls with an array of the new custom components. Instead of three form field components for item details, five for an address, an upload, two date fields and a toggle – a total of twelve (12) or more component configurations, there are just four custom components.
I find this more concise and easier to understand when looking at the overall resource.
Now the resource class form()
method looks something like this.
<?php // UserResource.php
use App\Filament\Components\Forms\Status;
use App\Filament\Components\User\Forms;
public static function form(Form $form): Form
{
return $form
->schema([
Grid::make(6)
->schema([
Grid::make()
->schema([
Forms\PersonalDetails::make(),
Forms\Address::make(),
])
->columnSpan(4),
Grid::make()
->schema([
Forms\Avatar::make(),
Status::make(),
])
->columnSpan(2),
]),
]);
}