Skip to content

Commit

Permalink
Move getTablesUsedInFrom() from ActiveQuery to Query
Browse files Browse the repository at this point in the history
enable this feature on the Query level

close yiisoft#14431
  • Loading branch information
cebe committed Jul 12, 2017
1 parent 0070b97 commit 173108b
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 197 deletions.
72 changes: 3 additions & 69 deletions framework/db/ActiveQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -791,81 +791,15 @@ public function alias($alias)
}

/**
* Returns table names used in [[from]] indexed by aliases.
* Both aliases and names are enclosed into {{ and }}.
* @return string[] table names indexed by aliases
* @throws \yii\base\InvalidConfigException
* @inheritdoc
* @since 2.0.12
*/
public function getTablesUsedInFrom()
{
if (empty($this->from)) {
$tableNames = [$this->getPrimaryTableName()];
} elseif (is_array($this->from)) {
$tableNames = $this->from;
} elseif (is_string($this->from)) {
$tableNames = preg_split('/\s*,\s*/', trim($this->from), -1, PREG_SPLIT_NO_EMPTY);
} else {
throw new InvalidConfigException(gettype($this->from) . ' in $from is not supported.');
}

// Clean up table names and aliases
$cleanedUpTableNames = [];
foreach ($tableNames as $alias => $tableName) {
if (!is_string($alias)) {
$pattern = <<<PATTERN
~
^
\s*
(
(?:['"`\[]|{{)
.*?
(?:['"`\]]|}})
|
.*?
)
(?:
(?:
\s+
(?:as)?
\s*
)
(
(?:['"`\[]|{{)
.*?
(?:['"`\]]|}})
|
.*?
)
)?
\s*
$
~iux
PATTERN;
if (preg_match($pattern, $tableName, $matches)) {
if (isset($matches[1])) {
if (isset($matches[2])) {
list(, $tableName, $alias) = $matches;
} else {
$tableName = $alias = $matches[1];
}
if (strncmp($alias, '{{', 2) !== 0) {
$alias = '{{' . $alias . '}}';
}
if (strncmp($tableName, '{{', 2) !== 0) {
$tableName = '{{' . $tableName . '}}';
}
}
}
}

$tableName = str_replace(["'", '"', '`', '[', ']'], '', $tableName);
$alias = str_replace(["'", '"', '`', '[', ']'], '', $alias);

$cleanedUpTableNames[$alias] = $tableName;
$this->from = [$this->getPrimaryTableName()];
}

return $cleanedUpTableNames;
return parent::getTablesUsedInFrom();
}

/**
Expand Down
79 changes: 79 additions & 0 deletions framework/db/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;

/**
* Query represents a SELECT SQL statement in a way that is independent of DBMS.
Expand Down Expand Up @@ -440,6 +441,84 @@ protected function queryScalar($selectExpression, $db)
->queryScalar();
}

/**
* Returns table names used in [[from]] indexed by aliases.
* Both aliases and names are enclosed into {{ and }}.
* @return string[] table names indexed by aliases
* @throws \yii\base\InvalidConfigException
* @since 2.0.12
*/
public function getTablesUsedInFrom()
{
if (empty($this->from)) {
return [];
} elseif (is_array($this->from)) {
$tableNames = $this->from;
} elseif (is_string($this->from)) {
$tableNames = preg_split('/\s*,\s*/', trim($this->from), -1, PREG_SPLIT_NO_EMPTY);
} else {
throw new InvalidConfigException(gettype($this->from) . ' in $from is not supported.');
}

// Clean up table names and aliases
$cleanedUpTableNames = [];
foreach ($tableNames as $alias => $tableName) {
if (!is_string($alias)) {
$pattern = <<<PATTERN
~
^
\s*
(
(?:['"`\[]|{{)
.*?
(?:['"`\]]|}})
|
.*?
)
(?:
(?:
\s+
(?:as)?
\s*
)
(
(?:['"`\[]|{{)
.*?
(?:['"`\]]|}})
|
.*?
)
)?
\s*
$
~iux
PATTERN;
if (preg_match($pattern, $tableName, $matches)) {
if (isset($matches[1])) {
if (isset($matches[2])) {
list(, $tableName, $alias) = $matches;
} else {
$tableName = $alias = $matches[1];
}
if (strncmp($alias, '{{', 2) !== 0) {
$alias = '{{' . $alias . '}}';
}
if (strncmp($tableName, '{{', 2) !== 0) {
$tableName = '{{' . $tableName . '}}';
}
}
}
}

$tableName = str_replace(["'", '"', '`', '[', ']'], '', $tableName);
$alias = str_replace(["'", '"', '`', '[', ']'], '', $alias);

$cleanedUpTableNames[$alias] = $tableName;
}

return $cleanedUpTableNames;
}

/**
* Sets the SELECT part of the query.
* @param string|array|Expression $columns the columns to be selected.
Expand Down
132 changes: 4 additions & 128 deletions tests/framework/db/ActiveQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,62 +237,13 @@ public function testAlias_yet_set()
$this->assertEquals(['alias' => 'old'], $result->from);
}

public function testGetTableNames_notFilledFrom()
use GetTablesAliasTestTrait;
protected function createQuery()
{
$query = new ActiveQuery(Profile::className());

$tables = $query->getTablesUsedInFrom();

$this->assertEquals([
'{{' . Profile::tableName() . '}}' => '{{' . Profile::tableName() . '}}',
], $tables);
return new ActiveQuery(null);
}

public function testGetTableNames_isFromArray()
{
$query = new ActiveQuery(null);
$query->from = [
'{{prf}}' => '{{profile}}',
'{{usr}}' => '{{user}}',
'{{a b}}' => '{{c d}}',
];

$tables = $query->getTablesUsedInFrom();

$this->assertEquals([
'{{prf}}' => '{{profile}}',
'{{usr}}' => '{{user}}',
'{{a b}}' => '{{c d}}',
], $tables);
}

public function testGetTableNames_isFromString()
{
$query = new ActiveQuery(null);
$query->from = 'profile AS \'prf\', user "usr", `order`, "customer", "a b" as "c d"';

$tables = $query->getTablesUsedInFrom();

$this->assertEquals([
'{{prf}}' => '{{profile}}',
'{{usr}}' => '{{user}}',
'{{order}}' => '{{order}}',
'{{customer}}' => '{{customer}}',
'{{c d}}' => '{{a b}}',
], $tables);
}

public function testGetTableNames_isFromObject_generateException()
{
$query = new ActiveQuery(null);
$query->from = new \stdClass();

$this->setExpectedException('\yii\base\InvalidConfigException');

$query->getTablesUsedInFrom();
}

public function testGetTablesAlias_notFilledFrom()
public function testGetTableNames_notFilledFrom()
{
$query = new ActiveQuery(Profile::className());

Expand All @@ -302,79 +253,4 @@ public function testGetTablesAlias_notFilledFrom()
'{{' . Profile::tableName() . '}}' => '{{' . Profile::tableName() . '}}',
], $tables);
}

public function testGetTablesAlias_isFromArray()
{
$query = new ActiveQuery(null);
$query->from = [
'{{prf}}' => '{{profile}}',
'{{usr}}' => '{{user}}',
'{{a b}}' => '{{c d}}',
];

$tables = $query->getTablesUsedInFrom();

$this->assertEquals([
'{{prf}}' => '{{profile}}',
'{{usr}}' => '{{user}}',
'{{a b}}' => '{{c d}}',
], $tables);
}

public function testGetTablesAlias_isFromString()
{
$query = new ActiveQuery(null);
$query->from = 'profile AS \'prf\', user "usr", service srv, order, [a b] [c d], {{something}} AS myalias';

$tables = $query->getTablesUsedInFrom();

$this->assertEquals([
'{{prf}}' => '{{profile}}',
'{{usr}}' => '{{user}}',
'{{srv}}' => '{{service}}',
'{{order}}' => '{{order}}',
'{{c d}}' => '{{a b}}',
'{{myalias}}' => '{{something}}',
], $tables);
}

public function testGetTablesAlias_isFromObject_generateException()
{
$query = new ActiveQuery(null);
$query->from = new \stdClass();

$this->setExpectedException('\yii\base\InvalidConfigException');

$query->getTablesUsedInFrom();
}

/**
* @see https://github.com/yiisoft/yii2/issues/14150
*/
public function testGetTableAliasFromPrefixedTableName()
{
$query = new ActiveQuery(null);
$query->from = '{{%order_item}}';

$tables = $query->getTablesUsedInFrom();

$this->assertEquals([
'{{%order_item}}' => '{{%order_item}}',
], $tables);
}

/**
* @see https://github.com/yiisoft/yii2/issues/14211
*/
public function testGetTableAliasFromTableNameWithDatabase()
{
$query = new ActiveQuery(null);
$query->from = 'tickets.workflows';

$tables = $query->getTablesUsedInFrom();

$this->assertEquals([
'{{tickets.workflows}}' => '{{tickets.workflows}}',
], $tables);
}
}
1 change: 1 addition & 0 deletions tests/framework/db/ActiveRecordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,7 @@ public function testNoTablenameReplacement()

/**
* Ensure no ambiguous colum error occurs if ActiveQuery adds a JOIN
* https://github.com/yiisoft/yii2/issues/13757
*/
public function testAmbiguousColumnFindOne()
{
Expand Down
Loading

0 comments on commit 173108b

Please sign in to comment.