From bfbc891619bc5a78e8beca3680d6b52a84aa44ed Mon Sep 17 00:00:00 2001 From: Hannes Reinhardt Date: Wed, 26 Nov 2025 21:37:33 +0100 Subject: [PATCH 1/4] test: test if whereDoesntHaveMorph is logically grouped and commutates --- .../Database/EloquentWhereHasMorphTest.php | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/Integration/Database/EloquentWhereHasMorphTest.php b/tests/Integration/Database/EloquentWhereHasMorphTest.php index 8661571f959e..c551b08128ca 100644 --- a/tests/Integration/Database/EloquentWhereHasMorphTest.php +++ b/tests/Integration/Database/EloquentWhereHasMorphTest.php @@ -27,6 +27,7 @@ protected function afterRefreshingDatabase() Schema::create('comments', function (Blueprint $table) { $table->increments('id'); + $table->string('title'); $table->nullableMorphs('commentable'); $table->softDeletes(); }); @@ -45,7 +46,10 @@ protected function afterRefreshingDatabase() $models[] = null; // deleted foreach ($models as $model) { - (new Comment)->commentable()->associate($model)->save(); + $comment = new Comment(); + $comment->commentable()->associate($model); + $comment->title = 'foo'; + $comment->save(); } } @@ -262,6 +266,22 @@ public function testWhereDoesntHaveMorphWithNullableMorph() $this->assertEquals([3, 7, 8], $comments->pluck('id')->all()); } + + public function testWhereDoesntHaveMorphWithNullableMorphAndAdditionalWhereIsLogicallyGrouped() + { + $commentsWhereFirst = Comment::whereNot('title', 'foo') + ->whereDoesntHaveMorph('commentable', '*') + ->orderBy('id') + ->get(); + + $commentsWhereLast = Comment::whereDoesntHaveMorph('commentable', '*') + ->whereNot('title', 'foo') + ->orderBy('id') + ->get(); + + $this->assertCount(0, $commentsWhereFirst); + $this->assertCount(0, $commentsWhereLast); + } } class Comment extends Model From 3d7ea75d7cd5ed0d765ae34f60e35032d507780c Mon Sep 17 00:00:00 2001 From: Hannes Reinhardt Date: Wed, 26 Nov 2025 21:37:38 +0100 Subject: [PATCH 2/4] fix: group orWhereMorphedTo correctly in hasMorph --- .../Database/Eloquent/Concerns/QueriesRelationships.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index c443e7313b06..f9aa760ffcd5 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -275,7 +275,7 @@ public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boole $type = Relation::getMorphedModel($type) ?? $type; } - return $this->where(function ($query) use ($relation, $callback, $operator, $count, $types) { + return $this->where(function ($query) use ($checkMorphNull, $relation, $callback, $operator, $count, $types) { foreach ($types as $type) { $query->orWhere(function ($query) use ($relation, $callback, $operator, $count, $type) { $belongsTo = $this->getBelongsToRelation($relation, $type); @@ -290,8 +290,8 @@ public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boole ->whereHas($belongsTo, $callback, $operator, $count); }); } - }, null, null, $boolean) - ->when($checkMorphNull, fn (self $query) => $query->orWhereMorphedTo($relation, null)); + $query->when($checkMorphNull, fn (self $query) => $query->orWhereMorphedTo($relation, null)); + }, null, null, $boolean); } /** From 7c641a29d8bd59640ae91d5c093a58a3bc1c8e89 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 27 Nov 2025 10:29:27 -0600 Subject: [PATCH 3/4] Update QueriesRelationships.php --- .../Database/Eloquent/Concerns/QueriesRelationships.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index f9aa760ffcd5..c935440ed261 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -275,7 +275,7 @@ public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boole $type = Relation::getMorphedModel($type) ?? $type; } - return $this->where(function ($query) use ($checkMorphNull, $relation, $callback, $operator, $count, $types) { + return $this->where(function ($query) use ($relation, $callback, $operator, $count, $types, $checkMorphNull) { foreach ($types as $type) { $query->orWhere(function ($query) use ($relation, $callback, $operator, $count, $type) { $belongsTo = $this->getBelongsToRelation($relation, $type); From b5007f47bbf5f92755399f329c92184ab3338056 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 27 Nov 2025 10:29:57 -0600 Subject: [PATCH 4/4] Add conditional morph check to relationship query --- .../Database/Eloquent/Concerns/QueriesRelationships.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index c935440ed261..81020f6817cc 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -290,6 +290,7 @@ public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boole ->whereHas($belongsTo, $callback, $operator, $count); }); } + $query->when($checkMorphNull, fn (self $query) => $query->orWhereMorphedTo($relation, null)); }, null, null, $boolean); }