Skip to content

Commit

Permalink
Add multiple joins (#43)
Browse files Browse the repository at this point in the history
* added multiple joins with on clauses

* fixing tests coverage

* fixing code style

* added check for using and on clauses in one join and refactored on method for join clause

* code style fixes
  • Loading branch information
romanpravda authored May 14, 2021
1 parent 637bbd2 commit c29de12
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 79 deletions.
17 changes: 11 additions & 6 deletions src/Exceptions/GrammarException.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class GrammarException extends Exception
{
public static function wrongJoin(JoinClause $joinClause)
public static function wrongJoin(JoinClause $joinClause): self
{
$whatMissing = [];

Expand All @@ -22,26 +22,31 @@ public static function wrongJoin(JoinClause $joinClause)
$whatMissing[] = 'table or subquery';
}

if (is_null($joinClause->getUsing())) {
$whatMissing[] = 'using';
if (is_null($joinClause->getUsing()) && is_null($joinClause->getOnClauses())) {
$whatMissing[] = 'using or on clauses';
}

$whatMissing = implode(', ', $whatMissing);

return new static("Missed required segments for 'JOIN' section. Missed: {$whatMissing}");
}

public static function wrongFrom($from)
public static function ambiguousJoinKeys(): self
{
return new static('You cannot use using and on clauses as join keys for the same join.');
}

public static function wrongFrom(): self
{
return new static("Missed table or subquery for 'FROM' section.");
}

public static function missedTableForInsert()
public static function missedTableForInsert(): self
{
return new static('Missed table for insert statement.');
}

public static function missedWhereForDelete()
public static function missedWhereForDelete(): self
{
return new static('Missed where section for delete statement.');
}
Expand Down
42 changes: 22 additions & 20 deletions src/Query/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ abstract class BaseBuilder
protected $sample;

/**
* Join clause.
* Join clauses.
*
* @var JoinClause
* @var JoinClause[]|null
*/
protected $join;
protected $joins;

/**
* Array join clause.
Expand Down Expand Up @@ -490,13 +490,13 @@ public function join(
bool $global = false,
?string $alias = null
) {
$this->join = new JoinClause($this);
$join = new JoinClause($this);

/*
* If builder instance given, then we assume that sub-query should be used as table in join
*/
if ($table instanceof BaseBuilder) {
$this->join->query($table);
$join->query($table);

$this->files = array_merge($this->files, $table->getFiles());
}
Expand All @@ -506,42 +506,44 @@ public function join(
* set up JoinClause object in callback
*/
if ($table instanceof Closure) {
$table($this->join);
$table($join);
}

/*
* If given anything that is not builder instance or callback. For example, string,
* then we assume that table name was given.
*/
if (!$table instanceof Closure && !$table instanceof BaseBuilder) {
$this->join->table($table);
$join->table($table);
}

/*
* If using was given, then merge it with using given before, in closure
*/
if (!is_null($using)) {
$this->join->addUsing($using);
$join->addUsing($using);
}

if (!is_null($strict) && is_null($this->join->getStrict())) {
$this->join->strict($strict);
if (!is_null($strict) && is_null($join->getStrict())) {
$join->strict($strict);
}

if (!is_null($type) && is_null($this->join->getType())) {
$this->join->type($type);
if (!is_null($type) && is_null($join->getType())) {
$join->type($type);
}

if (!is_null($alias) && is_null($this->join->getAlias())) {
$this->join->as($alias);
if (!is_null($alias) && is_null($join->getAlias())) {
$join->as($alias);
}

$this->join->distributed($global);
$join->distributed($global);

if (!is_null($this->join->getSubQuery())) {
$this->join->query($this->join->getSubQuery());
if (!is_null($join->getSubQuery())) {
$join->query($join->getSubQuery());
}

$this->joins[] = $join;

return $this;
}

Expand Down Expand Up @@ -1933,11 +1935,11 @@ public function getArrayJoin(): ?ArrayJoinClause
/**
* Get JoinClause.
*
* @return JoinClause
* @return JoinClause[]|null
*/
public function getJoin(): ?JoinClause
public function getJoins(): ?array
{
return $this->join;
return $this->joins;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Query/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class Grammar
'from',
'sample',
'arrayJoin',
'join',
'joins',
'prewheres',
'wheres',
'groups',
Expand Down
56 changes: 43 additions & 13 deletions src/Query/JoinClause.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Tinderbox\ClickhouseBuilder\Query;

use Closure;
use Tinderbox\ClickhouseBuilder\Query\Enums\JoinStrict;
use Tinderbox\ClickhouseBuilder\Query\Enums\JoinType;
use Tinderbox\ClickhouseBuilder\Query\Enums\Operator;

class JoinClause
{
Expand Down Expand Up @@ -61,10 +63,17 @@ class JoinClause
/**
* Join alias.
*
* @var \Tinderbox\ClickhouseBuilder\Query\Identifier
* @var Identifier
*/
private $alias;

/**
* On clauses for joining rows between tables.
*
* @var TwoElementsLogicExpression[]|null
*/
private $onClauses;

/**
* JoinClause constructor.
*
Expand Down Expand Up @@ -116,15 +125,26 @@ public function using(...$columns): self
}

/**
* Alias for using method.
* Set "on" clause for join.
*
* @param array ...$columns
* @param string $first
* @param string $operator
* @param string $second
* @param string $concatOperator
*
* @return JoinClause
*/
public function on(...$columns): self
public function on(string $first, string $operator, string $second, string $concatOperator = Operator::AND): self
{
return $this->using($columns);
$expression = (new TwoElementsLogicExpression($this->query))
->firstElement(new Identifier($first))
->operator($operator)
->secondElement(new Identifier($second))
->concatOperator($concatOperator);

$this->onClauses[] = $expression;

return $this;
}

/**
Expand Down Expand Up @@ -174,7 +194,7 @@ public function type(string $type): self
*
* @return JoinClause
*/
public function all()
public function all(): self
{
return $this->strict(JoinStrict::ALL);
}
Expand All @@ -184,7 +204,7 @@ public function all()
*
* @return JoinClause
*/
public function any()
public function any(): self
{
return $this->strict(JoinStrict::ANY);
}
Expand All @@ -194,7 +214,7 @@ public function any()
*
* @return JoinClause
*/
public function inner()
public function inner(): self
{
return $this->type(JoinType::INNER);
}
Expand All @@ -204,7 +224,7 @@ public function inner()
*
* @return JoinClause
*/
public function left()
public function left(): self
{
return $this->type(JoinType::LEFT);
}
Expand All @@ -226,7 +246,7 @@ public function distributed(bool $global = false): self
/**
* Set sub-query as table to select from.
*
* @param \Closure|BaseBuilder|null $query
* @param Closure|BaseBuilder|null $query
*
* @return JoinClause|BaseBuilder
*/
Expand All @@ -236,7 +256,7 @@ public function query($query = null)
return $this->subQuery();
}

if ($query instanceof \Closure) {
if ($query instanceof Closure) {
$query = tap($this->query->newQuery(), $query);
}

Expand Down Expand Up @@ -270,7 +290,7 @@ public function subQuery(string $alias = null): BaseBuilder
*
* @return $this
*/
public function as(string $alias)
public function as(string $alias): self
{
$this->alias = new Identifier($alias);

Expand All @@ -287,6 +307,16 @@ public function getUsing(): ?array
return $this->using;
}

/**
* Get on clauses.
*
* @return array|null
*/
public function getOnClauses(): ?array
{
return $this->onClauses;
}

/**
* Get flag to use or not to use GLOBAL option.
*
Expand Down Expand Up @@ -330,7 +360,7 @@ public function getSubQuery(): ?BaseBuilder
/**
* Get table to select from.
*
* @return Expression|null|Identifier
* @return Expression|null|string
*/
public function getTable()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Query/Traits/FromComponentCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function compileFromComponent(BaseBuilder $builder, From $from): string
private function verifyFrom(From $from)
{
if (is_null($from->getTable())) {
throw GrammarException::wrongFrom($from);
throw GrammarException::wrongFrom();
}
}
}
Loading

0 comments on commit c29de12

Please sign in to comment.