fix: handle nullable filter edge cases for empty and not-null values#1051
Conversation
Fixes two edge cases in FiltersOperator when using dynamic operator filters with nullable(): 1. Empty filter value (?filter[x]=) now defaults to EQUAL operator instead of empty string, correctly producing WHERE x IS NULL 2. Not-null filter (?filter[x]=<>) now correctly uses null value instead of empty string, producing WHERE x IS NOT NULL Both issues were identified by @pimdongit in spatie#1047. Ref: spatie#1047 (comment)
0649f9c to
631bebd
Compare
freekmurze
left a comment
There was a problem hiding this comment.
Thanks for the PR and the tests! The edge cases you're fixing are valid.
However, the if ($value === '') { $value = null; } check on line 46-48 is unconditional, meaning it applies to all operator filters, not just dynamic ones. This could change behavior for non-dynamic filters:
For example, AllowedFilter::operator('name', FilterOperator::EQUAL) with ?filter[name]=:
- Before:
WHERE name = ''(filter for empty string) - After:
WHERE name IS NULL(different semantics)
The empty-string-to-null conversion should only happen for dynamic filters, where it's the result of stripping the operator prefix (e.g., <> stripped from <> leaves '').
Could you move the check inside the dynamic filter branch instead?
} elseif ($this->filterOperator->isDynamic() && $value !== null) {
$filterOperator = $this->getDynamicFilterOperator($value);
$this->removeDynamicFilterOperatorFromValue($value, $filterOperator);
if ($value === '') {
$value = null;
}
} elseif ($this->filterOperator->isDynamic() && $value === null) {
$filterOperator = FilterOperator::EQUAL;
}Per maintainer feedback, the conversion should only apply when a dynamic operator prefix is stripped (e.g. '<>' stripped from '<>' leaves ''), not for all operator filters. Moves the check inside the isDynamic && value !== null branch.
|
Thanks for the clear explanation, @freekmurze. You're right that the unconditional check would change semantics for non-dynamic filters like Moved the empty-string-to-null conversion inside the |
freekmurze
left a comment
There was a problem hiding this comment.
Looks good, thanks for the fix and the tests!
Summary
Fixes two edge cases in
FiltersOperatorwhen using dynamic operator filters with->nullable():Edge Case 1: Empty filter value
Query:
?filter[my_filter]=Before: Operator resolved to empty string
""After: Operator defaults to
FilterOperator::EQUAL, producingWHERE column IS NULLEdge Case 2: Not-null filter with empty value
Query:
?filter[my_filter]=<>Before: Value was empty string
"", producingWHERE column <> ''After: Value converts to
null, producingWHERE column IS NOT NULLChanges
elseifclause to default toEQUALoperator when value is null in dynamic filter modeCredits
Both issues were identified and diagnosed by @pimdongit in #1047 (comment). They provided the proposed fixes -- this PR implements them with test coverage.
Ref: #1047