From fcd1e2e21cb6694b9d6fc9e8e9026c29c796bbcc Mon Sep 17 00:00:00 2001 From: Guilhem DELAITRE Date: Fri, 23 Feb 2024 08:40:14 +0100 Subject: [PATCH 1/2] Add tests to emphasize the issues with nesting due to scope (nesting "or" groups but not "or not" groups and doubling first where clause negation) --- .../DatabaseEloquentLocalScopesTest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/Database/DatabaseEloquentLocalScopesTest.php b/tests/Database/DatabaseEloquentLocalScopesTest.php index 1d71f6f57661..d34a510f1e5f 100644 --- a/tests/Database/DatabaseEloquentLocalScopesTest.php +++ b/tests/Database/DatabaseEloquentLocalScopesTest.php @@ -61,6 +61,32 @@ public function testLocalScopesCanChained() $this->assertSame('select * from "table" where "active" = ? and "type" = ?', $query->toSql()); $this->assertEquals([true, 'foo'], $query->getBindings()); } + + public function testLocalScopeNestingDoesntDoubleFirstWhereClauseNegation() + { + $model = new EloquentLocalScopesTestModel; + $query = $model + ->newQuery() + ->whereNot('firstWhere', true) + ->orWhere('secondWhere', true) + ->active(); + + $this->assertSame('select * from "table" where (not "firstWhere" = ? or "secondWhere" = ?) and "active" = ?', $query->toSql()); + $this->assertEquals([true, true, true], $query->getBindings()); + } + + public function testLocalScopeNestingGroupsOrNotWhereClause() + { + $model = new EloquentLocalScopesTestModel; + $query = $model + ->newQuery() + ->where('firstWhere', true) + ->orWhereNot('secondWhere', true) + ->active(); + + $this->assertSame('select * from "table" where ("firstWhere" = ? or not "secondWhere" = ?) and "active" = ?', $query->toSql()); + $this->assertEquals([true, true, true], $query->getBindings()); + } } class EloquentLocalScopesTestModel extends Model From 2ec4ad2e885a34e46faa04a0e15065e980949ec0 Mon Sep 17 00:00:00 2001 From: Guilhem DELAITRE Date: Fri, 23 Feb 2024 08:41:32 +0100 Subject: [PATCH 2/2] Fix issues : also nest on "or not" clause, and don't repeat first where clause negation when nesting --- src/Illuminate/Database/Eloquent/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 5563b5477a2b..185781580684 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -1454,9 +1454,9 @@ protected function groupWhereSliceForScope(QueryBuilder $query, $whereSlice) // Here we'll check if the given subset of where clauses contains any "or" // booleans and in this case create a nested where expression. That way // we don't add any unnecessary nesting thus keeping the query clean. - if ($whereBooleans->contains('or')) { + if ($whereBooleans->contains(fn ($logicalOperator) => str_contains($logicalOperator, 'or'))) { $query->wheres[] = $this->createNestedWhere( - $whereSlice, $whereBooleans->first() + $whereSlice, str_replace(' not', '', $whereBooleans->first()) ); } else { $query->wheres = array_merge($query->wheres, $whereSlice);