Files
yii2/framework/db/QueryTrait.php
Carsten Brandt 745c20538c Merge pull request #2885 branch 'query-filter'
* query-filter:
  added CHANGELOG lines
  added tests for redis
  added proper tests for elasticsearch
  removed unnecessary code duplication
  adjusted tests
  typo
  renamed Query::filter() to Query::filterWhere()
  reverted elasticsearch rename of filter
  Adjusted search model code generated by Gii CRUD generator
  Added support for arbitrary number of parameters for NOT, AND, OR in filter methods of Query
  Gii CRUD generator now uses new addFilter method
  Query::filter() adjustments
  fixes #2002
  Added tests for Query::filter()

Conflicts:
	framework/db/QueryTrait.php
2014-04-02 19:33:40 +02:00

353 lines
12 KiB
PHP

<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
use yii\base\NotSupportedException;
/**
* The BaseQuery trait represents the minimum method set of a database Query.
*
* It is supposed to be used in a class that implements the [[QueryInterface]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
trait QueryTrait
{
/**
* @var string|array query condition. This refers to the WHERE clause in a SQL statement.
* For example, `age > 31 AND team = 1`.
* @see where()
*/
public $where;
/**
* @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
*/
public $limit;
/**
* @var integer zero-based offset from where the records are to be returned. If not set or
* less than 0, it means starting from the beginning.
*/
public $offset;
/**
* @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [SORT_ASC](http://php.net/manual/en/array.constants.php#constant.sort-asc)
* or [SORT_DESC](http://php.net/manual/en/array.constants.php#constant.sort-desc).
* The array may also contain [[Expression]] objects. If that is the case, the expressions
* will be converted into strings without any change.
*/
public $orderBy;
/**
* @var string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. For more details, see [[indexBy()]]. This property is only used by [[QueryInterface::all()|all()]].
*/
public $indexBy;
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. The signature of the callable should be:
*
* ~~~
* function ($row)
* {
* // return the index value corresponding to $row
* }
* ~~~
*
* @return static the query object itself
*/
public function indexBy($column)
{
$this->indexBy = $column;
return $this;
}
/**
* Sets the WHERE part of the query.
*
* See [[QueryInterface::where()]] for detailed documentation.
*
* @param array $condition the conditions that should be put in the WHERE part.
* @return static the query object itself
* @see andWhere()
* @see orWhere()
*/
public function where($condition)
{
$this->where = $condition;
return $this;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see orWhere()
*/
public function andWhere($condition)
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = ['and', $this->where, $condition];
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see andWhere()
*/
public function orWhere($condition)
{
if ($this->where === null) {
$this->where = $condition;
} else {
$this->where = ['or', $this->where, $condition];
}
return $this;
}
/**
* Sets the WHERE part of the query but ignores [[isParameterNotEmpty|empty parameters]].
*
* This function can be used to pass fields of a search form directly as search condition
* by ignoring fields that have not been filled.
*
* @param array $condition the conditions that should be put in the WHERE part.
* See [[where()]] on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see andFilterWhere()
* @see orFilterWhere()
*/
public function filterWhere($condition)
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->where($condition);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one but ignores [[isParameterNotEmpty|empty parameters]].
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see filterWhere()
* @see orFilterWhere()
*/
public function andFilterWhere($condition)
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->andWhere($condition);
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one but ignores [[isParameterNotEmpty|empty parameters]].
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see filterWhere()
* @see andFilterWhere()
*/
public function orFilterWhere($condition)
{
$condition = $this->filterCondition($condition);
if ($condition !== []) {
$this->orWhere($condition);
}
return $this;
}
/**
* Returns a new condition with [[isParameterNotEmpty|empty parameters]] removed.
*
* @param array $condition original condition
* @return array condition with [[isParameterNotEmpty|empty parameters]] removed.
* @throws NotSupportedException if the condition format is not supported
*/
protected function filterCondition($condition)
{
if (is_array($condition) && isset($condition[0])) {
$operator = strtoupper($condition[0]);
switch ($operator) {
case 'NOT':
case 'AND':
case 'OR':
for ($i = 1, $operandsCount = count($condition); $i < $operandsCount; $i++) {
$subCondition = $this->filterCondition($condition[$i]);
if ($this->isParameterNotEmpty($subCondition)) {
$condition[$i] = $subCondition;
} else {
unset($condition[$i]);
}
}
$operandsCount = count($condition) - 1;
if ($operator === 'NOT' && $operandsCount === 0) {
$condition = [];
} else {
// reindex array
array_splice($condition, 0, 0);
if ($operandsCount === 1) {
$condition = $condition[1];
}
}
break;
case 'IN':
case 'NOT IN':
case 'LIKE':
case 'OR LIKE':
case 'NOT LIKE':
case 'OR NOT LIKE':
if (!$this->isParameterNotEmpty($condition[2])) {
$condition = [];
}
break;
case 'BETWEEN':
case 'NOT BETWEEN':
if (!$this->isParameterNotEmpty($condition[2]) && !$this->isParameterNotEmpty($condition[3])) {
$condition = [];
}
break;
default:
throw new NotSupportedException("filterWhere() does not support the '$operator' operator.");
}
} elseif (is_array($condition)) {
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return array_filter($condition, [$this, 'isParameterNotEmpty']);
} else {
throw new NotSupportedException("filterWhere() does not support plain string conditions use where() instead.");
}
return $condition;
}
/**
* Returns `true` if value passed is not "empty".
*
* The value is considered "empty", if
*
* - it is `null`,
* - an empty string (`''`),
* - a string containing only whitespace characters,
* - or an empty array.
*
* @param $value
* @return boolean if parameter is empty
*/
protected function isParameterNotEmpty($value)
{
if (is_string($value)) {
$value = trim($value);
}
return $value !== '' && $value !== [] && $value !== null;
}
/**
* Sets the ORDER BY part of the query.
* @param string|array $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
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (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.
* @return static the query object itself
* @see addOrderBy()
*/
public function orderBy($columns)
{
$this->orderBy = $this->normalizeOrderBy($columns);
return $this;
}
/**
* Adds additional ORDER BY columns to the query.
* @param string|array $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
* (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return static the query object itself
* @see orderBy()
*/
public function addOrderBy($columns)
{
$columns = $this->normalizeOrderBy($columns);
if ($this->orderBy === null) {
$this->orderBy = $columns;
} else {
$this->orderBy = array_merge($this->orderBy, $columns);
}
return $this;
}
protected function normalizeOrderBy($columns)
{
if (is_array($columns)) {
return $columns;
} else {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
$result = [];
foreach ($columns as $column) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$result[$matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC;
} else {
$result[$column] = SORT_ASC;
}
}
return $result;
}
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit. Use null or negative value to disable limit.
* @return static the query object itself
*/
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset. Use null or negative value to disable offset.
* @return static the query object itself
*/
public function offset($offset)
{
$this->offset = $offset;
return $this;
}
}