MedicOneSystems / livewire-datatables

Advanced datatables using Laravel, Livewire, Tailwind CSS and Alpine JS
https://livewire-datatables.com/
MIT License
1.19k stars 258 forks source link

applying filter to a column which is using Eloquent method in itself. #547

Closed rameezmeans closed 1 year ago

rameezmeans commented 1 year ago

I am trying to filter a column which has a username which I am getting from user_id which here in this context is assigned_id. please help me.

Column::callback('assigned_to', function($id){
                return '<label class="label label-success">'.User::findOrFail($id)->name.'<label>';
            })->label('Assigned to')->filterable(collect(User::findOrFail())->map( function($value, $key) {
                return ['id' => $key, 'name' => $value];
            }))->searchable(),
mrtorks commented 1 year ago

@rameezmeans From my understanding, a callback would require an attribute that exists on the model class. I see you are using $id so I would do this:

Column::callback(['id'], function($id){
                return '<label class="label label-success">'.User::findOrFail($id)->name.'<label>';
            })->label('Assigned to')->filterable(User::get('name')->pluck('name')->toArray())->searchable(),

The above should do the trick. Also callbacks are funny in a sense that you need to pass only attributes you need or else the package would throw errors. I would have to do some investigation about using filterable because I have never done that. I would have to investigate. UPDATE A quick read validates what you are doing for filtrable. I have also updated your answer. I used pluck there because sometimes you might have a protected $appends and $with array defined on your model and you do not need that. The updated answer should mimic the example in the docs. Let me know if it works.

rameezmeans commented 1 year ago

Thank you for replying. I really appreciate that. Yes, your solution worked fine for the part of the problem. Now I have a dropdown filter with right values but it must filter as well. That dropdown is Not filtering the table. For simplicity, I am using this.

Column::callback(['assigned_to'], function($id){ return User::findOrFail($id)->name; })->label('Assigned to')->filterable(User::get('name')->pluck('name')->toArray())->searchable(),

mrtorks commented 1 year ago

Hi @rameezmeans. A callback column, when using this package, needs an array with attributes you would pass to the function., whether you are doing stuff directly or passing the params to a view. Your code is not working because the callback is looking for $id, which it expects in the array your are passing however you have passed ['assigned_to'] to the array instead of ['id']. I reproduced your question and it worked, using this:

Column::callback(['id','name'], function($id){
                return '<label class="label label-success">'.User::findOrFail($id)->name.'<label>';
            })->label('Assigned to')->filterable(User::get('name')->pluck('name')->toArray())->searchable(),

I am attaching an image of the code working

Screen Shot 2023-02-25 at 3 42 38 PM

if possible can you attach your migration for users here?

rameezmeans commented 1 year ago

Hi. I really thank you for your effort but it is not working for me. I will try to elaborate.

  1. I have a Files table and I am assigning files to users. 1 to 1 relationship there. File has 'id' and 'assigned_to' is foreign key.
  2. I have to use 'assigned_to' because 'id' will give me ID of file. I can not use that to get Username from User table.
  3. I did use 'id' as you mentioned but unfortunately it is still not working. Here is my query.
  4. 'pluck()' will give us 'name' array. It shows in dropdown like this.
<select>
   <option id="Nick">Nick</option>
</select>

I think it must be like this.

<select>
   <option id="24">Nick</option>
</select>

Here is the modified code, I am using.

Column::callback(['id','name'], function($id){
                    $assignedTo = File::findOrFail($id)->assigned_to;
                    return User::findOrFail($assignedTo)->name;
                })->label('Assigned to')
                ->filterable(User::get('name')->pluck('name')->toArray())
                ->searchable(),

Is there anyway to tell 'filterable()' that is must create select using 'id' and 'name' not just 'name'. I even used this.

Column::callback(['id','name'], function($id){
                    $assignedTo = File::findOrFail($id)->assigned_to;
                    return User::findOrFail($assignedTo)->name;
                })->label('Assigned to')
                ->filterable(User::get(['id', 'name'])->pluck('name','id')->toArray())
                ->searchable(),

But 'select does not have 'id' in it. That is why it is not working, I guess.

mrtorks commented 1 year ago

Viola!! @rameezmeans I got the select box to use id and name. We can make solid progress from here. Try this:

Column::callback(['id','name'], function($id){
                    $assignedTo = File::findOrFail($id)->assigned_to;
                    return User::findOrFail($assignedTo)->name;
                })->label('Assigned to')
                ->filterable(User::get(['id', 'name']))
                ->searchable(),

I think when you pass a collection instead of an array, it would magically assign the value as the id and the name as the text. I have attached an image of how it looks like in debugger.

Screen Shot 2023-02-25 at 7 23 11 PM

Let me know if this works.

Now that I know why you were passing assigned_to you can do this instead if the above works:

Column::callback(['assigned_to'], function($assigned_to){
                    return User::findOrFail($assigned_to)->name;
                })->label('Assigned to')
                ->filterable(User::get(['id', 'name']))
                ->searchable(),
rameezmeans commented 1 year ago

Thank you so much, Sir. The code with the "['id', 'name'] "does provide results but they were Wrong. However, the code with 'assigned_id' worked perfectly as it should be working.

mrtorks commented 1 year ago

Glad I was able to help

rameezmeans commented 1 year ago

There is only a slight confusion. The Sign which keeps track of Selected items is showing 'id'. It should show 'name'.

mrtorks commented 1 year ago

Its a trade off, it would filter by id since id is the value. try it with just name. like this:

Column::callback(['assigned_to'], function($assigned_to){
                    return User::findOrFail($assigned_to)->name;
                })->label('Assigned to')
                ->filterable(User::get(['name']))
                ->searchable(),

You can reopen the issue if you want. Logically this should work since you are filtering using a collection of names and passing a name to it via a callback.

rameezmeans commented 1 year ago

Yeah. It is a trade off. I am just opening it so that you can keep track of it. I am satisfied. Also I used your code and it broke at this point.

@elseif(is_array($label))
     <option value="{{ $label['id'] }}">{{ $label['name'] }}</option>
@elseif(is_numeric($value))

and this was the error.

"Undefined array key "id"".

mrtorks commented 1 year ago

@rameezmeans this is because we do not have id in this collection User::get(['name']). You have to refactor your code to use just name in this case. In the code snippet you just posted, it would have to be

@elseif(is_array($label))
     <option value="{{ $label['name'] }}">{{ $label['name'] }}</option>
@elseif(is_numeric($value))
rameezmeans commented 1 year ago

No. That was the code from the file 'resources/views/livewire/datatables/filters/select.blade.php'. It must remain the same otherwise query will bring forward wrong results. However, there is a piece of code at the end of same file and this is responsible for printing 'id' instead of 'name.'

@if($value)    
    <span>{{ \App\Models\User::findOrFail($value)->name }}</span>
@endif

can you tell me where I can find 'getDisplayValue' function?

rameezmeans commented 1 year ago

Yes. That is why I am getting this code in 'views' folder of My project. I just did this at that line and it is wonderful. Thank you for you time and energy. I really appreciate that.

@if($value)    
    <span>{{ \App\Models\User::findOrFail($value)->name }}</span>
@endif
mrtorks commented 1 year ago

I updated my response and yes your option works too. I am glad you fully got your issue resolved. I learned a lot too

rameezmeans commented 1 year ago

Sorry, but your solution involving line 15 will break everything. There must be 'id' in there. Not 'name'. However, I just updated line 29 of the same file according to my needs. So you may update your response again. It does provide 'name' in display but it is fetching wrong results. I am using 'id' in 'assigned_to' column. Thanks!

mrtorks commented 1 year ago

I would remove that then