earmsby's avatar

adding icon column to table widget

I have a widget with this code:

I want to add an icon column to the table. The icon column is not tied to a field in the data. Instead if the step_date is more than 6 weeks in the past, the icon should be Heroicon::ExclamationCircle, otherwise, it should be Heroicon::Check.

When I attempted to do something like:

IconColumn::make('Status Icon')
                    ->label('Status')
                    ->icon(function ($record) {
							ray($record);
						}

I don't get anything in ray, so it seems like it's just skipping the whole ->icon() method. I do get the column label.

I must be doing something wrong, but can't seem to crack it.

0 likes
3 replies
LaryAI's avatar
Level 58

Certainly! Let’s solve your question about adding an icon column that checks if a record's step_date is older than 6 weeks, showing a warning or check icon accordingly.

Why isn't icon() working?

From your description, it seems you’re returning raw PHP arrays as your table data (return $widget_data;). Filament expects an Eloquent Collection or objects, not arrays, so $record is just a plain array in your case—not an Eloquent model. That’s why the icon() closures might feel "skipped" or the data isn't acting as you expect.

Solution

1. Convert your $widget_data items to objects

At the end of your records() callback, wrap $widget_data as a collect() of objects. For each entry, convert to a simple object:

->records(function (): \Illuminate\Support\Collection {
    $widget_data = [];
    // ... your logic populating $widget_data as arrays ...

    // sort as before
    $dates = array_column($widget_data, 'step_date');
    array_multisort($dates, SORT_ASC, $widget_data);

    // map arrays to objects
    return collect($widget_data)
        ->map(function ($item) {
            return (object) $item;
        });
})

Now, in your column callbacks, $record will be an object as expected.

2. Add the IconColumn

In your columns() array, add:

IconColumn::make('status_icon')
    ->label('Status')
    ->icon(function ($record) {
        // Ensure $record is an object; if not, convert as above
        $stepDate = \Carbon\Carbon::parse($record->step_date);

        if ($stepDate->lt(now()->subWeeks(6))) {
            return \Filament\Support\Icons\Heroicon::ExclamationCircle;
        }
        return \Filament\Support\Icons\Heroicon::Check;
    })
    ->color(function ($record) {
        $stepDate = \Carbon\Carbon::parse($record->step_date);

        if ($stepDate->lt(now()->subWeeks(6))) {
            return 'danger';
        }
        return 'success';
    })

Note: The icon() function must return a string of one of the Supported Heroicon constants.

3. Full Columns Example

->columns([
    IconColumn::make('status_icon')
        ->label('Status')
        ->icon(function ($record) {
            $stepDate = \Carbon\Carbon::parse($record->step_date);

            if ($stepDate->lt(now()->subWeeks(6))) {
                return \Filament\Support\Icons\Heroicon::ExclamationCircle;
            }
            return \Filament\Support\Icons\Heroicon::Check;
        }),
    TextColumn::make('catalog_number')
        ->label('Catalog Number'),
    TextColumn::make('last_step')
        ->label('Step'),
    TextColumn::make('step_date')
        ->label('Date'),
])

4. Don't forget to import Carbon:

use Carbon\Carbon;

Final code for records()

->records(function (): \Illuminate\Support\Collection {
    $widget_data = [];
    // ... your current logic ...

    $dates = array_column($widget_data, 'step_date');
    array_multisort($dates, SORT_ASC, $widget_data);

    // Convert arrays to objects
    return collect($widget_data)->map(fn($item) => (object) $item);
})

Now your icon column should work!
If you want to use Ray for debugging, try ray($record) inside your closure and you should now see the record output.


Let me know if you need any further help!

earmsby's avatar

When I add the code to convert $widget_date to objects:

// map arrays to objects
return collect($widget_data)
	->map(function ($item) {
     	return (object) $item;
    });

As given in step 1, I get this error:

Filament\Widgets\TableWidget::Filament\Tables\Concerns\{closure}(): Argument #1 ($record) must be of type Illuminate\Database\Eloquent\Model|array, stdClass given, called in C:\Users\earms\Herd\hildegard-dashboard-v2\vendor\laravel\framework\src\Illuminate\Collections\Arr.php on line 847

This is before I even attempt to add the iconColumn. I'm not sure I understand the error, but it looks like Filament doesn't like an object an array. What to try now?

earmsby's avatar

Ok, I found a solution.

First, I went back to returning the data as an array:

$dates = array_column($widget_data, 'step_date');
                array_multisort($dates, SORT_ASC, $widget_data);
                return $widget_data;

Then I added an element to my custom data for the number of weeks old each record was:

$step_date = Carbon::parse($pub_last_step[0]['StepDate']);
...
'weeks_old' => round($step_date->diffInWeeks(Carbon::now())),

Then in my iconcolumn, I used this:

IconColumn::make('weeks_old')
                    ->label('Age')
                    ->icon(function (array $record){
                        if($record['weeks_old'] >= 6){
                            return Heroicon::ExclamationTriangle;
                        }elseif ($record['weeks_old'] >= 4) {
                            return Heroicon::ExclamationCircle;
                        }else{
                            return Heroicon::CheckCircle;
                        }
                    })
                    ->color(function (array $record){
                        if($record['weeks_old'] >= 6){
                            return 'danger';
                        }elseif ($record['weeks_old'] >= 4) {
                            return 'warning';
                        }else{
                            return 'success';
                        }
                    }),

It seems like it was possible to stick with returning an array from the ->records() method. The key was to tell the closure to expect an array with ->icon(function (array $record){...]

It's working perfectly now. I might experiment with not adding the extra "weeks_old" array element and just calculate it directly in the iconcolumn. But for now, I'm going to leave it as is.

Please or to participate in this conversation.