From ab69a8b236986e33d096b13d010981dba58b1958 Mon Sep 17 00:00:00 2001 From: Henrique Pedrosa Date: Sat, 10 Mar 2018 02:36:59 -0300 Subject: [PATCH] Adding fromSub and fromRaw methods to query Builder --- src/Illuminate/Database/Query/Builder.php | 68 +++++++++++++++++---- tests/Database/DatabaseQueryBuilderTest.php | 34 +++++++++++ 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 3362f025d368..a1c1f0c38f74 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -52,6 +52,7 @@ class Builder */ public $bindings = [ 'select' => [], + 'from' => [], 'join' => [], 'where' => [], 'having' => [], @@ -241,15 +242,12 @@ public function selectRaw($expression, array $bindings = []) } /** - * Add a subselect expression to the query. + * Creates a subquery and parse it. * * @param \Closure|\Illuminate\Database\Query\Builder|string $query - * @param string $as - * @return \Illuminate\Database\Query\Builder|static - * - * @throws \InvalidArgumentException + * @return array */ - public function selectSub($query, $as) + protected function createSub($query) { // If the given query is a Closure, we will execute it while passing in a new // query instance to the Closure. This will give the developer a chance to @@ -261,9 +259,23 @@ public function selectSub($query, $as) } // Here, we will parse this query into an SQL string and an array of bindings - // so we can add it to the query builder using the selectRaw method so the - // query is included in the real SQL generated by this builder instance. - list($query, $bindings) = $this->parseSubSelect($query); + // so we can later add it to the query builder using a componentRaw method + // to include the sub in the real SQL generated by the builder instance. + return $this->parseSub($query); + } + + /** + * Add a subselect expression to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|string $query + * @param string $as + * @return \Illuminate\Database\Query\Builder|static + * + * @throws \InvalidArgumentException + */ + public function selectSub($query, $as) + { + list($query, $bindings) = $this->createSub($query); return $this->selectRaw( '('.$query.') as '.$this->grammar->wrap($as), $bindings @@ -271,12 +283,46 @@ public function selectSub($query, $as) } /** - * Parse the sub-select query into SQL and bindings. + * Makes "from" fetch from a subquery. + * + * @param \Closure|\Illuminate\Database\Query\Builder|string $query + * @param string $as + * @return \Illuminate\Database\Query\Builder|static + * + * @throws \InvalidArgumentException + */ + public function fromSub($query, $as) + { + list($query, $bindings) = $this->createSub($query); + + return $this->fromRaw( + '('.$query.') as '.$this->grammar->wrap($as), $bindings + ); + } + + /** + * Add a raw or where clause to the query. + * + * @param string $expression + * @param mixed $bindings + * @return \Illuminate\Database\Query\Builder|static + */ + public function fromRaw($expression, $bindings = []) + { + $this->from = new Expression($expression); + + $this->addBinding($bindings, 'from'); + + return $this; + } + + /** + * Parse the subquery into SQL and bindings. * * @param mixed $query * @return array */ - protected function parseSubSelect($query) + protected function parseSub($query) { if ($query instanceof self) { return [$query->toSql(), $query->getBindings()]; diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 6c0fd656d4ae..5f524a88c60c 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -2505,6 +2505,40 @@ public function testWhereRowValuesArityMismatch() $builder->select('*')->from('orders')->whereRowValues(['last_update'], '<', [1, 2]); } + public function testFromSub() + { + $builder = $this->getBuilder(); + $builder->fromSub(function ($query) { + $query->select(new Raw('max(last_seen_at) as last_seen_at'))->from('user_sessions')->where('foo', '=', '1'); + }, 'sessions')->where('bar', '<', '10'); + $this->assertEquals('select * from (select max(last_seen_at) as last_seen_at from "user_sessions" where "foo" = ?) as "sessions" where "bar" < ?', $builder->toSql()); + $this->assertEquals(['1', '10'], $builder->getBindings()); + } + + public function testFromSubWithoutBindings() + { + $builder = $this->getBuilder(); + $builder->fromSub(function ($query) { + $query->select(new Raw('max(last_seen_at) as last_seen_at'))->from('user_sessions'); + }, 'sessions'); + $this->assertEquals('select * from (select max(last_seen_at) as last_seen_at from "user_sessions") as "sessions"', $builder->toSql()); + } + + public function testFromRaw() + { + $builder = $this->getBuilder(); + $builder->fromRaw(new Raw('(select max(last_seen_at) as last_seen_at from "user_sessions") as "sessions"')); + $this->assertEquals('select * from (select max(last_seen_at) as last_seen_at from "user_sessions") as "sessions"', $builder->toSql()); + } + + public function testFromRawWithWhereOnTheMainQuery() + { + $builder = $this->getBuilder(); + $builder->fromRaw(new Raw('(select max(last_seen_at) as last_seen_at from "sessions") as "last_seen_at"'))->where('last_seen_at', '>', '1520652582'); + $this->assertEquals('select * from (select max(last_seen_at) as last_seen_at from "sessions") as "last_seen_at" where "last_seen_at" > ?', $builder->toSql()); + $this->assertEquals(['1520652582'], $builder->getBindings()); + } + protected function getBuilder() { $grammar = new \Illuminate\Database\Query\Grammars\Grammar;