In this lesson, I will show you how to customize the behavior with the field value, both in the form and table, with an example of the Money field.
We have a products.price
DB field, which requires extra logic. According to all the DB theories, we shouldn't store monetary values as floats. Instead, we should use an integer field and store the value in cents.
It means that after someone enters 49.99
in the form, we need to automatically multiply it by 100 and save 4999
in the DB.
It also means that, from the other side, in the table, we need to take the 4999
value from the DB and automatically divide it by 100 to show 49.99
in the table column.
Let's see how to do both in Filament. Also, there's an option to do that on Laravel level, and I will also show it at the end of the lesson.
Mutate Value Before Creating
Filament has methods to modify the value between entering it in the form and saving it in the DB.
- Method
mutateFormDataBeforeCreate()
for Create forms - Method
mutateFormDataBeforeSave()
for Edit forms
They both accept the array of field values, and we can modify that array however we want and return it from the function.
app/Filament/Resources/Products/Pages/CreateProduct.php:
class CreateProduct extends CreateRecord{ // ... protected function mutateFormDataBeforeCreate(array $data): array { $data['price'] = $data['price'] * 100; return $data; }}
Let's fix that table column value.
Format Value in Table Column
We will add two modifier methods: dividing the value by 100 and formating the value with a currency.
For re-calculating the value from DB, you can use the ->formatStateUsing()
method, which acts similarly to the Eloquent Accessor.
For formatting the monetary value with separators, we can just add a method ->money()
to the column. Not only that, we can specify which currency to show, by default, it is USD.
app/Filament/Resources/Products/Tables/ProductsTable.php:
TextColumn::make('price') ->formatStateUsing(fn (int $state): float => $state / 100) ->sortable(),
Now, if I add a price of 12345.67
to a new product, it will look like this in the table:
Within formatStateUsing()
, you can format the state of the field however you need. But for the money field, there is a money()
function that accepts a currency—by default, USD—and a divide by number as a second parameter. So, in this example, we can replace formatStateUsing()
with the money()
function.
app/Filament/Resources/Products/Tables/ProductsTable.php:
TextColumn::make('price') ->formatStateUsing(fn (int $state): float => $state / 100) ->money('EUR', 100) ->sortable(),
Formatting with the money()
function, you even get a currency sign.
Edit Form: Modify Value Before/After
Now, if we load the Edit form, we will have the same problem: the value will come exactly as it is in the DB: 1234567
instead of 12345.67
.
And also, when saving, there won't be any modification either. So let's fix both with two methods:
app/Filament/Resources/Products/Pages/EditProduct.php:
class EditProduct extends EditRecord{ // ... protected function mutateFormDataBeforeFill(array $data): array { $data['price'] = $data['price'] / 100; return $data; } protected function mutateFormDataBeforeSave(array $data): array { $data['price'] = $data['price'] * 100; return $data; }}
As you can see, in the beforeFill()
, we divide by 100, and in the beforeSave()
, we multiply back. Again, this behavior is similar to Eloquent Accessors, just in Filament syntax.
Now, our Edit form looks/works correctly!
Even better, currency sign can be added to the input as a prefix.
app/Filament/Resources/Products/Schemas/ProductForm.php:
TextInput::make('price') ->prefix('€') ->required(),
The Laravel Way
I showed you how to modify the data in Filament tables/forms, but you may also want to make these modifications outside Filament, if your data also comes from other sources.
For that, instead of defining all those Filament methods, you may define Eloquent accessor/mutator as an Attribute in the Model:
app/Models/Product.php:
use Illuminate\Database\Eloquent\Casts\Attribute; class Product extends Model{ // ... public function price(): Attribute { return Attribute::make( get: fn (string $value) => $value / 100, set: fn (string $value) => $value * 100, ); }}
Then, you may remove all those Filament methods from above. The transformation happens whenever the value is saved/retrieved from the DB, and then Filament displays that value without any modifications.
No comments or questions yet...