Refactored solution to be contained within ActiveQuery instead of helper

This commit is contained in:
Alexander Makarov
2017-04-25 23:12:50 +03:00
parent 8474927a55
commit 7db93fc33d
6 changed files with 172 additions and 211 deletions

View File

@ -5,6 +5,7 @@ Yii Framework 2 Change Log
--------------------------
- Bug #13694: `yii\widgets\Pjax` now sends `X-Pjax-Url` header with response to fix redirect (wleona3, Faryshta)
- Bug #13842: Fixed ambiguous table SQL error while using unique validator (vladis84, samdark)
- Bug #14012: `yii\db\pgsql\Schema::findViewNames()` was skipping materialized views (insolita)
- Bug #13362: Fixed return value of `yii\caching\MemCache::setValues()` (masterklavi)
- Enh #13963: Added tests for yii\behaviors\TimestampBehavior (vladis84)

View File

@ -148,10 +148,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
}
if (empty($this->from)) {
/* @var $modelClass ActiveRecord */
$modelClass = $this->modelClass;
$tableName = $modelClass::tableName();
$this->from = [$tableName];
$this->from = [$this->getPrimaryTableName()];
}
if (empty($this->select) && !empty($this->join)) {
@ -559,9 +556,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
private function getTableNameAndAlias()
{
if (empty($this->from)) {
/* @var $modelClass ActiveRecord */
$modelClass = $this->modelClass;
$tableName = $modelClass::tableName();
$tableName = $this->getPrimaryTableName();
} else {
$tableName = '';
foreach ($this->from as $alias => $tableName) {
@ -783,9 +778,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
list($tableName, ) = $this->getTableNameAndAlias();
$this->from = [$alias => $tableName];
} else {
/* @var $modelClass ActiveRecord */
$modelClass = $this->modelClass;
$tableName = $modelClass::tableName();
$tableName = $this->getPrimaryTableName();
foreach ($this->from as $key => $table) {
if ($table === $tableName) {
@ -796,4 +789,89 @@ class ActiveQuery extends Query implements ActiveQueryInterface
}
return $this;
}
/**
* Table names based on from
* @return string[] table names
* @throws \yii\base\InvalidConfigException
* @since 2.0.12
*/
public function getFromTableNames()
{
$tableNames = [];
if (empty($this->from)) {
$tableNames[] = $this->getPrimaryTableName();
} elseif (is_array($this->from)) {
$tableNames = array_values($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
foreach ($tableNames as &$tableName) {
$tableName = preg_replace('/^(\w+)\s*.*$/', '$1', $tableName);
}
return $tableNames;
}
/**
* @return string[] table aliases based on from
* @throws \yii\base\InvalidConfigException
* @since 2.0.12
*/
public function getFromAliases()
{
$tablesAlias = [];
if (empty($this->from)) {
return [$this->getPrimaryTableName()];
}
if (is_string($this->from)) {
$tableNames = preg_split('/\s*,\s*/', trim($this->from), -1, PREG_SPLIT_NO_EMPTY);
} elseif (is_array($this->from)) {
$tableNames = $this->from;
} else {
throw new InvalidConfigException(gettype($this->from). ' in $from is not supported.');
}
foreach ($tableNames as $alias => $tableName) {
if (is_string($alias)) {
$tablesAlias[] = $alias;
} else {
$tablesAlias[] = $this->getAliasFromTableName($tableName);
}
}
return $tablesAlias;
}
/**
* Getting alias from table name
* Input string may contain alias already specified
*
* @param string $tableName
* @return string
* @since 2.0.12
*/
private function getAliasFromTableName($tableName)
{
$cleanedUpTableName = preg_replace('/\'|\"|`|as/u', '', trim($tableName));
return preg_replace('/^.+\s+(\w+)$/', '$1', $cleanedUpTableName);
}
/**
* @return string primary table name
* @since 2.0.12
*/
protected function getPrimaryTableName()
{
/* @var $modelClass ActiveRecord */
$modelClass = $this->modelClass;
return $modelClass::tableName();
}
}

View File

@ -1,107 +0,0 @@
<?php
namespace yii\helpers;
use yii\db\ActiveQuery;
use yii\base\InvalidConfigException;
class ActiveQueryHelper
{
/**
* Table names calculate on from.
* @param ActiveQuery $query
* @return string[] table names
*/
public static function getTableNames(ActiveQuery $query)
{
$tableNames = [];
$from = $query->from;
if (empty($from)) {
$tableNames[] = self::getTableNameForModel($query);
} elseif (is_array($from)) {
$tableNames = array_values($from);
} elseif (is_string($from)) {
$tableNames = preg_split('/\s*,\s*/', trim($from), -1, PREG_SPLIT_NO_EMPTY);
} else {
self::generateNotSupportedTypeFrom($from);
}
// Clear table alias.
foreach ($tableNames as &$tableName) {
$tableName = preg_replace('/^(\w+)\s*.*$/', '$1', $tableName);
}
return $tableNames;
}
/**
* Tables alias calculate on from.
* @param ActiveQuery $query
* @return string[] table alias
*/
public static function getTablesAlias(ActiveQuery $query)
{
$tablesAlias = [];
$from = $query->from;
if (empty($from)) {
return [self::getTableNameForModel($query)];
}
if (is_string($from)) {
$tableNames = preg_split('/\s*,\s*/', trim($from), -1, PREG_SPLIT_NO_EMPTY);
} elseif (is_array($from)) {
$tableNames = $from;
} else {
self::generateNotSupportedTypeFrom($from);
}
foreach ($tableNames as $alias => $tableName) {
if (is_string($alias)) {
$tablesAlias[] = $alias;
} else {
$tablesAlias[] = self::getAliasForTableName($tableName);
}
}
return $tablesAlias;
}
/**
* @param string $tableName
* @return string
*/
private static function getAliasForTableName($tableName)
{
$cleanedTableName = preg_replace('/\'|\"|`|as/u', '', trim($tableName));
$alias = preg_replace('/^.+\s+(\w+)$/', '$1', $cleanedTableName);
return $alias;
}
/**
* Table name get Model.
* @param ActiveQuery $query
* @return type
*/
private static function getTableNameForModel(ActiveQuery $query)
{
/* @var $modelClass ActiveRecord */
$modelClass = $query->modelClass;
return $modelClass::tableName();
}
/**
* @param mixed $from
* @throws InvalidConfigException
*/
private static function generateNotSupportedTypeFrom($from)
{
$error = sprintf('Not supported type "$s"', gettype($from));
throw new InvalidConfigException($error);
}
}

View File

@ -250,11 +250,14 @@ class UniqueValidator extends Validator
// Add table prefix for column
$targetClass = $this->getTargetClass($model);
/** @var ActiveRecord $targetClass */
$query = $targetClass::find();
$tableAlias = ActiveQueryHelper::getTablesAlias($query)[0];
$tableAliases = $query->getFromAliases();
$primaryTableAlias = $tableAliases[0];
$prefixedConditions = [];
foreach ($conditions as $columnName => $columnValue) {
$prefixedColumn = "{$tableAlias}.{$columnName}";
$prefixedColumn = "{$primaryTableAlias}.{$columnName}";
$prefixedConditions[$prefixedColumn] = $columnValue;
}

View File

@ -231,4 +231,82 @@ abstract class ActiveQueryTest extends DatabaseTestCase
$this->assertInstanceOf('yii\db\ActiveQuery', $result);
$this->assertEquals(['alias' => 'old'], $result->from);
}
public function testGetTableNames_notFilledFrom()
{
$query = new ActiveQuery(Profile::className());
$tableNames = $query->getFromTableNames();
$this->assertEquals([Profile::tableName()], $tableNames);
}
public function testGetTableNames_isFromArray()
{
$query = new ActiveQuery(null);
$query->from = ['prf' => 'profile', 'usr' => 'user'];
$tableNames = $query->getFromTableNames();
$this->assertEquals(['profile', 'user'], $tableNames);
}
public function testGetTableNames_isFromString()
{
$query = new ActiveQuery(null);
$query->from = 'profile AS \'prf\', user "usr", `order`, "customer"';
$tableNames = $query->getFromTableNames();
$this->assertEquals(['profile', 'user', '`order`', '"customer"'], $tableNames);
}
public function testGetTableNames_isFromObject_generateException()
{
$query = new ActiveQuery(null);
$query->from = new \stdClass;
$this->setExpectedException('\yii\base\InvalidConfigException');
$query->getFromTableNames();
}
public function testGetTablesAlias_notFilledFrom()
{
$query = new ActiveQuery(Profile::className());
$tablesAlias = $query->getFromAliases();
$this->assertEquals([Profile::tableName()], $tablesAlias);
}
public function testGetTablesAlias_isFromArray()
{
$query = new ActiveQuery(null);
$query->from = ['prf' => 'profile', 'usr' => 'user'];
$tablesAlias = $query->getFromAliases();
$this->assertEquals(['prf', 'usr'], $tablesAlias);
}
public function testGetTablesAlias_isFromString()
{
$query = new ActiveQuery(null);
$query->from = 'profile AS \'prf\', user "usr", service srv, order';
$tablesAlias = $query->getFromAliases();
$this->assertEquals(['prf', 'usr', 'srv', 'order'], $tablesAlias);
}
public function testGetTablesAlias_isFromObject_generateException()
{
$query = new ActiveQuery(null);
$query->from = new \stdClass;
$this->setExpectedException('\yii\base\InvalidConfigException');
$query->getFromAliases();
}
}

View File

@ -1,92 +0,0 @@
<?php
namespace yii\helpers;
use yiiunit\TestCase;
use yii\db\ActiveQuery;
use yii\helpers\ActiveQueryHelper;
use yiiunit\data\ar\Profile;
/**
* Generated by PHPUnit_SkeletonGenerator on 2017-03-25 at 03:32:13.
*/
class ActiveQueryHelperTest extends TestCase
{
public function testGetTableNames_notFilledFrom()
{
$query = new ActiveQuery(Profile::className());
$tableNames = ActiveQueryHelper::getTableNames($query);
$this->assertEquals([Profile::tableName()], $tableNames);
}
public function testGetTableNames_isFromArray()
{
$query = new ActiveQuery(null);
$query->from = ['prf' => 'profile', 'usr' => 'user'];
$tableNames = ActiveQueryHelper::getTableNames($query);
$this->assertEquals(['profile', 'user'], $tableNames);
}
public function testGetTableNames_isFromString()
{
$query = new ActiveQuery(null);
$query->from = 'profile AS \'prf\', user "usr", `order`, "customer"';
$tableNames = ActiveQueryHelper::getTableNames($query);
$this->assertEquals(['profile', 'user', '`order`', '"customer"'], $tableNames);
}
public function testGetTableNames_isFromObject_generateException()
{
$query = new ActiveQuery(null);
$query->from = new \stdClass;
$this->setExpectedException('\yii\base\InvalidConfigException');
ActiveQueryHelper::getTableNames($query);
}
public function testGetTablesAlias_notFilledFrom()
{
$query = new ActiveQuery(Profile::className());
$tablesAlias = ActiveQueryHelper::getTablesAlias($query);
$this->assertEquals([Profile::tableName()], $tablesAlias);
}
public function testGetTablesAlias_isFromArray()
{
$query = new ActiveQuery(null);
$query->from = ['prf' => 'profile', 'usr' => 'user'];
$tablesAlias = ActiveQueryHelper::getTablesAlias($query);
$this->assertEquals(['prf', 'usr'], $tablesAlias);
}
public function testGetTablesAlias_isFromString()
{
$query = new ActiveQuery(null);
$query->from = 'profile AS \'prf\', user "usr", service srv, order';
$tablesAlias = ActiveQueryHelper::getTablesAlias($query);
$this->assertEquals(['prf', 'usr', 'srv', 'order'], $tablesAlias);
}
public function testGetTablesAlias_isFromObject_generateException()
{
$query = new ActiveQuery(null);
$query->from = new \stdClass;
$this->setExpectedException('\yii\base\InvalidConfigException');
ActiveQueryHelper::getTablesAlias($query);
}
}