Commit eb1b5578 by Klimov Paul

`yii\sphinx\Query` updated to be more consistent with `yii\db\Query`

parent df82aabe
......@@ -8,6 +8,7 @@
namespace yii\sphinx;
use yii\base\InvalidParamException;
use yii\base\NotSupportedException;
use yii\base\Object;
use yii\db\Exception;
use yii\db\Expression;
......@@ -38,6 +39,24 @@ class QueryBuilder extends Object
*/
public $separator = " ";
/**
* @var array map of query condition to builder methods.
* These methods are used by [[buildCondition]] to build SQL conditions from array syntax.
*/
protected $conditionBuilders = [
'AND' => 'buildAndCondition',
'OR' => 'buildAndCondition',
'BETWEEN' => 'buildBetweenCondition',
'NOT BETWEEN' => 'buildBetweenCondition',
'IN' => 'buildInCondition',
'NOT IN' => 'buildInCondition',
'LIKE' => 'buildLikeCondition',
'NOT LIKE' => 'buildLikeCondition',
'OR LIKE' => 'buildLikeCondition',
'OR NOT LIKE' => 'buildLikeCondition',
'NOT' => 'buildNotCondition',
];
/**
* Constructor.
......@@ -55,12 +74,19 @@ class QueryBuilder extends Object
* @param Query $query the [[Query]] object from which the SQL statement will be generated
* @param array $params the parameters to be bound to the generated SQL statement. These parameters will
* be included in the result with the additional parameters generated during the query building process.
* @throws NotSupportedException if query contains 'join' option.
* @return array the generated SQL statement (the first array element) and the corresponding
* parameters to be bound to the SQL statement (the second array element). The parameters returned
* include those provided in `$params`.
*/
public function build($query, $params = [])
{
$query = $query->prepare($this);
if (!empty($query->join)) {
throw new NotSupportedException('Build of "' . get_class($query) . '::join" is not supported.');
}
$params = empty($params) ? $query->params : array_merge($params, $query->params);
$from = $query->from;
......@@ -76,6 +102,7 @@ class QueryBuilder extends Object
$this->buildWhere($query->from, $query->where, $params, $query->match),
$this->buildGroupBy($query->groupBy),
$this->buildWithin($query->within),
$this->buildHaving($query->from, $query->having, $params),
$this->buildOrderBy($query->orderBy),
$this->buildLimit($query->limit, $query->offset),
$this->buildOption($query->options, $params),
......@@ -501,15 +528,7 @@ class QueryBuilder extends Object
if (empty($condition)) {
return '';
}
$indexSchemas = [];
if (!empty($indexes)) {
foreach ($indexes as $indexName) {
$index = $this->db->getIndexSchema($indexName);
if ($index !== null) {
$indexSchemas[] = $index;
}
}
}
$indexSchemas = $this->getIndexSchemas($indexes);
$where = $this->buildCondition($indexSchemas, $condition, $params);
return $where === '' ? '' : 'WHERE ' . $where;
......@@ -525,6 +544,24 @@ class QueryBuilder extends Object
}
/**
* @param string[] $indexes list of index names, which affected by query
* @param string|array $condition
* @param array $params the binding parameters to be populated
* @return string the HAVING clause built from [[Query::$having]].
*/
public function buildHaving($indexes, $condition, &$params)
{
if (empty($condition)) {
return '';
}
$indexSchemas = $this->getIndexSchemas($indexes);
$having = $this->buildCondition($indexSchemas, $condition, $params);
return $having === '' ? '' : 'HAVING ' . $having;
}
/**
* Builds the ORDER BY and LIMIT/OFFSET clauses and appends them to the given SQL.
* @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
* @param array $orderBy the order by columns. See [[Query::orderBy]] for more details on how to specify this parameter.
......@@ -623,19 +660,6 @@ class QueryBuilder extends Object
*/
public function buildCondition($indexes, $condition, &$params)
{
static $builders = [
'AND' => 'buildAndCondition',
'OR' => 'buildAndCondition',
'BETWEEN' => 'buildBetweenCondition',
'NOT BETWEEN' => 'buildBetweenCondition',
'IN' => 'buildInCondition',
'NOT IN' => 'buildInCondition',
'LIKE' => 'buildLikeCondition',
'NOT LIKE' => 'buildLikeCondition',
'OR LIKE' => 'buildLikeCondition',
'OR NOT LIKE' => 'buildLikeCondition',
];
if (!is_array($condition)) {
return (string) $condition;
} elseif (empty($condition)) {
......@@ -643,15 +667,14 @@ class QueryBuilder extends Object
}
if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
$operator = strtoupper($condition[0]);
if (isset($builders[$operator])) {
$method = $builders[$operator];
if (isset($this->conditionBuilders[$operator])) {
$method = $this->conditionBuilders[$operator];
} else {
$method = 'buildSimpleCondition';
}
array_shift($condition);
return $this->$method($indexes, $operator, $condition, $params);
} else { // hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return $this->buildHashCondition($indexes, $condition, $params);
}
}
......@@ -714,6 +737,32 @@ class QueryBuilder extends Object
}
/**
* Inverts an SQL expressions with `NOT` operator.
* @param IndexSchema[] $indexes list of indexes, which affected by query
* @param string $operator the operator to use for connecting the given operands
* @param array $operands the SQL expressions to connect.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
* @throws InvalidParamException if wrong number of operands have been given.
*/
public function buildNotCondition($indexes, $operator, $operands, &$params)
{
if (count($operands) != 1) {
throw new InvalidParamException("Operator '$operator' requires exactly one operand.");
}
$operand = reset($operands);
if (is_array($operand)) {
$operand = $this->buildCondition($indexes, $operand, $params);
}
if ($operand === '') {
return '';
}
return "$operator ($operand)";
}
/**
* Creates an SQL expressions with the `BETWEEN` operator.
* @param IndexSchema[] $indexes list of indexes, which affected by query
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
......@@ -1042,4 +1091,22 @@ class QueryBuilder extends Object
return "$column $operator $phName";
}
}
/**
* @param array $indexes index names.
* @return IndexSchema[] index schemas.
*/
private function getIndexSchemas($indexes)
{
$indexSchemas = [];
if (!empty($indexes)) {
foreach ($indexes as $indexName) {
$index = $this->db->getIndexSchema($indexName);
if ($index !== null) {
$indexSchemas[] = $index;
}
}
}
return $indexSchemas;
}
}
......@@ -132,6 +132,22 @@ class QueryTest extends SphinxTestCase
$this->assertEquals(['team', 'company', 'age'], $query->groupBy);
}
public function testHaving()
{
$query = new Query;
$query->having('id = :id', [':id' => 1]);
$this->assertEquals('id = :id', $query->having);
$this->assertEquals([':id' => 1], $query->params);
$query->andHaving('name = :name', [':name' => 'something']);
$this->assertEquals(['and', 'id = :id', 'name = :name'], $query->having);
$this->assertEquals([':id' => 1, ':name' => 'something'], $query->params);
$query->orHaving('age = :age', [':age' => '30']);
$this->assertEquals(['or', ['and', 'id = :id', 'name = :name'], 'age = :age'], $query->having);
$this->assertEquals([':id' => 1, ':name' => 'something', ':age' => '30'], $query->params);
}
public function testOrder()
{
$query = new Query;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment