Power-Components / livewire-powergrid

⚡ PowerGrid generates modern, powerful and easy-to-customize data tables using Laravel Livewire.
https://livewire-powergrid.com
MIT License
1.48k stars 217 forks source link

WithMultiSelectBuilder doesn't have relation search which is a big issue #1606

Closed diveshahuja closed 2 months ago

diveshahuja commented 3 months ago

Have you searched through other issues to see if your problem is already reported or has been fixed?

Yes, I did not find it.

Did you read the documentation?

Yes, I did not find it.

Have you tried to publish the views?

No, this error is not related to views.

Is there an error in the console?

No

PHP Version

8.2

PowerGrid

5.8.1

Laravel

10.48.14

Livewire

3.5

Alpine JS

No response

Theme

Bootstrap

Describe the bug.

I wanted to apply a relation search on multi select and it was not working then i went through your code and saw that WithMultiSelectBuilder trait doesn't have whereHas or whereHasMorph just like InputText

To Reproduce...

First click on "FOO" then....

Extra information

<?php

namespace App\Livewire\Tables;

use App\Models\Client;
use App\Models\Group;
use App\Models\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Gate;
use Livewire\Attributes\On;
use PowerComponents\LivewirePowerGrid\Button;
use PowerComponents\LivewirePowerGrid\Column;
use PowerComponents\LivewirePowerGrid\Exportable;
use PowerComponents\LivewirePowerGrid\Facades\Filter;
use PowerComponents\LivewirePowerGrid\Footer;
use PowerComponents\LivewirePowerGrid\Header;
use PowerComponents\LivewirePowerGrid\PowerGrid;
use PowerComponents\LivewirePowerGrid\PowerGridComponent;
use PowerComponents\LivewirePowerGrid\PowerGridFields;
use PowerComponents\LivewirePowerGrid\Traits\WithExport;

final class ClientsTable extends PowerGridComponent
{
    use WithExport;
    public string $sortField = 'id';
    public int $companyId = 0;
    public string $sortDirection = 'desc';

    public function header(): array
    {
        return [
            Button::add('bulk-delete')
                ->slot('Bulk delete (<span x-text="window.pgBulkActions.count(\'' . $this->tableName . '\')"></span>)')
                ->class('btn btn-danger pg-btn-white dark:ring-pg-primary-600 dark:border-pg-primary-600 dark:hover:bg-pg-primary-700 dark:ring-offset-pg-primary-800 dark:text-pg-primary-300 dark:bg-pg-primary-700')
                ->dispatch('bulkDelete.' . $this->tableName, []),
        ];
    }

    public function setUp(): array
    {
        $this->showCheckBox();

        return [
            Exportable::make('export')
                ->striped()
                ->type(Exportable::TYPE_XLS, Exportable::TYPE_CSV),
            Header::make()->showSearchInput()->showToggleColumns()->showSearchInput(),
            Footer::make()
                ->showPerPage(perPageValues: [10, 50, 100, 200])
                ->showRecordCount(),
        ];
    }

    public function datasource(): Builder
    {
        return Client::companyId($this->companyId)->with('primaryContact', 'groups');
    }

    public function relationSearch(): array
    {
        return [
            'primaryContact' => [
                'email',
                'name'
            ],
            'groups' => [
                'name'
            ]
        ];
    }

    public function fields(): PowerGridFields
    {
        return PowerGrid::fields()
            ->add('id')
            ->add('company_name')
            ->add('primary_email', fn(Model $model) => e($model->primaryContact->email ?? ''))
            ->add('primary_contact', fn(Model $model) => e($model->primaryContact->name ?? ''))
            ->add('phone')
            ->add('groups', function (Model $model) {
                if ($model->groups->isNotEmpty()) {
                    return implode(',', $model->groups->pluck('name')->toArray());
                }
                return '';
            })
            ->add('active')
            ->add('created_at');
    }

    public function columns(): array
    {
        return [
            Column::make('ID', 'id')
                ->searchable()
                ->sortable(),

            Column::make('Company', 'company_name')
                ->searchable()
                ->editOnClick(Gate::allows('edit-client'), 'company_name', '1', true)
                ->sortable(),

            Column::make('Primary Contact', 'primary_contact')
                ->searchable()
                ->sortable(),

            Column::make('Primary Email', 'primary_email')
                ->sortable()
                ->searchable(),

            Column::make('Phone', 'phone')
                ->searchable()
                ->sortable(),

            Column::make('Active', 'active')
                ->searchable()
                ->toggleable()
                ->sortable(),

            Column::make('Groups', 'groups')
                ->searchable()
                ->sortable(),

            Column::make('Created at', 'created_at', 'created_at')
                ->searchable()
                ->sortable(),

            Column::action('Action')->visibleInExport(visible: false),
        ];
    }

    public function filters(): array
    {
        return [
            Filter::number('id'),
            Filter::inputText('company_name', 'company_name')->operators(['contains', 'contains_not', 'starts_with', 'ends_with']),
            Filter::inputText('primary_contact')->filterRelation('primaryContact', 'name'),
            Filter::inputText('primary_email')->filterRelation('primaryContact', 'email'),
            Filter::inputText('phone'),
            Filter::boolean('active')->label('Active', 'Inactive'),
            Filter::multiSelect('groups', 'id')
                ->dataSource(Group::companyId(1)->get())
                ->optionValue('id')
                ->optionLabel('name')
                ->filterRelation('groups', 'id'),
        ];
    }

    #[\Livewire\Attributes\On('edit')]
    public function edit($rowId): void
    {
        $this->js('alert(' . $rowId . ')');
    }

    public function actions(Client $row): array
    {
        $actions = [];
        if (Gate::allows('edit-clients')) {
            $actions[] = Button::make('edit', 'Edit')
                ->class('btn btn-primary btn-sm round waves-effect waves-float waves-light')
                ->route('admin::clients-edit', ['id' => $row->id]);
        }
        if (Gate::allows('delete-clients')) {
            $button = Button::make('destroy', 'Delete')
                ->target('')
                ->class('btn btn-danger btn-sm round waves-effect waves-float waves-light')
                ->route('admin::clients-delete', ['id' => $row->id]);
            $actions[] = $button;
        }
        if (Gate::allows('view-clients')) {
            $button = Button::make('view', 'View')
                ->class('btn btn-secondary btn-sm round waves-effect waves-float waves-light')
                ->route('admin::clients-view', ['id' => $row->id]);
            $actions[] = $button;
        }
        return $actions;
    }

    #[On('bulkDelete.{tableName}')]
    public function bulkDelete(): void
    {
        if($this->checkboxValues){
            Client::companyId($this->companyId)->whereIn('id', $this->checkboxValues)->delete();
            $this->js('window.pgBulkActions.clearAll()'); // clear the count on the interface.
            $this->js('Message.success("'.count($this->checkboxValues).' clients deleted")');
        }
    }
}
eafarooqi commented 3 months ago

Hallo @diveshahuja

working fine for me when i am using group_id instead of id in the filter definition, where group_id is the relational field on the main table. but i am not sure about your tables structure.

Filter::multiSelect('groups', 'group_id')

or you can also customize the filter query with builder instead of filterRelation.

 ->builder(function (Builder $query, mixed $value) {
    $query->whereHas('groups', function ($query) use ($value) {
        return $query->whereIn('id', $value);
    });
}),
luanfreitasdev commented 2 months ago

I think using the builder as @eafarooqi mentioned is safer than supporting this specifically in this filter.

Thanks