Filament also offers convenient ways to group table data by columns and calculate the aggregated data like "sum" or "average" to show at the bottom.
To demonstrate that, I've created a new Model for Orders:
app/Models/Order.php:
class Order extends Model{ use HasFactory; protected $fillable = ['user_id', 'product_id', 'price']; public function user(): BelongsTo { return $this->belongsTo(User::class); } public function product(): BelongsTo { return $this->belongsTo(Product::class); }}
And seeded some data for the orders:
Then, we can generate a new Filament Resource to show the table of Orders:
php artisan make:filament-resource Order
The table has these values:
app/Filament/Resources/OrderResource.php:
class OrderResource extends Resource{ // ... public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('created_at') ->dateTime(), Tables\Columns\TextColumn::make('product.name'), Tables\Columns\TextColumn::make('user.name'), Tables\Columns\TextColumn::make('price') ->money('usd') ->getStateUsing(function (Order $record): float { return $record->price / 100; }) ]) ->defaultSort('created_at', 'desc') // ... other methods with default values }}
Here's how it looks now:
Grouping by Product Name
What if you want to divide this table by some condition? Let's try to group by product. Then, you just add a ->defaultGroup()
method.
return $table ->columns([ Tables\Columns\TextColumn::make('created_at') ->dateTime(), Tables\Columns\TextColumn::make('product.name'), Tables\Columns\TextColumn::make('user.name'), Tables\Columns\TextColumn::make('price') ->money('usd') ->getStateUsing(function (Order $record): float { return $record->price / 100; }) ]) ->defaultSort('created_at', 'desc') ->defaultGroup('product.name')
Here's the result:
Cool, right?
Table/Group Summary: Total Order Price
Another powerful concept is to have a sum/average row at the bottom, to aggregate the data from the table.
In Filament, they are called Summarizers.
In each table column, we can add ->summarize()
to the chain and provide the rules for summarizing.
Let's try it without grouping first:
return $table ->columns([ // ... other columns Tables\Columns\TextColumn::make('price') ->money('usd') ->getStateUsing(function (Order $record): float { return $record->price / 100; }) ->summarize(Tables\Columns\Summarizers\Sum::make()), ]) // Commented out grouping, for now // ->defaultGroup('product.name')
Here's how it looks.
You probably have noticed that the sum is incorrect, as our DB contains price values in cents, so we need to divide it by 100. This is the syntax:
Tables\Columns\TextColumn::make('price') ->money('usd') ->getStateUsing(function (Order $record): float { return $record->price / 100; }) ->summarize(Tables\Columns\Summarizers\Sum::make() ->formatStateUsing(fn ($state) => '$' . number_format($state / 100, 2)) ),
Looks better now:
Now, if we combine the summarizers with the groups we saw above, Filament will show the summary for both the group and the table.
return $table ->columns([ // ... Tables\Columns\TextColumn::make('price') ->money('usd') ->getStateUsing(function (Order $record): float { return $record->price / 100; }) ->summarize(Tables\Columns\Summarizers\Sum::make() ->formatStateUsing(fn ($state) => '$' . number_format($state / 100, 2)) ), ]) ->defaultGroup('product.name')
Here's the result:
And also, it still shows the total sum at the bottom:
Interestingly, Filament optimizes SQL queries under the hood, so there's only one query for grouped summary and another for the total summary. There's no N+1 query problem.
If we take a look at the Debugbar, we see these two queries:
select sum(orders.price) as "PWpinat1Bw0cknFP", products.name as "PSfJ4eWj4cHYpN2f" from (select * from `orders`) as `orders` left join `products` on `orders`.`product_id` = `products`.`id` group by `products`.`name` select sum(orders.price) as "PWpinat1Bw0cknFP" from (select * from `orders`) as `orders`
Filament ships with four types of summarizers:
- Average
- Count
- Range
- Sum
You can read more about them in the official docs.
Very usefull and a nice new feature in filament v3
Great 👍
I got wrong valuesr with this code where it divides by 100 as my price value is stored as a decimal in the database. I just removed the 100 instead and it works ->summarize(Tables\Columns\Summarizers\Sum::make() ->formatStateUsing(fn ($state) => '$' . number_format($state , 2))),
Yes, if you store value as a decimal, you don't need to divide by 100. To be honest, I regret putting this integer-money logic in this course as it may confuse some people. But that's how I would build it in my projects.
is there a way to defaultSort on number. like 1,2,3,4,5 now i get 1,10,11,12,2
How do you sort and on what kind of field?
i already solve my problem. i make the in the db tabke a string instead integer. after i chance that it work just fine
Thank you for the wonderful lesson. I'm having fun building it. Now, regarding this summarize() method, when I run it, the calculation results and display are generally good, but the title of the total becomes as shown below.
It seems like I could use something like label(), but I don't know where to put it. What should I do? (This is a Japanese machine translation.)
I don't exactly understand what you get. Is it instead of text you get
filament-tables::table.summary.subheadings.group
? If so than I would guess not everything is translated to your language.@Nerijus thank you! When I changed 'locale' => 'ja', to 'locale' => 'en', in config/app.php, the display changed to "Product6 summary"! I will try to respond by referring to the subsequent Multi-language lessons. thank you very much!!
Check here https://github.com/filamentphp/filament#checking-for-missing-translations to see which keys are missing and make a pr to the filament
thank you! "php artisan filament:check-translations ja" When I tried it, I found a lot of "Missing".
I've never done PR, but if I can fill it, I'll give it a try!
Thank you for your detailed guide!
There is a way to export a table with the summaries? I didn't manage a way to do that. Nothing to fancy, just the sum of the columns.
Not sure about this one as I have not seen this asked in filament discord. So it is either pretty simple (with plugin or without) or it's pretty hard and nobody did it
Is order model a pivot table? i couldn't go through this. for filament the most important part are model and migration. pleace include those in the beginning
You can find the repository at our last lesson:
https://github.com/LaravelDaily/Filament3-Course-Main
Hope that helps!