diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index 63cb031f970f..43807c29167a 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -414,6 +414,47 @@ 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|string $model + * @return \Illuminate\Database\Eloquent\Builder|static + */ + public function whereMorphedTo($relation, $model, $boolean = 'and') + { + if (is_string($relation)) { + $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()); + }, 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|string $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..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; @@ -1305,6 +1306,62 @@ 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 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(); @@ -1793,6 +1850,11 @@ public function roles() 'related_id' ); } + + public function morph() + { + return $this->morphTo(); + } } class EloquentBuilderTestModelCloseRelatedStub extends Model