From 6fe5916f40c320ace083ad19d06e83cceeb07f47 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 4 Sep 2021 10:08:11 +1000 Subject: [PATCH 1/2] Add Eloquent builder `whereMorphedTo` method to streamline finding models morphed to another model --- .../Concerns/QueriesRelationships.php | 31 +++++++++++++++++ .../Database/DatabaseEloquentBuilderTest.php | 33 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index 63cb031f970f..cf8cc06bb3ee 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -414,6 +414,37 @@ public function orWhereMorphRelation($relation, $types, $column, $operator = nul }); } + /** + * Add a morph-to relationship condition to the query. + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param \Illuminate\Database\Eloquent\Model $model + * @return \Illuminate\Database\Eloquent\Builder|static + */ + public function whereMorphedTo($relation, $model, $boolean = 'and') + { + if (is_string($relation)) { + $relation = $this->getRelationWithoutConstraints($relation); + } + + return $this->where(function ($query) use ($relation, $model) { + $query->where($relation->getMorphType(), $model->getMorphClass()) + ->where($relation->getForeignKeyName(), $model->getKey()); + }, null, null, $boolean); + } + + /** + * Add a morph-to relationship condition to the query with an "or where" clause. + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param \Illuminate\Database\Eloquent\Model $model + * @return \Illuminate\Database\Eloquent\Builder|static + */ + public function orWhereMorphedTo($relation, $model) + { + return $this->whereMorphedTo($relation, $model, 'or'); + } + /** * Add subselect queries to include an aggregate value for a relationship. * diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php index a652bfcae573..c2160eff8809 100755 --- a/tests/Database/DatabaseEloquentBuilderTest.php +++ b/tests/Database/DatabaseEloquentBuilderTest.php @@ -1305,6 +1305,34 @@ public function testOrWhereDoesntHave() $this->assertEquals(['baz', 'quux'], $builder->getBindings()); } + public function testWhereMorphedTo() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $relatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $relatedModel->id = 1; + + $builder = $model->whereMorphedTo('morph', $relatedModel); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where ("morph_type" = ? and "morph_id" = ?)', $builder->toSql()); + $this->assertEquals([$relatedModel->getMorphClass(), $relatedModel->getKey()], $builder->getBindings()); + } + + public function testOrWhereMorphedTo() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $relatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $relatedModel->id = 1; + + $builder = $model->where('bar', 'baz')->orWhereMorphedTo('morph', $relatedModel); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or ("morph_type" = ? and "morph_id" = ?)', $builder->toSql()); + $this->assertEquals(['baz', $relatedModel->getMorphClass(), $relatedModel->getKey()], $builder->getBindings()); + } + public function testWhereKeyMethodWithInt() { $model = $this->getMockModel(); @@ -1793,6 +1821,11 @@ public function roles() 'related_id' ); } + + public function morph() + { + return $this->morphTo(); + } } class EloquentBuilderTestModelCloseRelatedStub extends Model From b0dec3580c480323eb46b156b48d87067ac8fab4 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Tue, 7 Sep 2021 06:32:42 +1000 Subject: [PATCH 2/2] Add ability to pass string as the model name --- .../Concerns/QueriesRelationships.php | 14 +++++++-- .../Database/DatabaseEloquentBuilderTest.php | 29 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index cf8cc06bb3ee..43807c29167a 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -418,7 +418,7 @@ public function orWhereMorphRelation($relation, $types, $column, $operator = nul * Add a morph-to relationship condition to the query. * * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation - * @param \Illuminate\Database\Eloquent\Model $model + * @param \Illuminate\Database\Eloquent\Model|string $model * @return \Illuminate\Database\Eloquent\Builder|static */ public function whereMorphedTo($relation, $model, $boolean = 'and') @@ -427,6 +427,16 @@ public function whereMorphedTo($relation, $model, $boolean = 'and') $relation = $this->getRelationWithoutConstraints($relation); } + if (is_string($model)) { + $morphMap = Relation::morphMap(); + + if (! empty($morphMap) && in_array($model, $morphMap)) { + $model = array_search($model, $morphMap, true); + } + + return $this->where($relation->getMorphType(), $model, null, $boolean); + } + return $this->where(function ($query) use ($relation, $model) { $query->where($relation->getMorphType(), $model->getMorphClass()) ->where($relation->getForeignKeyName(), $model->getKey()); @@ -437,7 +447,7 @@ public function whereMorphedTo($relation, $model, $boolean = 'and') * Add a morph-to relationship condition to the query with an "or where" clause. * * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation - * @param \Illuminate\Database\Eloquent\Model $model + * @param \Illuminate\Database\Eloquent\Model|string $model * @return \Illuminate\Database\Eloquent\Builder|static */ public function orWhereMorphedTo($relation, $model) diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php index c2160eff8809..3ec4b97547d1 100755 --- a/tests/Database/DatabaseEloquentBuilderTest.php +++ b/tests/Database/DatabaseEloquentBuilderTest.php @@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\RelationNotFoundException; +use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder as BaseBuilder; use Illuminate\Database\Query\Grammars\Grammar; @@ -1333,6 +1334,34 @@ public function testOrWhereMorphedTo() $this->assertEquals(['baz', $relatedModel->getMorphClass(), $relatedModel->getKey()], $builder->getBindings()); } + public function testWhereMorphedToClass() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $builder = $model->whereMorphedTo('morph', EloquentBuilderTestModelCloseRelatedStub::class); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "morph_type" = ?', $builder->toSql()); + $this->assertEquals([EloquentBuilderTestModelCloseRelatedStub::class], $builder->getBindings()); + } + + public function testWhereMorphedToAlias() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + Relation::morphMap([ + 'alias' => EloquentBuilderTestModelCloseRelatedStub::class, + ]); + + $builder = $model->whereMorphedTo('morph', EloquentBuilderTestModelCloseRelatedStub::class); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "morph_type" = ?', $builder->toSql()); + $this->assertEquals(['alias'], $builder->getBindings()); + + Relation::morphMap([], false); + } + public function testWhereKeyMethodWithInt() { $model = $this->getMockModel();