Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Add whereBelongsTo() Eloquent builder method #38927

Merged
merged 14 commits into from
Sep 28, 2021
53 changes: 53 additions & 0 deletions src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace Illuminate\Database\Eloquent\Concerns;

use BadMethodCallException;
use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\RelationNotFoundException;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Builder as QueryBuilder;
Expand Down Expand Up @@ -455,6 +458,56 @@ public function orWhereMorphedTo($relation, $model)
return $this->whereMorphedTo($relation, $model, 'or');
}

/**
* Add a "belongs to" relationship where clause to the query.
*
* @param \Illuminate\Database\Eloquent\Model $related
* @param string $relationship
* @param string $boolean
* @return $this
*
* @throws \Exception
*/
public function whereBelongsTo($related, $relationshipName = null, $boolean = 'and')
{
if ($relationshipName === null) {
$relationshipName = Str::camel(class_basename($related));
}

try {
$relationship = $this->model->{$relationshipName}();
} catch (BadMethodCallException $exception) {
throw RelationNotFoundException::make($this->model, $relationshipName);
}

if (! $relationship instanceof BelongsTo) {
throw RelationNotFoundException::make($this->model, $relationshipName, BelongsTo::class);
}

$this->where(
$relationship->getQualifiedForeignKeyName(),
'=',
$related->getAttributeValue($relationship->getOwnerKeyName()),
$boolean,
);

return $this;
}

/**
* Add an "BelongsTo" relationship with an "or where" clause to the query.
*
* @param \Illuminate\Database\Eloquent\Model $related
* @param string $relationship
* @return $this
*
* @throws \Exception
*/
public function orWhereBelongsTo($related, $relationshipName = null)
{
return $this->whereBelongsTo($related, $relationshipName, 'or');
}

/**
* Add subselect queries to include an aggregate value for a relationship.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ class RelationNotFoundException extends RuntimeException
*
* @param object $model
* @param string $relation
* @param string|null $type
* @return static
*/
public static function make($model, $relation)
public static function make($model, $relation, $type = null)
{
$class = get_class($model);

$instance = new static("Call to undefined relationship [{$relation}] on model [{$class}].");
$instance = new static(
is_null($type)
? "Call to undefined relationship [{$relation}] on model [{$class}]."
: "Call to undefined relationship [{$relation}] on model [{$class}] of type [{$type}].",
);

$instance->model = $class;
$instance->relation = $relation;
Expand Down
47 changes: 47 additions & 0 deletions tests/Database/DatabaseEloquentBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,35 @@ public function testPostgresOperatorsWhere()
$this->assertEquals($result, $builder);
}

public function testWhereBelongsTo()
{
$related = new EloquentBuilderTestWhereBelongsToStub([
'id' => 1,
'parent_id' => 2,
]);

$parent = new EloquentBuilderTestWhereBelongsToStub([
'id' => 2,
'parent_id' => 1,
]);

$builder = $this->getBuilder();
$builder->shouldReceive('from')->with('eloquent_builder_test_where_belongs_to_stubs');
$builder->setModel($related);
$builder->getQuery()->shouldReceive('where')->once()->with('eloquent_builder_test_where_belongs_to_stubs.parent_id', '=', 2, 'and');

$result = $builder->whereBelongsTo($parent);
$this->assertEquals($result, $builder);

$builder = $this->getBuilder();
$builder->shouldReceive('from')->with('eloquent_builder_test_where_belongs_to_stubs');
$builder->setModel($related);
$builder->getQuery()->shouldReceive('where')->once()->with('eloquent_builder_test_where_belongs_to_stubs.parent_id', '=', 2, 'and');

$result = $builder->whereBelongsTo($parent, 'parent');
$this->assertEquals($result, $builder);
}

public function testDeleteOverride()
{
$builder = $this->getBuilder();
Expand Down Expand Up @@ -1948,3 +1977,21 @@ class EloquentBuilderTestStubStringPrimaryKey extends Model

protected $keyType = 'string';
}

class EloquentBuilderTestWhereBelongsToStub extends Model
{
protected $fillable = [
'id',
'parent_id',
];

public function eloquentBuilderTestWhereBelongsToStub()
{
return $this->belongsTo(self::class, 'parent_id', 'id', 'parent');
}

public function parent()
{
return $this->belongsTo(self::class, 'parent_id', 'id', 'parent');
}
}