laravel-livewire-tables icon indicating copy to clipboard operation
laravel-livewire-tables copied to clipboard

DRAFT: Allow filters to use mounted properties

Open belzaaron opened this issue 1 year ago • 0 comments

All Submissions:

  • [x] Have you followed the guidelines in our Contributing document?
  • [x] Have you checked to ensure there aren't other open Pull Requests for the same update/change?

New Feature Submissions:

  1. [x] Does your submission pass tests and did you add any new tests needed for your feature?
  2. [ ] Did you update all templates (if applicable)?
  3. [ ] Did you add the relevant documentation (if applicable)?
  4. Did you test locally to make sure your feature works as intended?

Changes to Core Features:

  • [x] Have you added an explanation of what your changes do and why you'd like us to include them?
  • [ ] Have you written new tests for your core changes, as applicable?
  • [x] Have you successfully ran tests with your changes locally?

Example

Consider an application has the notion of companies and these companies should be able to create their own tags for filtering some data model within their own data table on the application. We would probably implement something like this:

public function filters(): array
{
    return [
        MultiSelectFilter::make('Tags')
            ->options(
                $this->company->tags()
                              ->orderBy('name')
                              ->get()
                              ->keyBy('id')
                              ->map(fn($tag) => $tag->name)
                              ->toArray()
            ),
    ];
}

Assuming the company property is public and on the component declaration, we would currently receive: Typed property App\Http\Livewire\SomeCompanyTable::$company must not be accessed before initialization. And if the $company variable did not have a type declared: Call to a member function tags() on null. This is expected as the filtering is setup and called via the boot lifecycle hook.

Explanation

Currently the DataTableComponent implements the boot method available to use from Livewire\Component - within the boot method, a call to FilterHelpers::setFilterDefaults(); is made; which sets up the filters for the instance. Since the setup method is called within the "boot" lifecycle for Livewire components, the use of properties is limited/removed. Moving the setup method to the "mount" lifecycle maintains existing functionality and affords the use of instance properties within the filters method on the DataTable.

Resolution

My proposed changes will utilize the currently suggested extension architecture for Laravel Livewire (see https://github.com/livewire/livewire/blob/65d311c63d2d1092ad5891fd0c1c1bdbed272cee/src/Features/SupportComponentTraits.php), maintains the passing tests, and will allow the use of mounted properties within filters.

Current workaround

This section mainly acts as a space for my currently implemented solution to be documented for readers down the line that may need mounted properties within their own filters if this isn't merged. I've made a trait, FixesFilters:

<?php

namespace App\Concerns\Livewire\Tables;

trait FixesFilters
{
    public function boot(): void
    {
        $this->{$this->tableName} = [
            'sorts' => $this->{$this->tableName}['sorts'] ?? [],
            'filters' => $this->{$this->tableName}['filters'] ?? [],
            'columns' => $this->{$this->tableName}['columns'] ?? [],
        ];
    }

    public function mountFixesFilters()
    {
        $this->setFilterDefaults();
    }
}

And of course use it on my tables that require dynamic/mounted property access in filters, for reference this is using rappasoft/[email protected].

Why this is in draft / Caveats

I am opening this PR as I would like to have some help brainstorming the solution to page refreshes with state persistence. Moving to the mount lifecycle hook will remove filters upon a page refresh/hotlink click.

belzaaron avatar Aug 21 '22 06:08 belzaaron