diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 42fdfcedd858..32fd66e246d5 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -999,6 +999,41 @@ public function with($relations) return $this; } + /** + * Add subselect queries to count the relations. + * + * @param mixed $relations + * @return $this + */ + public function withCount($relations) + { + // If no columns are set, add the default * columns. + if (is_null($this->query->columns)) { + $this->query->select(['*']); + } + + if (is_string($relations)) { + $relations = func_get_args(); + } + + $relations = $this->parseWithRelations($relations); + + foreach ($relations as $name => $constraints) { + // First determine the count query for the given relationship, + // then run the constraints callback to get the final query. + // This query will be added as subSelect query. + $relation = $this->getHasRelationQuery($name); + $query = $relation->getRelationCountQuery($relation->getRelated()->newQuery(), $this); + + call_user_func($constraints, $query); + + $asColumn = snake_case($name).'_count'; + $this->selectSub($query->getQuery(), $asColumn); + } + + return $this; + } + /** * Parse a list of relations into individuals. * diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php index 41e0499f6760..89ba77bc0d76 100755 --- a/tests/Database/DatabaseEloquentBuilderTest.php +++ b/tests/Database/DatabaseEloquentBuilderTest.php @@ -455,6 +455,37 @@ public function testDeleteOverride() $this->assertEquals(['foo' => $builder], $builder->delete()); } + public function testWithCount() + { + $model = new EloquentBuilderTestModelParentStub; + + $builder = $model->withCount('foo'); + + $this->assertEquals('select *, (select count(*) from "eloquent_builder_test_model_close_related_stubs" where "eloquent_builder_test_model_parent_stubs"."foo_id" = "eloquent_builder_test_model_close_related_stubs"."id") as "foo_count" from "eloquent_builder_test_model_parent_stubs"', $builder->toSql()); + } + + public function testWithCountAndSelect() + { + $model = new EloquentBuilderTestModelParentStub; + + $builder = $model->select('id')->withCount('foo'); + + $this->assertEquals('select "id", (select count(*) from "eloquent_builder_test_model_close_related_stubs" where "eloquent_builder_test_model_parent_stubs"."foo_id" = "eloquent_builder_test_model_close_related_stubs"."id") as "foo_count" from "eloquent_builder_test_model_parent_stubs"', $builder->toSql()); + } + + public function testWithCountAndContraintsAndHaving() + { + $model = new EloquentBuilderTestModelParentStub; + + $builder = $model->where('bar', 'baz'); + $builder->withCount(['foo' => function ($q) { + $q->where('bam', '>', 'qux'); + }])->having('fooCount', '>=', 1); + + $this->assertEquals('select *, (select count(*) from "eloquent_builder_test_model_close_related_stubs" where "eloquent_builder_test_model_parent_stubs"."foo_id" = "eloquent_builder_test_model_close_related_stubs"."id" and "bam" > ?) as "foo_count" from "eloquent_builder_test_model_parent_stubs" where "bar" = ? having "fooCount" >= ?', $builder->toSql()); + $this->assertEquals(['qux', 'baz', 1], $builder->getBindings()); + } + public function testHasWithContraintsAndHavingInSubquery() { $model = new EloquentBuilderTestModelParentStub;