Need some help to make Filament table filters dependant to each other
Hello. I've been struggling with this for some time. I have a Task model, that has following relations:
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
public function reviewer(): BelongsTo
{
return $this->belongsTo(User::class, 'reviewer_id');
}
For the current user, I am showing his tasks via this Livewire component:
<?php
declare(strict_types=1);
namespace App\Livewire\Task\User;
use App\Livewire\CommonComponent;
use App\Models\Task;
use App\Traits\TaskResourceTrait;
use Exception;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Concerns\ExposesTableToWidgets;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Title;
class ListTasks extends CommonComponent implements HasForms, HasTable
{
use ExposesTableToWidgets;
use InteractsWithForms;
use InteractsWithTable;
use TaskResourceTrait;
public Task $record;
public int $recordCount = 0;
public int $filteredCount = 0;
public Collection $tasksIds;
/**
* Cached tasks.
*/
protected ?Collection $cachedTasks = null;
public function mount($id = ''): void
{
$this->authorize('viewAny', Task::class);
// Cache the tasks once
$this->cachedTasks = $this->getCachedTasks();
// Set the task Ids based on the cached tasks
$this->tasksIds = $this->cachedTasks->pluck('id');
$this->totalRecordCount();
}
/**
* Retrieve the cached Tasks for the authenticated user.
*/
protected function getCachedTasks(): Collection
{
$cacheKey = 'tasks_' . auth()->id();
return Cache::remember($cacheKey, now()->addDays(), function () {
return auth()->user()->ownTasks;
});
}
/**
* Get the total record count
*/
public function totalRecordCount(): void
{
$this->recordCount = Task::query()
->withTrashed()
->whereIn('id', $this->tasksIds)
->count(); // Update the count
}
#[Computed]
public function getFilteredTaskCountProperty(): int
{
$query = Task::query()->whereIn('id', $this->tasksIds);
$this->applyFiltersToTableQuery($query);
return $query->count();
}
/**
* Get the view / contents that represent the component.
*/
#[Title('Tasks')]
public function render(): string|View
{
return view('livewire.task.user.list-tasks', ['title' => __('Tasks')]);
}
/**
* @throws Exception
*/
public function table(Table $table): Table
{
return self::tasksLivewireTable($table, $this)->query(Task::query()->whereIn('id', $this->tasksIds));
}
}
And the table and filters that come from the TaskResourceTrait
public static function tasksLivewireTable(Table $table, Component $livewireComponent): Table
{
return $table
->columns(self::taskBaseTableSchema('frontend'))
->filters(self::taskBaseTableFilters($livewireComponent))
->actions(
self::taskTableActions(
[
'view' => 'task.show',
'edit' => 'task.edit',
],
'frontend',
$livewireComponent,
),
)
->bulkActions(self::taskTableBulkActions())
->recordAction(null)
->recordUrl(null)
->groups([
TableGroup::make('statut')->collapsible(),
TableGroup::make('priority')->collapsible(),
TableGroup::make('project.name')->collapsible(),
]);
}
public static function taskBaseTableFilters(?Component $livewireComponent = null): array
{
return [
SelectFilter::make('project_id')
->label(__('Project'))
->options(function () use ($livewireComponent) {
if ($livewireComponent) {
return Project::query()
->orderBy('name')
->whereIn(
'id',
Task::query()
->whereIn('id', $livewireComponent->tasksIds)
->pluck('project_id'),
)
->pluck('name', 'id');
} else {
return Project::query()
->orderBy('name')
->pluck('name', 'id');
}
})
->multiple()
->preload(),
SelectFilter::make('statut')
->label(__('Status'))
->options(Statut::class)
->multiple()
->preload(),
SelectFilter::make('priority')
->label(__('Priority'))
->options(Priority::class)
->multiple()
->preload(),
SelectFilter::make('created_by')
->label(__('Created by'))
->options(function () use ($livewireComponent) {
if ($livewireComponent) {
$taskUsers = Task::query()
->whereIn('id', $livewireComponent->tasksIds) // Filter by the tasks currently shown
->pluck('created_by')
->unique();
return User::query()
->whereIn('id', $taskUsers)
->orderBy('last_name')
->get()
->pluck('full_name', 'id');
} else {
return User::query()
->orderBy('last_name')
->get()
->pluck('full_name', 'id');
}
})
->multiple()
->preload(),
SelectFilter::make('reviewer_id')
->label(__('Reviewer'))
->options(function () use ($livewireComponent) {
if ($livewireComponent) {
$taskUsers = Task::query()
->whereIn('id', $livewireComponent->tasksIds) // Filter by the tasks currently shown
->pluck('reviewer_id')
->unique();
return User::query()
->whereIn('id', $taskUsers)
->orderBy('last_name')
->get()
->pluck('full_name', 'id');
} else {
return User::query()
->orderBy('last_name')
->get()
->pluck('full_name', 'id');
}
})
->multiple()
->preload(),
TrashedFilter::make('trashed')->label(__('Deleted records')),
];
}
What I'd like to achieve is that when a filter is changed (an option or options are selected), the choices for the rest of the filters are reduced to show only the available pairs. For example, now, from a total of 300 tasks, if I choose a created_by, there are only 11 tasks and 2 reviewers for those tasks, but the reviewer filter still shows all the reviewers for the 300 tasks, not the current filtered 11.
P.S.: The reason for the if/else in the filters is that I am using the same trait to generate a table for the front end (no panel defined) and for the Admin panel. My goal is to make the filters dependent on the front end, but if the solution can be applied for both cases even better.
Please or to participate in this conversation.