diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 16afbd436295..c92b7ebb7518 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -776,6 +776,49 @@ public function crossJoinSub($query, $as) return $this; } + /** + * Add a straight join to the query. + * + * @param \Illuminate\Contracts\Database\Query\Expression|string $table + * @param \Closure|string $first + * @param string|null $operator + * @param \Illuminate\Contracts\Database\Query\Expression|string|null $second + * @return $this + */ + public function straightJoin($table, $first, $operator = null, $second = null) + { + return $this->join($table, $first, $operator, $second, 'straight_join'); + } + + /** + * Add a "straight join where" clause to the query. + * + * @param \Illuminate\Contracts\Database\Query\Expression|string $table + * @param \Closure|\Illuminate\Contracts\Database\Query\Expression|string $first + * @param string $operator + * @param \Illuminate\Contracts\Database\Query\Expression|string $second + * @return $this + */ + public function straightJoinWhere($table, $first, $operator, $second) + { + return $this->joinWhere($table, $first, $operator, $second, 'straight_join'); + } + + /** + * Add a subquery straight join to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder<*>|string $query + * @param string $as + * @param \Closure|\Illuminate\Contracts\Database\Query\Expression|string $first + * @param string|null $operator + * @param \Illuminate\Contracts\Database\Query\Expression|string|null $second + * @return $this + */ + public function straightJoinSub($query, $as, $first, $operator = null, $second = null) + { + return $this->joinSub($query, $as, $first, $operator, $second, 'straight_join'); + } + /** * Get a new join clause. * diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index ea1c44e00866..092380bebc70 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -199,7 +199,9 @@ protected function compileJoins(Builder $query, $joins) return $this->compileJoinLateral($join, $tableAndNestedJoins); } - return trim("{$join->type} join {$tableAndNestedJoins} {$this->compileWheres($join)}"); + $joinWord = ($join->type === 'straight_join' && $this->supportsStraightJoins()) ? '' : ' join'; + + return trim("{$join->type}{$joinWord} {$tableAndNestedJoins} {$this->compileWheres($join)}"); })->implode(' '); } @@ -217,6 +219,16 @@ public function compileJoinLateral(JoinLateralClause $join, string $expression): throw new RuntimeException('This database engine does not support lateral joins.'); } + /** + * Determine if the grammar supports straight joins. + * + * @return bool + */ + protected function supportsStraightJoins() + { + throw new RuntimeException('This database engine does not support straight joins.'); + } + /** * Compile the "where" portions of the query. * diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index 4372bae32791..6c4c2c09e212 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -387,6 +387,14 @@ public function compileJoinLateral(JoinLateralClause $join, string $expression): return trim("{$join->type} join lateral {$expression} on true"); } + /** + * {@inheritdoc} + */ + protected function supportsStraightJoins() + { + return true; + } + /** * Prepare a JSON column being updated using the JSON_SET function. * diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 700d64a9502c..a22dd68c8f18 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -3299,6 +3299,53 @@ public function testRightJoinSub() $builder->from('users')->rightJoinSub(['foo'], 'sub', 'users.id', '=', 'sub.id'); } + public function testStraightJoin() + { + $builder = $this->getMySqlBuilder(); + $builder->getConnection()->shouldReceive('getDatabaseName'); + $builder->select('*')->from('users')->straightJoin('contacts', 'users.id', 'contacts.id'); + $this->assertSame('select * from `users` straight_join `contacts` on `users`.`id` = `contacts`.`id`', $builder->toSql()); + + $builder = $this->getMySqlBuilder(); + $builder->getConnection()->shouldReceive('getDatabaseName'); + $builder->select('*')->from('users')->join('contacts', 'users.id', '=', 'contacts.id')->straightJoin('photos', 'users.id', '=', 'photos.id'); + $this->assertSame('select * from `users` inner join `contacts` on `users`.`id` = `contacts`.`id` straight_join `photos` on `users`.`id` = `photos`.`id`', $builder->toSql()); + + $builder = $this->getMySqlBuilder(); + $builder->getConnection()->shouldReceive('getDatabaseName'); + $builder->select('*')->from('users')->straightJoinWhere('photos', 'users.id', '=', 'bar')->joinWhere('photos', 'users.id', '=', 'foo'); + $this->assertSame('select * from `users` straight_join `photos` on `users`.`id` = ? inner join `photos` on `users`.`id` = ?', $builder->toSql()); + $this->assertEquals(['bar', 'foo'], $builder->getBindings()); + } + + public function testStraightJoinNoSupport() + { + $this->expectException(RuntimeException::class); + $builder = $this->getBuilder(); + $builder->select('*')->from('users')->straightJoin('contacts', 'users.id', 'contacts.id'); + $builder->toSql(); + } + + public function testStraightJoinSub() + { + $builder = $this->getMySqlBuilder(); + $builder->getConnection()->shouldReceive('getDatabaseName'); + $builder->from('users')->straightJoinSub($this->getBuilder()->from('contacts'), 'sub', 'users.id', '=', 'sub.id'); + $this->assertSame('select * from `users` straight_join (select * from "contacts") as `sub` on `users`.`id` = `sub`.`id`', $builder->toSql()); + + $this->expectException(InvalidArgumentException::class); + $builder = $this->getBuilder(); + $builder->from('users')->straightJoinSub(['foo'], 'sub', 'users.id', '=', 'sub.id'); + } + + public function testStraightJoinSubNoSupport() + { + $this->expectException(RuntimeException::class); + $builder = $this->getBuilder(); + $builder->from('users')->straightJoinSub($this->getBuilder()->from('contacts'), 'sub', 'users.id', '=', 'sub.id'); + $builder->toSql(); + } + public function testJoinLateral() { $builder = $this->getMySqlBuilder();