From 805507ec54991eddc1a5208743b47f652a794f9f Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Fri, 6 Apr 2018 00:11:35 +0200 Subject: [PATCH 1/2] Add joinSub(), leftJoinSub(), rightJoinSub() to query builder --- src/Illuminate/Database/Query/Builder.php | 55 +++++++++++++++++++++ tests/Database/DatabaseQueryBuilderTest.php | 33 +++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 36eea18842c3..bb0f40cffa21 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -423,6 +423,31 @@ public function joinWhere($table, $first, $operator, $second, $type = 'inner') return $this->join($table, $first, $operator, $second, $type, true); } + /** + * Add a subquery join clause to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|string $query + * @param string $as + * @param string $first + * @param string|null $operator + * @param string|null $second + * @param string $type + * @param bool $where + * @return \Illuminate\Database\Query\Builder|static + * + * @throws \InvalidArgumentException + */ + public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false) + { + list($query, $bindings) = $this->createSub($query); + + $expression = '('.$query.') as '.$this->grammar->wrap($as); + + $this->addBinding($bindings, 'join'); + + return $this->join(new Expression($expression), $first, $operator, $second, $type, $where); + } + /** * Add a left join to the query. * @@ -451,6 +476,21 @@ public function leftJoinWhere($table, $first, $operator, $second) return $this->joinWhere($table, $first, $operator, $second, 'left'); } + /** + * Add a subquery left join to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|string $query + * @param string $as + * @param string $first + * @param string|null $operator + * @param string|null $second + * @return \Illuminate\Database\Query\Builder|static + */ + public function leftJoinSub($query, $as, $first, $operator = null, $second = null) + { + return $this->joinSub($query, $as, $first, $operator, $second, 'left'); + } + /** * Add a right join to the query. * @@ -479,6 +519,21 @@ public function rightJoinWhere($table, $first, $operator, $second) return $this->joinWhere($table, $first, $operator, $second, 'right'); } + /** + * Add a subquery right join to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|string $query + * @param string $as + * @param string $first + * @param string|null $operator + * @param string|null $second + * @return \Illuminate\Database\Query\Builder|static + */ + public function rightJoinSub($query, $as, $first, $operator = null, $second = null) + { + return $this->joinSub($query, $as, $first, $operator, $second, 'right'); + } + /** * Add a "cross join" clause to the query. * diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 5f524a88c60c..e6b91e4ca105 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -1286,6 +1286,39 @@ public function testJoinsWithNestedJoinWithAdvancedSubqueryCondition() $this->assertEquals(['1', 10000], $builder->getBindings()); } + public function testJoinSub() + { + $builder = $this->getBuilder(); + $builder->from('users')->joinSub('select * from "contacts"', 'sub', 'users.id', '=', 'sub.id'); + $this->assertEquals('select * from "users" inner join (select * from "contacts") as "sub" on "users"."id" = "sub"."id"', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->from('users')->joinSub(function ($q) { + $q->from('contacts'); + }, 'sub', 'users.id', '=', 'sub.id'); + $this->assertEquals('select * from "users" inner join (select * from "contacts") as "sub" on "users"."id" = "sub"."id"', $builder->toSql()); + + $builder = $this->getBuilder(); + $query = $this->getBuilder()->from('contacts')->where('name', 'foo'); + $builder->from('users')->joinSub($query, 'sub', 'users.id', '=', 1, 'inner', true); + $this->assertEquals('select * from "users" inner join (select * from "contacts" where "name" = ?) as "sub" on "users"."id" = ?', $builder->toSql()); + $this->assertEquals(['foo', 1], $builder->getRawBindings()['join']); + } + + public function testLeftJoinSub() + { + $builder = $this->getBuilder(); + $builder->from('users')->leftJoinSub($this->getBuilder()->from('contacts'), 'sub', 'users.id', '=', 'sub.id'); + $this->assertEquals('select * from "users" left join (select * from "contacts") as "sub" on "users"."id" = "sub"."id"', $builder->toSql()); + } + + public function testRightJoinSub() + { + $builder = $this->getBuilder(); + $builder->from('users')->rightJoinSub($this->getBuilder()->from('contacts'), 'sub', 'users.id', '=', 'sub.id'); + $this->assertEquals('select * from "users" right join (select * from "contacts") as "sub" on "users"."id" = "sub"."id"', $builder->toSql()); + } + public function testRawExpressionsInSelect() { $builder = $this->getBuilder(); From d9d2bfc94709d4f0c0487a326aeef38893fe1ee2 Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Sun, 8 Apr 2018 00:21:19 +0200 Subject: [PATCH 2/2] Add joinSub() test with multiple subqueries --- tests/Database/DatabaseQueryBuilderTest.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index e6b91e4ca105..b4f6a36130e4 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -1299,10 +1299,16 @@ public function testJoinSub() $this->assertEquals('select * from "users" inner join (select * from "contacts") as "sub" on "users"."id" = "sub"."id"', $builder->toSql()); $builder = $this->getBuilder(); - $query = $this->getBuilder()->from('contacts')->where('name', 'foo'); - $builder->from('users')->joinSub($query, 'sub', 'users.id', '=', 1, 'inner', true); - $this->assertEquals('select * from "users" inner join (select * from "contacts" where "name" = ?) as "sub" on "users"."id" = ?', $builder->toSql()); - $this->assertEquals(['foo', 1], $builder->getRawBindings()['join']); + $sub1 = $this->getBuilder()->from('contacts')->where('name', 'foo'); + $sub2 = $this->getBuilder()->from('contacts')->where('name', 'bar'); + $builder->from('users') + ->joinSub($sub1, 'sub1', 'users.id', '=', 1, 'inner', true) + ->joinSub($sub2, 'sub2', 'users.id', '=', 'sub2.user_id'); + $expected = 'select * from "users" '; + $expected .= 'inner join (select * from "contacts" where "name" = ?) as "sub1" on "users"."id" = ? '; + $expected .= 'inner join (select * from "contacts" where "name" = ?) as "sub2" on "users"."id" = "sub2"."user_id"'; + $this->assertEquals($expected, $builder->toSql()); + $this->assertEquals(['foo', 1, 'bar'], $builder->getRawBindings()['join']); } public function testLeftJoinSub()