Laravel Filament: Building custom table columns

In this short post, we’ll walk through creating a simple custom table column class for Filament. If you don’t need explanations, jump to the final code.

Here’s a visual of what we will be building:

This screenshot is from a cinema management and ticketing system called Kinola. The table fragment shows sales results for each screening of a film. Note that the designer has decided that showing the little gray column labels inside each column would be pretty – and this little detail is exactly what warrants creating a custom table column.

We could implement these labels using the formatStateUsing() function to add some HTML to the value of the table cell. However, if you need to use this formatting in multiple table cells, you’ll end up with lots of duplicate code. In this case, a custom class is a cleaner solution.

Let’s start from the end. As the result, we’ll want to have a LabeledColumn class that can be used like this in a Filament Resource:

PHP
public static function table(Table $table): Table
{
    return $table
        ->columns([
            LabeledColumn::make('box_office')
                ->setInnerLabel("Box office")
                ->money('EUR'),
            LabeledColumn::make('total_sold_tickets')
                ->setInnerLabel("Sold"),
            LabeledColumn::make('available_seats')
                ->setInnerLabel("Available"),
        ]);
}

The custom column

First, let’s create our custom table column class.

PHP
use Filament\Tables\Columns\TextColumn;

class LabeledColumn extends TextColumn
{
    protected string $view = 'partials.columns.labeled-column';
}

Any custom table column needs to extend Filament\Tables\Columns\Column or one of its children. In this case, we’re extending TextColumn so that we could use its money() method later.

The $view property defines the location of the blade template which will be used when rendering the column. Using a custom view isn’t required, but in this case we’ll need it – the label requires some custom HTML.

Now, let’s create the view itself in views/partials/columns/labeled-column.blade.php and display the data.

PHP
<div class="text-right">
    <div class="whitespace-nowrap">{{ $getState() }}</div>
</div>

The $getState() function comes from the base Column class we extended earlier and it simply returns the column’s content. (If the syntax is unfamiliar to you, it’s because this is Livewire’s magic. Just know that you can call any public method of the column class by prefixing it with $.)

Let’s add the functionality required for storing and displaying the label.

PHP
use Filament\Tables\Columns\Column;

class LabeledColumn extends Column
{
    protected string $view = 'partials.columns.labeled-column';
    
    protected string $innerLabel;
    
    public function setInnerLabel(string $innerLabel): static
    {
        $this->innerLabel = $innerLabel;

        return $this;
    }

    public function getInnerLabel(): string
    {
        return $this->innerLabel;
    }
}

These are just your regular old getter and setter for a protected variable, but note that the setter returns $this – this is called a fluent interface and it allows us to chain function calls for better readability. Filament uses this approach everywhere.

And now we can easily display the label in our custom view:

PHP
<div class="text-right">
    <div class="text-sm text-neutral-400 whitespace-nowrap">{{ $getInnerLabel() }}</div>
    <div class="whitespace-nowrap">{{ $getState() }}</div>
</div>

Final code

use Filament\Tables\Columns\Column;

class LabeledColumn extends Column
{
    protected string $view = 'partials.columns.labeled-column';
    
    protected string $innerLabel;
    
    public function setInnerLabel(string $innerLabel): static
    {
        $this->innerLabel = $innerLabel;

        return $this;
    }

    public function getInnerLabel(): string
    {
        return $this->innerLabel;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *