Skip to content

Commit

Permalink
Merge pull request #2 from OssiPesonen/feature/exclude-aliases
Browse files Browse the repository at this point in the history
Optionally exclude aliases
  • Loading branch information
OssiPesonen authored Jun 24, 2020
2 parents 89cc649 + 180d54c commit 7168dab
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 26 deletions.
86 changes: 60 additions & 26 deletions src/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ class QueryBuilder
*/
private $type = self::SELECT;

/**
* Option to automatically excluded alias use from joins without the additional $excludeAlias parameter
*
* @var bool
*/
private $excludeAliases = false;

/**
* The state of the query object. Can be dirty or clean.
*
Expand Down Expand Up @@ -278,6 +285,16 @@ public function getParameter($key)
return $this->params[$key] ?? null;
}

/**
* Exclude aliases from join clauses and instead use the table's full name
*
* @return $this
*/
public function setExcludeAliases() {
$this->excludeAliases = true;
return $this;
}

/**
* Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
*
Expand Down Expand Up @@ -595,16 +612,17 @@ public function from($from, $alias = null)
* ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
* </code>
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param string $fromAlias The alias or table name that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param bool $excludeAlias The $alias will be set to an empty string and no check is performed on uniqueness
*
* @return $this This QueryBuilder instance.
*/
public function join($fromAlias, $join, $alias, $condition = null)
public function join($fromAlias, $join, $alias, $condition = null, $excludeAlias = false)
{
return $this->innerJoin($fromAlias, $join, $alias, $condition);
return $this->innerJoin($fromAlias, $join, $alias, $condition, $excludeAlias);
}

/**
Expand All @@ -617,21 +635,26 @@ public function join($fromAlias, $join, $alias, $condition = null)
* ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
* </code>
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param string $fromAlias The alias or table name that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param bool $excludeAlias The $alias will be set to an empty string and no check is performed on uniqueness
*
* @return $this This QueryBuilder instance.
*/
public function innerJoin($fromAlias, $join, $alias, $condition = null)
public function innerJoin($fromAlias, $join, $alias, $condition = null, $excludeAlias = false)
{
# Exclude alias use if either class variable or given parameter is set to true
$excludeAlias = $this->excludeAliases || $excludeAlias;

return $this->add('join', [
$fromAlias => [
'joinType' => 'inner',
'joinTable' => $join,
'joinAlias' => $alias,
'joinAlias' => !$excludeAlias ? $alias : "",
'joinCondition' => $condition,
'excludeAlias' => $excludeAlias
],
], true);
}
Expand All @@ -646,21 +669,26 @@ public function innerJoin($fromAlias, $join, $alias, $condition = null)
* ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
* </code>
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param string $fromAlias The alias or table name that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param bool $excludeAlias The $alias will be set to an empty string and no check is performed on uniqueness
*
* @return $this This QueryBuilder instance.
*/
public function leftJoin($fromAlias, $join, $alias, $condition = null)
public function leftJoin($fromAlias, $join, $alias, $condition = null, $excludeAlias = false)
{
# Exclude alias use if either class variable or given parameter is set to true
$excludeAlias = $this->excludeAliases || $excludeAlias;

return $this->add('join', [
$fromAlias => [
'joinType' => 'left',
'joinTable' => $join,
'joinAlias' => $alias,
'joinAlias' => !$excludeAlias ? $alias : "",
'joinCondition' => $condition,
'excludeAlias' => $excludeAlias
],
], true);
}
Expand All @@ -675,21 +703,26 @@ public function leftJoin($fromAlias, $join, $alias, $condition = null)
* ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
* </code>
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param string $fromAlias The alias or table name that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
* @param bool $excludeAlias The $alias will be set to an empty string and no check is performed on uniqueness
*
* @return $this This QueryBuilder instance.
*/
public function rightJoin($fromAlias, $join, $alias, $condition = null)
public function rightJoin($fromAlias, $join, $alias, $condition = null, $excludeAlias = false)
{
# Exclude alias use if either class variable or given parameter is set to true
$excludeAlias = $this->excludeAliases || $excludeAlias;

return $this->add('join', [
$fromAlias => [
'joinType' => 'right',
'joinTable' => $join,
'joinAlias' => $alias,
'joinAlias' => !$excludeAlias ? $alias : "",
'joinCondition' => $condition,
'excludeAlias' => $excludeAlias
],
], true);
}
Expand Down Expand Up @@ -1346,11 +1379,12 @@ private function getSQLForJoins($fromAlias, array &$knownAliases)
}

foreach ($this->sqlParts['join'][$fromAlias] as $join) {
if (array_key_exists($join['joinAlias'], $knownAliases)) {
if (!$join['excludeAlias'] && array_key_exists($join['joinAlias'], $knownAliases)) {
throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
}

$sql .= ' ' . strtoupper($join['joinType'])
. ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias']
. ' JOIN ' . $join['joinTable'] . ($join['excludeAlias'] ? '' : ' ' . $join['joinAlias'])
. ' ON ' . ((string) $join['joinCondition']);
$knownAliases[$join['joinAlias']] = true;
}
Expand Down
116 changes: 116 additions & 0 deletions tests/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Tests\Elixir;

use Elixir\Exceptions\QueryException;
use Elixir\QueryBuilder;
use Elixir\ParameterType;
use MongoDB\Driver\Query;
Expand Down Expand Up @@ -959,4 +960,119 @@ public function testWrongfulWhereChainingWithTwoAndWheres(): void

$this->assertEquals("SELECT id FROM users WHERE (1 = 1) AND (id = :id) AND (name = :name)", (string)$qb);
}

public function testJoinWithoutAlias(): void
{
$qb = new QueryBuilder();

$qb->select('user.id')
->from('user')
->join('user','addresses','','addresses.user_id = user.id', true);

$this->assertEquals("SELECT user.id FROM user INNER JOIN addresses ON addresses.user_id = user.id", (string)$qb);
}

public function testInnerJoinWithoutAlias(): void
{
$qb = new QueryBuilder();

$qb->select('user.id')
->from('user')
->innerJoin('user','addresses','','addresses.user_id = user.id', true);

$this->assertEquals("SELECT user.id FROM user INNER JOIN addresses ON addresses.user_id = user.id", (string)$qb);
}

public function testLeftJoinWithoutAlias(): void
{
$qb = new QueryBuilder();

$qb->select('user.id')
->from('user')
->leftJoin('user','addresses','','addresses.user_id = user.id', true);

$this->assertEquals("SELECT user.id FROM user LEFT JOIN addresses ON addresses.user_id = user.id", (string)$qb);
}

public function testRightJoinWithoutAlias(): void
{
$qb = new QueryBuilder();

$qb->select('user.id')
->from('user')
->rightJoin('user','addresses','','addresses.user_id = user.id', true);

$this->assertEquals("SELECT user.id FROM user RIGHT JOIN addresses ON addresses.user_id = user.id", (string)$qb);
}

public function testMultipleJoinsWithoutAlias(): void
{
$qb = new QueryBuilder();

$qb->select('user.id')
->from('user')
->join('user','addresses','','addresses.user_id = user.id', true)
->innerJoin('user','car','','car.user_id = user.id', true)
->leftJoin('user','pet','','pet.user_id = user.id', true)
->rightJoin('user','child','','child.user_id = user.id', true);

$this->assertEquals("SELECT user.id FROM user INNER JOIN addresses ON addresses.user_id = user.id INNER JOIN car ON car.user_id = user.id LEFT JOIN pet ON pet.user_id = user.id RIGHT JOIN child ON child.user_id = user.id", (string)$qb);
}


public function testLeftJoinWithSetExcludeAliases(): void
{
$qb = new QueryBuilder();

$qb->setExcludeAliases()
->select('user.id')
->from('user')
->leftJoin('user','addresses','','addresses.user_id = user.id', true);

$this->assertEquals("SELECT user.id FROM user LEFT JOIN addresses ON addresses.user_id = user.id", (string)$qb);
}

public function testMultipleJoinsWithSetExcludeAliases(): void
{
$qb = new QueryBuilder();

$qb->setExcludeAliases()
->select('user.id')
->from('user')
->join('user','addresses','','addresses.user_id = user.id')
->innerJoin('user','car','','car.user_id = user.id')
->leftJoin('user','pet','','pet.user_id = user.id')
->rightJoin('user','child','','child.user_id = user.id');

$this->assertEquals("SELECT user.id FROM user INNER JOIN addresses ON addresses.user_id = user.id INNER JOIN car ON car.user_id = user.id LEFT JOIN pet ON pet.user_id = user.id RIGHT JOIN child ON child.user_id = user.id", (string)$qb);
}

public function testJoinWithAliasWhenExcludingThrowsException(): void
{
$qb = new QueryBuilder();

$this->expectException(QueryException::class);
$this->expectExceptionMessage("The given alias 'table_a' is not part of any FROM or JOIN clause table. The currently registered aliases are: a.");

$qb->select('table_a.id')
->setExcludeAliases()
->from('table_a', 'a')
->join('table_a', 'table_b', 'a', 'table_b.fk_b = table_a.id')
->join('table_a', 'table_c', 'a', 'table_c.fk_b = table_a.id');

self::assertEquals('', $qb->getSQL());
}

public function testJoinsWithExcludeAliasesButFromHasAlias(): void
{
$qb = new QueryBuilder();

$qb->select('a.id')
->setExcludeAliases()
->from('table_a', 'a')
->join('a', 'table_b', 'a', 'table_b.fk_b = a.id')
->join('a', 'table_c', 'a', 'table_c.fk_b = a.id');

self::assertEquals('SELECT a.id FROM table_a a INNER JOIN table_b ON table_b.fk_b = a.id INNER JOIN table_c ON table_c.fk_b = a.id', $qb->getSQL());
}
}

0 comments on commit 7168dab

Please sign in to comment.