Skip to content

Commit fa70e33

Browse files
authored
Merge pull request #793 from DirectoryTree/fix-model-filter-behaviour
Align model query builder filter behaviour with root query builder
2 parents c1b00d0 + ce20443 commit fa70e33

File tree

7 files changed

+69
-30
lines changed

7 files changed

+69
-30
lines changed

src/Query/Builder.php

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use LdapRecord\Query\Filter\AndGroup;
1818
use LdapRecord\Query\Filter\Factory;
1919
use LdapRecord\Query\Filter\Filter;
20-
use LdapRecord\Query\Filter\GroupFilter;
2120
use LdapRecord\Query\Filter\Not;
2221
use LdapRecord\Query\Filter\OrGroup;
2322
use LdapRecord\Query\Filter\Raw;
@@ -30,6 +29,7 @@ class Builder
3029
{
3130
use BuildsQueries;
3231
use EscapesValues;
32+
use ExtractsNestedFilters;
3333

3434
public const TYPE_SEARCH = 'search';
3535

@@ -788,30 +788,6 @@ public function orFilter(Closure $closure): static
788788
return $this;
789789
}
790790

791-
/**
792-
* Extract filters from a nested group filter for re-wrapping, preserving nested groups.
793-
*
794-
* @return array<Filter>
795-
*/
796-
protected function extractNestedFilters(Filter $filter): array
797-
{
798-
if (! $filter instanceof GroupFilter) {
799-
return [$filter];
800-
}
801-
802-
$children = $filter->getFilters();
803-
804-
// If any child is a group, preserve the structure
805-
foreach ($children as $child) {
806-
if ($child instanceof GroupFilter) {
807-
return $children;
808-
}
809-
}
810-
811-
// All children are non-groups, it's safe to unwrap.
812-
return $children;
813-
}
814-
815791
/**
816792
* Add a nested 'not' filter to the query.
817793
*/
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace LdapRecord\Query;
4+
5+
use LdapRecord\Query\Filter\Filter;
6+
use LdapRecord\Query\Filter\GroupFilter;
7+
8+
trait ExtractsNestedFilters
9+
{
10+
/**
11+
* Extract filters from a nested group filter for re-wrapping, preserving nested groups.
12+
*
13+
* @return array<Filter>
14+
*/
15+
protected function extractNestedFilters(Filter $filter): array
16+
{
17+
if (! $filter instanceof GroupFilter) {
18+
return [$filter];
19+
}
20+
21+
$children = $filter->getFilters();
22+
23+
// If any child is a group, preserve the structure.
24+
foreach ($children as $child) {
25+
if ($child instanceof GroupFilter) {
26+
return $children;
27+
}
28+
}
29+
30+
// All children are non-groups, it's safe to unwrap.
31+
return $children;
32+
}
33+
}

src/Query/Model/Builder.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use LdapRecord\Models\Types\ActiveDirectory;
1414
use LdapRecord\Query\Builder as QueryBuilder;
1515
use LdapRecord\Query\BuildsQueries;
16+
use LdapRecord\Query\ExtractsNestedFilters;
1617
use LdapRecord\Query\Filter\AndGroup;
1718
use LdapRecord\Query\Filter\Filter;
1819
use LdapRecord\Query\Filter\Not;
@@ -28,6 +29,7 @@
2829
class Builder
2930
{
3031
use BuildsQueries;
32+
use ExtractsNestedFilters;
3133
use ForwardsCalls;
3234

3335
/**
@@ -728,7 +730,9 @@ public function andFilter(Closure $closure): static
728730
$query = $this->newNestedModelInstance($closure);
729731

730732
if ($filter = $query->getQuery()->getFilter()) {
731-
$this->query->addFilter(new AndGroup($filter));
733+
$this->query->addFilter(new AndGroup(
734+
...$this->extractNestedFilters($filter)
735+
), wrap: false);
732736
}
733737

734738
return $this;
@@ -742,7 +746,9 @@ public function orFilter(Closure $closure): static
742746
$query = $this->newNestedModelInstance($closure);
743747

744748
if ($filter = $query->getQuery()->getFilter()) {
745-
$this->query->addFilter(new OrGroup($filter), 'or');
749+
$this->query->addFilter(new OrGroup(
750+
...$this->extractNestedFilters($filter)
751+
), wrap: false);
746752
}
747753

748754
return $this;

tests/Unit/Models/ModelHasManyTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ public function test_only_related_with_many_relation_object_classes()
227227
{
228228
// Scopes are wrapped in their own AndGroup for isolation
229229
$this->assertEquals(
230-
'(&(&(|(objectclass=top)(objectclass=person)(objectclass=organizationalperson)(objectclass=user))(|(objectclass=top)(objectclass=group))))',
230+
'(&(|(objectclass=top)(objectclass=person)(objectclass=organizationalperson)(objectclass=user))(|(objectclass=top)(objectclass=group)))',
231231
(new ModelHasManyStubWithManyRelated)->relation()->onlyRelated()->getQuery()->getUnescapedQuery()
232232
);
233233
}

tests/Unit/Models/ModelQueryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public function test_new_queries_apply_object_class_scopes()
8282

8383
// Scopes are wrapped in their own AndGroup for isolation
8484
$this->assertEquals(
85-
'(&(&(objectclass=foo)(objectclass=bar)(objectclass=baz)))',
85+
'(&(objectclass=foo)(objectclass=bar)(objectclass=baz))',
8686
ModelWithObjectClassStub::query()->getUnescapedQuery()
8787
);
8888
}

tests/Unit/Models/ModelScopeTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public function test_scopes_remain_enforced_with_complex_queries()
167167
{
168168
// The scope (foo=bar) must always be present and enforced at the root AND level
169169
$this->assertEquals(
170-
'(&(|(name=\4a\6f\68\6e)(name=\4a\61\6e\65))(active=\74\72\75\65)(&(foo=bar)))',
170+
'(&(&(|(name=\4a\6f\68\6e)(name=\4a\61\6e\65))(active=\74\72\75\65))(&(foo=bar)))',
171171
ModelWithGlobalScopeTestStub::query()
172172
->where('name', '=', 'John')
173173
->orWhere('name', '=', 'Jane')

tests/Unit/Query/Model/ActiveDirectoryBuilderTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,28 @@ public function test_add_select_with_variadic_arguments()
165165
$this->assertContains('mail', $selects);
166166
$this->assertContains('objectguid', $selects);
167167
}
168+
169+
public function test_or_filter_extracts_filters_from_nested_query()
170+
{
171+
$b = $this->newBuilder();
172+
173+
$query = $b->orFilter(function ($query) {
174+
$query->whereEquals('foo', '1');
175+
$query->whereEquals('foo', '2');
176+
})->getUnescapedQuery();
177+
178+
$this->assertEquals('(|(foo=1)(foo=2))', $query);
179+
}
180+
181+
public function test_and_filter_extracts_filters_from_nested_query()
182+
{
183+
$b = $this->newBuilder();
184+
185+
$query = $b->andFilter(function ($query) {
186+
$query->whereEquals('foo', '1');
187+
$query->whereEquals('foo', '2');
188+
})->getUnescapedQuery();
189+
190+
$this->assertEquals('(&(foo=1)(foo=2))', $query);
191+
}
168192
}

0 commit comments

Comments
 (0)