added tests and documentation for #8824

This commit is contained in:
Carsten Brandt
2016-01-27 16:26:43 +01:00
parent c3240525df
commit 5a462dc5d2
5 changed files with 142 additions and 8 deletions

View File

@ -69,6 +69,7 @@ Yii Framework 2 Change Log
- Enh #8613: `yii\widgets\FragmentCache` will not store empty content anymore which fixes some problems related to `yii\filters\PageCache` (kidol) - Enh #8613: `yii\widgets\FragmentCache` will not store empty content anymore which fixes some problems related to `yii\filters\PageCache` (kidol)
- Enh #8649: Added total applied migrations to final report (vernik91) - Enh #8649: Added total applied migrations to final report (vernik91)
- Enh #8687: Added support for non-gregorian calendars, e.g. persian, taiwan, islamic to `yii\i18n\Formatter` (cebe, z-avanes, hooman-pro) - Enh #8687: Added support for non-gregorian calendars, e.g. persian, taiwan, islamic to `yii\i18n\Formatter` (cebe, z-avanes, hooman-pro)
- Enh #8824: Allow passing a `yii\db\Expression` to `Query::groupBy()` (cebe)
- Enh #8995: `yii\validators\FileValidator::maxFiles` can be set to `0` to allow unlimited count of files (PowerGamer1, silverfire) - Enh #8995: `yii\validators\FileValidator::maxFiles` can be set to `0` to allow unlimited count of files (PowerGamer1, silverfire)
- Enh #9282: Improved JSON error handling to support PHP 5.5 error codes (freezy-sk) - Enh #9282: Improved JSON error handling to support PHP 5.5 error codes (freezy-sk)
- Enh #9337: Added `yii\db\ColumnSchemaBuilder::defaultExpression()` to support DB Expression as default value (kotchuprik) - Enh #9337: Added `yii\db\ColumnSchemaBuilder::defaultExpression()` to support DB Expression as default value (kotchuprik)
@ -103,7 +104,7 @@ Yii Framework 2 Change Log
- Enh #10319: `yii\helpers\VarDumper::dump()` now respects PHP magic method `__debugInfo()` (klimov-paul) - Enh #10319: `yii\helpers\VarDumper::dump()` now respects PHP magic method `__debugInfo()` (klimov-paul)
- Enh #10359: Support wildcard category name in `yii/console/controllers/MessageController` (rmrevin) - Enh #10359: Support wildcard category name in `yii/console/controllers/MessageController` (rmrevin)
- Enh #10390: Added ability to disable outer tag for `\yii\helpers\BaseHtml::radiolist()`, `::checkboxList()` (TianJinRong, githubjeka, silverfire) - Enh #10390: Added ability to disable outer tag for `\yii\helpers\BaseHtml::radiolist()`, `::checkboxList()` (TianJinRong, githubjeka, silverfire)
- Enh #10535: Allow passing a `yii\db\Expression` to `Query::orderBy()` (andrewnester, cebe) - Enh #10535: Allow passing a `yii\db\Expression` to `Query::orderBy()` and `Query::groupBy()` (andrewnester, cebe)
- Enh: Added last resort measure for `FileHelper::removeDirectory()` fail to unlink symlinks under Windows (samdark) - Enh: Added last resort measure for `FileHelper::removeDirectory()` fail to unlink symlinks under Windows (samdark)
- Chg #9369: `Yii::$app->user->can()` now returns `false` instead of erroring in case `authManager` component is not configured (creocoder) - Chg #9369: `Yii::$app->user->can()` now returns `false` instead of erroring in case `authManager` component is not configured (creocoder)
- Chg #9411: `DetailView` now automatically sets container tag ID in case it's not specified (samdark) - Chg #9411: `DetailView` now automatically sets container tag ID in case it's not specified (samdark)

View File

@ -677,16 +677,24 @@ class Query extends Component implements QueryInterface
/** /**
* Sets the GROUP BY part of the query. * Sets the GROUP BY part of the query.
* @param string|array $columns the columns to be grouped by. * @param string|array|Expression $columns the columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']). * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
*
* Note that if your group-by is an expression containing commas, you should always use an array
* to represent the group-by information. Otherwise, the method will not be able to correctly determine
* the group-by columns.
*
* Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
* @return $this the query object itself * @return $this the query object itself
* @see addGroupBy() * @see addGroupBy()
*/ */
public function groupBy($columns) public function groupBy($columns)
{ {
if (!is_array($columns)) { if ($columns instanceof Expression) {
$columns = [$columns];
} elseif (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
} }
$this->groupBy = $columns; $this->groupBy = $columns;
@ -699,12 +707,20 @@ class Query extends Component implements QueryInterface
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']). * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
*
* Note that if your group-by is an expression containing commas, you should always use an array
* to represent the group-by information. Otherwise, the method will not be able to correctly determine
* the group-by columns.
*
* Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
* @return $this the query object itself * @return $this the query object itself
* @see groupBy() * @see groupBy()
*/ */
public function addGroupBy($columns) public function addGroupBy($columns)
{ {
if (!is_array($columns)) { if ($columns instanceof Expression) {
$columns = [$columns];
} elseif (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
} }
if ($this->groupBy === null) { if ($this->groupBy === null) {
@ -717,7 +733,7 @@ class Query extends Component implements QueryInterface
/** /**
* Sets the HAVING part of the query. * Sets the HAVING part of the query.
* @param string|array $condition the conditions to be put after HAVING. * @param string|array|Expression $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter. * Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query. * @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself * @return $this the query object itself
@ -734,7 +750,7 @@ class Query extends Component implements QueryInterface
/** /**
* Adds an additional HAVING condition to the existing one. * Adds an additional HAVING condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator. * The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new HAVING condition. Please refer to [[where()]] * @param string|array|Expression $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter. * on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query. * @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself * @return $this the query object itself
@ -755,7 +771,7 @@ class Query extends Component implements QueryInterface
/** /**
* Adds an additional HAVING condition to the existing one. * Adds an additional HAVING condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator. * The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new HAVING condition. Please refer to [[where()]] * @param string|array|Expression $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter. * on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query. * @param array $params the parameters (name => value) to be bound to the query.
* @return $this the query object itself * @return $this the query object itself

View File

@ -295,8 +295,10 @@ trait QueryTrait
* @param string|array|Expression $columns the columns (and the directions) to be ordered by. * @param string|array|Expression $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. `"id ASC, name DESC"`) or an array * Columns can be specified in either a string (e.g. `"id ASC, name DESC"`) or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`). * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
*
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
*
* Note that if your order-by is an expression containing commas, you should always use an array * Note that if your order-by is an expression containing commas, you should always use an array
* to represent the order-by information. Otherwise, the method will not be able to correctly determine * to represent the order-by information. Otherwise, the method will not be able to correctly determine
* the order-by columns. * the order-by columns.
@ -316,9 +318,14 @@ trait QueryTrait
* @param string|array|Expression $columns the columns (and the directions) to be ordered by. * @param string|array|Expression $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`). * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
*
* The method will automatically quote the column names unless a column contains some parenthesis * The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression). * (which means the column contains a DB expression).
* *
* Note that if your order-by is an expression containing commas, you should always use an array
* to represent the order-by information. Otherwise, the method will not be able to correctly determine
* the order-by columns.
*
* Since version 2.0.7, an [[Expression]] object can be passed to specify the ORDER BY part explicitly in plain SQL. * Since version 2.0.7, an [[Expression]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
* @return $this the query object itself * @return $this the query object itself
* @see orderBy() * @see orderBy()

View File

@ -222,6 +222,33 @@ SQL;
$this->assertEquals('user5@example.com', $command->queryScalar()); $this->assertEquals('user5@example.com', $command->queryScalar());
} }
public function paramsNonWhereProvider()
{
return[
['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email GROUP BY SUBSTR(name, :len)'],
['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email ORDER BY SUBSTR(name, :len)'],
['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email'],
];
}
/**
* Test whether param binding works in other places than WHERE
* @dataProvider paramsNonWhereProvider
*/
public function testBindParamsNonWhere($sql)
{
$db = $this->getConnection();
$db->createCommand()->insert('customer', ['name' => 'testParams', 'email' => 'testParams@example.com', 'address' => '1'])->execute();
$params = [
':email' => 'testParams@example.com',
':len' => 5,
];
$command = $db->createCommand($sql, $params);
$this->assertEquals('Params', $command->queryScalar());
}
public function testFetchMode() public function testFetchMode()
{ {
$db = $this->getConnection(); $db = $this->getConnection();

View File

@ -536,6 +536,15 @@ class QueryBuilderTest extends DatabaseTestCase
$expected = $this->replaceQuotes("SELECT 1 AS ab, 2 AS cd, 3 AS [[ef]] FROM [[tablename]]"); $expected = $this->replaceQuotes("SELECT 1 AS ab, 2 AS cd, 3 AS [[ef]] FROM [[tablename]]");
$this->assertEquals($expected, $sql); $this->assertEquals($expected, $sql);
$this->assertEmpty($params); $this->assertEmpty($params);
$query = (new Query())
->select(new Expression("SUBSTR(name, 0, :len)", [':len' => 4]))
->from('tablename');
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes("SELECT SUBSTR(name, 0, :len) FROM [[tablename]]");
$this->assertEquals($expected, $sql);
$this->assertEquals([':len' => 4], $params);
} }
public function testCompositeInCondition() public function testCompositeInCondition()
@ -586,15 +595,89 @@ class QueryBuilderTest extends DatabaseTestCase
public function testOrderBy() public function testOrderBy()
{ {
// simple string
$query = (new Query())
->select('*')
->from('operations')
->orderBy('name ASC, date DESC');
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] ORDER BY `name`, `date` DESC');
$this->assertEquals($expected, $sql);
$this->assertEmpty($params);
// array syntax
$query = (new Query())
->select('*')
->from('operations')
->orderBy(['name' => SORT_ASC, 'date' => SORT_DESC]);
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] ORDER BY `name`, `date` DESC');
$this->assertEquals($expected, $sql);
$this->assertEmpty($params);
// expression
$query = (new Query()) $query = (new Query())
->select('*') ->select('*')
->from('operations') ->from('operations')
->where('account_id = accounts.id') ->where('account_id = accounts.id')
->orderBy(new Expression('SUBSTR(name, 3, 4) DESC, x ASC')); ->orderBy(new Expression('SUBSTR(name, 3, 4) DESC, x ASC'));
list ($sql, $params) = $this->getQueryBuilder()->build($query); list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] WHERE account_id = accounts.id ORDER BY SUBSTR(name, 3, 4) DESC, x ASC'); $expected = $this->replaceQuotes('SELECT * FROM [[operations]] WHERE account_id = accounts.id ORDER BY SUBSTR(name, 3, 4) DESC, x ASC');
$this->assertEquals($expected, $sql); $this->assertEquals($expected, $sql);
$this->assertEmpty($params); $this->assertEmpty($params);
// expression with params
$query = (new Query())
->select('*')
->from('operations')
->orderBy(new Expression('SUBSTR(name, 3, :to) DESC, x ASC', [':to' => 4]));
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] ORDER BY SUBSTR(name, 3, :to) DESC, x ASC');
$this->assertEquals($expected, $sql);
$this->assertEquals([':to' => 4], $params);
}
public function testGroupBy()
{
// simple string
$query = (new Query())
->select('*')
->from('operations')
->groupBy('name, date');
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] GROUP BY `name`, `date`');
$this->assertEquals($expected, $sql);
$this->assertEmpty($params);
// array syntax
$query = (new Query())
->select('*')
->from('operations')
->groupBy(['name', 'date']);
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] GROUP BY `name`, `date`');
$this->assertEquals($expected, $sql);
$this->assertEmpty($params);
// expression
$query = (new Query())
->select('*')
->from('operations')
->where('account_id = accounts.id')
->groupBy(new Expression('SUBSTR(name, 0, 1), x'));
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] WHERE account_id = accounts.id GROUP BY SUBSTR(name, 0, 1), x');
$this->assertEquals($expected, $sql);
$this->assertEmpty($params);
// expression with params
$query = (new Query())
->select('*')
->from('operations')
->groupBy(new Expression('SUBSTR(name, 0, :to), x', [':to' => 4]));
list ($sql, $params) = $this->getQueryBuilder()->build($query);
$expected = $this->replaceQuotes('SELECT * FROM [[operations]] GROUP BY SUBSTR(name, 0, :to), x');
$this->assertEquals($expected, $sql);
$this->assertEquals([':to' => 4], $params);
} }
} }