Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 43a313e

Browse files
committed
improve has for nested relations
1 parent 0dd537e commit 43a313e

2 files changed

Lines changed: 88 additions & 0 deletions

File tree

src/Illuminate/Database/Eloquent/Builder.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,11 @@ public function orWhere($column, $operator = null, $value = null)
597597
*/
598598
public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
599599
{
600+
if (strpos($relation, '.') !== false)
601+
{
602+
return $this->hasNested($relation, $operator, $count, $boolean, $callback);
603+
}
604+
600605
$relation = $this->getHasRelationQuery($relation);
601606

602607
$query = $relation->getRelationCountQuery($relation->getRelated()->newQuery(), $this);
@@ -606,6 +611,41 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C
606611
return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
607612
}
608613

614+
/**
615+
* Add nested relationship count conditions to the query.
616+
*
617+
* @param string $relations
618+
* @param string $operator
619+
* @param integer $count
620+
* @param string $boolean
621+
* @param \Closure $callback
622+
* @return \Illuminate\Database\Eloquent\Builder|static
623+
*/
624+
protected function hasNested($relations, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
625+
{
626+
$relations = explode('.', $relations);
627+
628+
// In order to nest "has", we need to add count relation constraints
629+
// on the callback closure. We will do this by simply passing to
630+
// closure its own reference, so it calls itself recursively.
631+
$closure = function ($q) use (&$closure, &$relations, $operator, $count, $boolean, $callback)
632+
{
633+
// If the "relation" is specified using dot notation, we will assume
634+
// that developer wants to check simple "has" on the intermediate
635+
// relations and add constraints only on the furthermost query.
636+
if (count($relations) > 1)
637+
{
638+
$q->whereHas(array_shift($relations), $closure);
639+
}
640+
else
641+
{
642+
$q->has(array_shift($relations), $operator, $count, $boolean, $callback);
643+
}
644+
};
645+
646+
return $this->whereHas(array_shift($relations), $closure);
647+
}
648+
609649
/**
610650
* Add a relationship count condition to the query.
611651
*

tests/Database/DatabaseEloquentBuilderTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,38 @@ public function testDeleteOverride()
487487
}
488488

489489

490+
public function testHasNestedWithConstraints()
491+
{
492+
$model = new EloquentBuilderTestModelParentStub;
493+
494+
$builder = $model->whereHas('foo', function ($q) {
495+
$q->whereHas('bar', function ($q) {
496+
$q->where('baz', 'bam');
497+
});
498+
})->toSql();
499+
500+
$result = $model->whereHas('foo.bar', function ($q) {
501+
$q->where('baz', 'bam');
502+
})->toSql();
503+
504+
$this->assertEquals($builder, $result);
505+
}
506+
507+
508+
public function testHasNested()
509+
{
510+
$model = new EloquentBuilderTestModelParentStub;
511+
512+
$builder = $model->whereHas('foo', function ($q) {
513+
$q->has('bar');
514+
});
515+
516+
$result = $model->has('foo.bar')->toSql();
517+
518+
$this->assertEquals($builder->toSql(), $result);
519+
}
520+
521+
490522
protected function mockConnectionForModel($model, $database)
491523
{
492524
$grammarClass = 'Illuminate\Database\Query\Grammars\\'.$database.'Grammar';
@@ -556,3 +588,19 @@ public function __get($key)
556588
return 'foo_' . $this->attributes[$key];
557589
}
558590
}
591+
592+
class EloquentBuilderTestModelParentStub extends Illuminate\Database\Eloquent\Model {
593+
public function foo()
594+
{
595+
return $this->belongsTo('EloquentBuilderTestModelCloseRelatedStub');
596+
}
597+
}
598+
599+
class EloquentBuilderTestModelCloseRelatedStub extends Illuminate\Database\Eloquent\Model {
600+
public function bar()
601+
{
602+
return $this->hasMany('EloquentBuilderTestModelFarRelatedStub');
603+
}
604+
}
605+
606+
class EloquentBuilderTestModelFarRelatedStub extends Illuminate\Database\Eloquent\Model {}

0 commit comments

Comments
 (0)