relationSearch() leaks records excluded by base where constraints — orWhereHas is not grouped with the column search #2131
alex-inbase
started this conversation in
BUG
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Livewire PowerGrid version
v6.11.0
Livewire version
v4.4.1
Laravel version
v13.29.0
Which PHP version are you using?
PHP 8.3
Which Theme are you using?
None
Have you published the resource files and customized them? (php artisan vendor:publish)
No
What is the problem?
When a base constraint is applied in datasource() (e.g. an archive flag), searching a relationship declared via relationSearch() returns records that the base constraint should exclude. The relationship search is appended as an ungrouped orWhereHas(...) at the outermost query level, outside the column-search group. Because SQL AND binds tighter than OR, the base constraint no longer applies to the relationship branch.
Code snippets
Location:
src/DataSource/Processors/Database/Handlers/SearchHandler.php, method apply(). The column search is wrapped in $query->where(function ($subQuery) { ... }), but the relationship search is applied afterwards, on the outer $query:
$query->where(function (...) use (...) {
// column search -> orWhere(...)
});
if ($hasRelationSearch) {
$this->filterRelation($query, $search); // <-- runs on the OUTER $query, outside the group
}
Reproduced SQL (minimal case: base filter + nested relationSearch)
Current (leaks — the or exists(...) sits outside the group):
sql
select * from
hauptwhere (
haupt.is_archived= ?)and (
haupt.namelike ?)or exists (
select * from
kindinner join
haupt_kindonkind.id=haupt_kind.kind_idwhere
haupt.id=haupt_kind.haupt_idand exists (
select * from
enkelwhere
kind.enkel_id=enkel.idandtour_nummerlike ?)
)
Result: an archived record whose relation matches the term is returned, despite is_archived = false.
How do you expect it to work?
Expected (relation search grouped with column search):
sql
select * from
hauptwhere (
haupt.is_archived= ?)and (
haupt.namelike ?or exists ( ... nested relation subquery ... )
)
Result: archived records are correctly excluded.
Suggested fix
Move filterRelation() inside the same where closure so both the column orWhere conditions and the relationship orWhereHas conditions are grouped together and AND-ed against the base query as one unit:
php
$query->where(function ($subQuery) use ($search, $hasRelationSearch) {
// ... column search ...
});
I verified this against a minimal reproduction: with the current code the archived record is returned (1 match, archived); with the fix it is correctly excluded (0 matches). Only the grouping parenthesis around the relation exists(...) differs between the two SQL statements above.
I've attached my locally patched SearchHandler.php for reference — the only change from the original is moving the filterRelation() call from after the where() closure into it (marked with a comment). I applied it locally without touching the vendor directory by rebinding SearchHandlerContract to a subclass, so the fix is possible without any breaking API change.
SearchHandler_patched.php.txt
Please confirm (incomplete submissions will not be addressed)
All reactions