How to split out applied filters in generated query
wonder95 opened this issue · comments
Per the docs, I have created a custom filter like so:
<?php
namespace App\Custom\Filters;
use Illuminate\Database\Eloquent\Builder;
use Spatie\QueryBuilder\Filters\Filter;
class NameMemberNumberFilter implements Filter
{
/**
* @inheritDoc
*/
public function __invoke(Builder $query, $value, string $property)
{
$query->where('last_name', 'like', "%{$value}%")
->orWhere('first_name', 'like', "%{$value}%")
->orWhere('member_number', 'like', "%{$value}%");
}
}
and implemented it in my controller like so
public function index()
{
return Inertia::render('Users/Index', [
'users' => QueryBuilder::for(User::class)
->defaultSort('member_number')
->allowedSorts(['last_name', 'member_number'])
->allowedFilters(AllowedFilter::custom('name_number', new NameMemberNumberFilter), 'status', 'member_type')
->paginate(20)
->withQueryString()
]);
}
By itself, it works fine. However, when I attempt to combine it with another filter, (e.g. 'status' or 'member_type'), the custom filter is applied, but the others are completely ignored.
http://members.test/users?filter[name_number]=Smith&filter[status]=Active
In this example, all the Smiths are returned, but not only the ones with a status
of Active
.
If I use status
and member_type
together, they work fine. The issue seems to be that when using a custom filter, it cannot be used with another filter. Have I somehow written or implemented my custom filter incorrectly? Or is this indeed a bug?
Turns out this is not a bug, but an issue with the query for my status
and member_type
fields. Per the docs
By default, any string values passed to allowedFilters() will automatically be converted to AllowedFilter::partial() filters.
so my query for the other fields was using LIKE
, e.g.
SELECT count(*) as aggregate FROM `users` WHERE (`last_name` like '%Smith%' or `first_name` like '%Smith%' or `member_number` like '%Smith%' and LOWER(`users`.`status`) LIKE '%active%') and `users`.`deleted_at` IS NULL
In this case, because I also have a status of Inactive
, so the LIKE
query would get both. The logical action would be to use AllowedFilter::exact()
like so
return Inertia::render('Users/Index', [
'users' => QueryBuilder::for(User::class)
->defaultSort('member_number')
->allowedSorts(['last_name', 'member_number'])
->allowedFilters(AllowedFilter::custom('name_number', new NameMemberNumberFilter),
AllowedFilter::exact('status'),
AllowedFilter::exact('member_type'))
->paginate(20)
->withQueryString()
]);
However, this still does not solve the problem, because of the way the query is generated. What it does is
SELECT count(*) as aggregate FROM `users` WHERE (`last_name` like '%Smith%' or `first_name` like '%Smith%' or `member_number` like '%Smith%' and LOWER(`users`.`status`) LIKE '%active%') and `users`.`deleted_at` IS NULL
Because of the way it is grouped, I still get an Inactive
record that I don't want. I need to change the query to split out the users.active
field to get what I want:
SELECT * FROM `users`
WHERE (`last_name` like '%Smith%' or `first_name` like '%Smith%' or `member_number` like '%Smith%') and `users`.`status` = 'Active'
and `users`.`deleted_at` IS NULL
ORDER BY `member_number`
ASC LIMIT 20 offset 0;
Since I am just specifying the available filters inside allowedFilters()
, is there a way I can specify that the filters should be separate AND
phrases and not all grouped in one WHERE
clause?
Dear contributor,
because this issue seems to be inactive for quite some time now, I've automatically closed it. If you feel this issue deserves some attention from my human colleagues feel free to reopen it.