Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
eb1b5578
Commit
eb1b5578
authored
Oct 07, 2014
by
Klimov Paul
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
`yii\sphinx\Query` updated to be more consistent with `yii\db\Query`
parent
df82aabe
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
165 additions
and
443 deletions
+165
-443
Query.php
extensions/sphinx/Query.php
+57
-418
QueryBuilder.php
extensions/sphinx/QueryBuilder.php
+92
-25
QueryTest.php
tests/unit/extensions/sphinx/QueryTest.php
+16
-0
No files found.
extensions/sphinx/Query.php
View file @
eb1b5578
...
@@ -8,10 +8,9 @@
...
@@ -8,10 +8,9 @@
namespace
yii\sphinx
;
namespace
yii\sphinx
;
use
Yii
;
use
Yii
;
use
yii\base\Component
;
use
yii\base\InvalidCallException
;
use
yii\base\InvalidCallException
;
use
yii\base\NotSupportedException
;
use
yii\db\Expression
;
use
yii\db\Expression
;
use
yii\db\QueryInterface
;
use
yii\db\QueryTrait
;
use
yii\db\QueryTrait
;
/**
/**
...
@@ -46,31 +45,8 @@ use yii\db\QueryTrait;
...
@@ -46,31 +45,8 @@ use yii\db\QueryTrait;
* @author Paul Klimov <klimov.paul@gmail.com>
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
* @since 2.0
*/
*/
class
Query
extends
Component
implements
QueryInterface
class
Query
extends
\yii\db\Query
{
{
use
QueryTrait
;
/**
* @var array the columns being selected. For example, `['id', 'group_id']`.
* This is used to construct the SELECT clause in a SQL statement. If not set, if means selecting all columns.
* @see select()
*/
public
$select
;
/**
* @var string additional option that should be appended to the 'SELECT' keyword.
*/
public
$selectOption
;
/**
* @var boolean whether to select distinct rows of data only. If this is set true,
* the SELECT clause would be changed to SELECT DISTINCT.
*/
public
$distinct
;
/**
* @var array the index(es) to be selected from. For example, `['idx_user', 'idx_user_delta']`.
* This is used to construct the FROM clause in a SQL statement.
* @see from()
*/
public
$from
;
/**
/**
* @var string|Expression text, which should be searched in fulltext mode.
* @var string|Expression text, which should be searched in fulltext mode.
* This value will be composed into MATCH operator inside the WHERE clause.
* This value will be composed into MATCH operator inside the WHERE clause.
...
@@ -80,11 +56,6 @@ class Query extends Component implements QueryInterface
...
@@ -80,11 +56,6 @@ class Query extends Component implements QueryInterface
*/
*/
public
$match
;
public
$match
;
/**
/**
* @var array how to group the query results. For example, `['company', 'department']`.
* This is used to construct the GROUP BY clause in a SQL statement.
*/
public
$groupBy
;
/**
* @var string WITHIN GROUP ORDER BY clause. This is a Sphinx specific extension
* @var string WITHIN GROUP ORDER BY clause. This is a Sphinx specific extension
* that lets you control how the best row within a group will to be selected.
* that lets you control how the best row within a group will to be selected.
* The possible value matches the [[orderBy]] one.
* The possible value matches the [[orderBy]] one.
...
@@ -97,11 +68,6 @@ class Query extends Component implements QueryInterface
...
@@ -97,11 +68,6 @@ class Query extends Component implements QueryInterface
*/
*/
public
$options
;
public
$options
;
/**
/**
* @var array list of query parameter values indexed by parameter placeholders.
* For example, `[':name' => 'Dan', ':age' => 31]`.
*/
public
$params
=
[];
/**
* @var callable PHP callback, which should be used to fetch source data for the snippets.
* @var callable PHP callback, which should be used to fetch source data for the snippets.
* Such callback will receive array of query result rows as an argument and must return the
* Such callback will receive array of query result rows as an argument and must return the
* array of snippet source strings in the order, which match one of incoming rows.
* array of snippet source strings in the order, which match one of incoming rows.
...
@@ -165,55 +131,33 @@ class Query extends Component implements QueryInterface
...
@@ -165,55 +131,33 @@ class Query extends Component implements QueryInterface
/**
/**
* Creates a Sphinx command that can be used to execute this query.
* Creates a Sphinx command that can be used to execute this query.
* @param Connection $
connection
the Sphinx connection used to generate the SQL statement.
* @param Connection $
db
the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* If this parameter is not given, the `sphinx` application component will be used.
* @return Command the created Sphinx command instance.
* @return Command the created Sphinx command instance.
*/
*/
public
function
createCommand
(
$
connection
=
null
)
public
function
createCommand
(
$
db
=
null
)
{
{
$this
->
setConnection
(
$
connection
);
$this
->
setConnection
(
$
db
);
$
connection
=
$this
->
getConnection
();
$
db
=
$this
->
getConnection
();
list
(
$sql
,
$params
)
=
$
connection
->
getQueryBuilder
()
->
build
(
$this
);
list
(
$sql
,
$params
)
=
$
db
->
getQueryBuilder
()
->
build
(
$this
);
return
$
connection
->
createCommand
(
$sql
,
$params
);
return
$
db
->
createCommand
(
$sql
,
$params
);
}
}
/**
/**
* Executes the query and returns all results as an array.
* @inheritdoc
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return array the query results. If the query results in nothing, an empty array will be returned.
*/
*/
public
function
all
(
$db
=
null
)
public
function
populate
(
$rows
)
{
{
$rows
=
$this
->
createCommand
(
$db
)
->
queryAll
();
return
parent
::
populate
(
$this
->
fillUpSnippets
(
$rows
));
$rows
=
$this
->
fillUpSnippets
(
$rows
);
if
(
$this
->
indexBy
===
null
)
{
return
$rows
;
}
$result
=
[];
foreach
(
$rows
as
$row
)
{
if
(
is_string
(
$this
->
indexBy
))
{
$key
=
$row
[
$this
->
indexBy
];
}
else
{
$key
=
call_user_func
(
$this
->
indexBy
,
$row
);
}
$result
[
$key
]
=
$row
;
}
return
$result
;
}
}
/**
/**
* Executes the query and returns a single row of result.
* @inheritdoc
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query
* results in nothing.
*/
*/
public
function
one
(
$db
=
null
)
public
function
one
(
$db
=
null
)
{
{
$row
=
$this
->
createCommand
(
$db
)
->
queryOne
(
);
$row
=
parent
::
one
(
$db
);
if
(
$row
!==
false
)
{
if
(
$row
!==
false
)
{
list
(
$row
)
=
$this
->
fillUpSnippets
([
$row
]);
list
(
$row
)
=
$this
->
fillUpSnippets
([
$row
]);
}
}
...
@@ -222,168 +166,6 @@ class Query extends Component implements QueryInterface
...
@@ -222,168 +166,6 @@ class Query extends Component implements QueryInterface
}
}
/**
/**
* Returns the query result as a scalar value.
* The value returned will be the first column in the first row of the query results.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return string|boolean the value of the first column in the first row of the query result.
* False is returned if the query result is empty.
*/
public
function
scalar
(
$db
=
null
)
{
return
$this
->
createCommand
(
$db
)
->
queryScalar
();
}
/**
* Executes the query and returns the first column of the result.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return array the first column of the query result. An empty array is returned if the query results in nothing.
*/
public
function
column
(
$db
=
null
)
{
return
$this
->
createCommand
(
$db
)
->
queryColumn
();
}
/**
* Returns the number of records.
* @param string $q the COUNT expression. Defaults to '*'.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return integer number of records
*/
public
function
count
(
$q
=
'*'
,
$db
=
null
)
{
$this
->
select
=
[
"COUNT(
$q
)"
];
return
$this
->
createCommand
(
$db
)
->
queryScalar
();
}
/**
* Returns the sum of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return integer the sum of the specified column values
*/
public
function
sum
(
$q
,
$db
=
null
)
{
$this
->
select
=
[
"SUM(
$q
)"
];
return
$this
->
createCommand
(
$db
)
->
queryScalar
();
}
/**
* Returns the average of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return integer the average of the specified column values.
*/
public
function
average
(
$q
,
$db
=
null
)
{
$this
->
select
=
[
"AVG(
$q
)"
];
return
$this
->
createCommand
(
$db
)
->
queryScalar
();
}
/**
* Returns the minimum of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return integer the minimum of the specified column values.
*/
public
function
min
(
$q
,
$db
=
null
)
{
$this
->
select
=
[
"MIN(
$q
)"
];
return
$this
->
createCommand
(
$db
)
->
queryScalar
();
}
/**
* Returns the maximum of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return integer the maximum of the specified column values.
*/
public
function
max
(
$q
,
$db
=
null
)
{
$this
->
select
=
[
"MAX(
$q
)"
];
return
$this
->
createCommand
(
$db
)
->
queryScalar
();
}
/**
* Returns a value indicating whether the query result contains any row of data.
* @param Connection $db the Sphinx connection used to generate the SQL statement.
* If this parameter is not given, the `sphinx` application component will be used.
* @return boolean whether the query result contains any row of data.
*/
public
function
exists
(
$db
=
null
)
{
$this
->
select
=
[
new
Expression
(
'1'
)];
return
$this
->
scalar
(
$db
)
!==
false
;
}
/**
* Sets the SELECT part of the query.
* @param string|array $columns the columns to be selected.
* 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
* (which means the column contains a Sphinx expression).
* @param string $option additional option that should be appended to the 'SELECT' keyword.
* @return static the query object itself
*/
public
function
select
(
$columns
,
$option
=
null
)
{
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
select
=
$columns
;
$this
->
selectOption
=
$option
;
return
$this
;
}
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param boolean $value whether to SELECT DISTINCT or not.
* @return static the query object itself
*/
public
function
distinct
(
$value
=
true
)
{
$this
->
distinct
=
$value
;
return
$this
;
}
/**
* Sets the FROM part of the query.
* @param string|array $tables the table(s) to be selected from. This can be either a string (e.g. `'idx_user'`)
* or an array (e.g. `['idx_user', 'idx_user_delta']`) specifying one or several index names.
* The method will automatically quote the table names unless it contains some parenthesis
* (which means the table is given as a sub-query or Sphinx expression).
* @return static the query object itself
*/
public
function
from
(
$tables
)
{
if
(
!
is_array
(
$tables
))
{
$tables
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$tables
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
from
=
$tables
;
return
$this
;
}
/**
* Sets the fulltext query text. This text will be composed into
* Sets the fulltext query text. This text will be composed into
* MATCH operator inside the WHERE clause.
* MATCH operator inside the WHERE clause.
* Note: this value will be processed by [[Connection::escapeMatchValue()]],
* Note: this value will be processed by [[Connection::escapeMatchValue()]],
...
@@ -405,210 +187,35 @@ class Query extends Component implements QueryInterface
...
@@ -405,210 +187,35 @@ class Query extends Component implements QueryInterface
}
}
/**
/**
* Sets the WHERE part of the query.
* @inheritdoc
*
* The method requires a $condition parameter, and optionally a $params parameter
* specifying the values to be bound to the query.
*
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
* - hash format: `['column1' => value1, 'column2' => value2, ...]`
* - operator format: `[operator, operand1, operand2, ...]`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array or a Query object,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`.
* - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`.
* - `['status' => null] generates `status IS NULL`.
* - `['id' => $query]` generates `id IN (...sub-query...)`
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression with parenthesis. Operand 2 can be an array
* or a Query object. If the former, the array represents the range of the values that the column
* or DB expression should be in. If the latter, a sub-query will be generated to represent the range.
* For example, `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`;
* `['in', 'id', (new Query)->select('id')->from('user'))]` will generate
* `id IN (SELECT id FROM user)`. The method will properly quote the column name and escape values in the range.
* The `in` operator also supports composite columns. In this case, operand 1 should be an array of the columns,
* while operand 2 should be an array of arrays or a `Query` object representing the range of the columns.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
* Sometimes, you may want to add the percentage characters to the matching value by yourself, you may supply
* a third operand `false` to do so. For example, `['like', 'name', '%tester', false]` will generate `name LIKE '%tester'`.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name => value) to be bound to the query.
* @return static the query object itself
* @see andWhere()
* @see orWhere()
*/
public
function
where
(
$condition
,
$params
=
[])
{
$this
->
where
=
$condition
;
$this
->
addParams
(
$params
);
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.
* @param array $params the parameters (name => value) to be bound to the query.
* @return static the query object itself
* @see where()
* @see orWhere()
*/
public
function
andWhere
(
$condition
,
$params
=
[])
{
if
(
$this
->
where
===
null
)
{
$this
->
where
=
$condition
;
}
else
{
$this
->
where
=
[
'and'
,
$this
->
where
,
$condition
];
}
$this
->
addParams
(
$params
);
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.
* @param array $params the parameters (name => value) to be bound to the query.
* @return static the query object itself
* @see where()
* @see andWhere()
*/
*/
public
function
orWhere
(
$condition
,
$params
=
[])
public
function
join
(
$type
,
$table
,
$on
=
''
,
$params
=
[])
{
{
if
(
$this
->
where
===
null
)
{
throw
new
NotSupportedException
(
'"'
.
__METHOD__
.
'" is not supported.'
);
$this
->
where
=
$condition
;
}
else
{
$this
->
where
=
[
'or'
,
$this
->
where
,
$condition
];
}
$this
->
addParams
(
$params
);
return
$this
;
}
/**
* Sets the GROUP BY part of the query.
* @param string|array $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']).
* 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 addGroupBy()
*/
public
function
groupBy
(
$columns
)
{
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
groupBy
=
$columns
;
return
$this
;
}
}
/**
/**
* Adds additional group-by columns to the existing ones.
* @inheritdoc
* @param string|array $columns additional columns to be grouped by.
* 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
* (which means the column contains a DB expression).
* @return static the query object itself
* @see groupBy()
*/
*/
public
function
addGroupBy
(
$columns
)
public
function
innerJoin
(
$table
,
$on
=
''
,
$params
=
[]
)
{
{
if
(
!
is_array
(
$columns
))
{
throw
new
NotSupportedException
(
'"'
.
__METHOD__
.
'" is not supported.'
);
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
if
(
$this
->
groupBy
===
null
)
{
$this
->
groupBy
=
$columns
;
}
else
{
$this
->
groupBy
=
array_merge
(
$this
->
groupBy
,
$columns
);
}
return
$this
;
}
}
/**
/**
* Sets the parameters to be bound to the query.
* @inheritdoc
* @param array $params list of query parameter values indexed by parameter placeholders.
* For example, `[':name' => 'Dan', ':age' => 31]`.
* @return static the query object itself
* @see addParams()
*/
*/
public
function
params
(
$params
)
public
function
leftJoin
(
$table
,
$on
=
''
,
$params
=
[]
)
{
{
$this
->
params
=
$params
;
throw
new
NotSupportedException
(
'"'
.
__METHOD__
.
'" is not supported.'
);
return
$this
;
}
}
/**
/**
* Adds additional parameters to be bound to the query.
* @inheritdoc
* @param array $params list of query parameter values indexed by parameter placeholders.
* For example, `[':name' => 'Dan', ':age' => 31]`.
* @return static the query object itself
* @see params()
*/
*/
public
function
addParams
(
$params
)
public
function
rightJoin
(
$table
,
$on
=
''
,
$params
=
[]
)
{
{
if
(
!
empty
(
$params
))
{
throw
new
NotSupportedException
(
'"'
.
__METHOD__
.
'" is not supported.'
);
if
(
empty
(
$this
->
params
))
{
$this
->
params
=
$params
;
}
else
{
foreach
(
$params
as
$name
=>
$value
)
{
if
(
is_integer
(
$name
))
{
$this
->
params
[]
=
$value
;
}
else
{
$this
->
params
[
$name
]
=
$value
;
}
}
}
}
return
$this
;
}
}
/**
/**
...
@@ -759,4 +366,36 @@ class Query extends Component implements QueryInterface
...
@@ -759,4 +366,36 @@ class Query extends Component implements QueryInterface
->
callSnippets
(
$from
,
$source
,
$match
,
$this
->
snippetOptions
)
->
callSnippets
(
$from
,
$source
,
$match
,
$this
->
snippetOptions
)
->
queryColumn
();
->
queryColumn
();
}
}
/**
* Creates a new Query object and copies its property values from an existing one.
* The properties being copies are the ones to be used by query builders.
* @param Query $from the source query object
* @return Query the new Query object
*/
public
static
function
create
(
$from
)
{
return
new
self
([
'where'
=>
$from
->
where
,
'limit'
=>
$from
->
limit
,
'offset'
=>
$from
->
offset
,
'orderBy'
=>
$from
->
orderBy
,
'indexBy'
=>
$from
->
indexBy
,
'select'
=>
$from
->
select
,
'selectOption'
=>
$from
->
selectOption
,
'distinct'
=>
$from
->
distinct
,
'from'
=>
$from
->
from
,
'groupBy'
=>
$from
->
groupBy
,
'join'
=>
$from
->
join
,
'having'
=>
$from
->
having
,
'union'
=>
$from
->
union
,
'params'
=>
$from
->
params
,
// Sphinx specifics :
'options'
=>
$from
->
options
,
'within'
=>
$from
->
within
,
'match'
=>
$from
->
match
,
'snippetCallback'
=>
$from
->
snippetCallback
,
'snippetOptions'
=>
$from
->
snippetOptions
,
]);
}
}
}
extensions/sphinx/QueryBuilder.php
View file @
eb1b5578
...
@@ -8,6 +8,7 @@
...
@@ -8,6 +8,7 @@
namespace
yii\sphinx
;
namespace
yii\sphinx
;
use
yii\base\InvalidParamException
;
use
yii\base\InvalidParamException
;
use
yii\base\NotSupportedException
;
use
yii\base\Object
;
use
yii\base\Object
;
use
yii\db\Exception
;
use
yii\db\Exception
;
use
yii\db\Expression
;
use
yii\db\Expression
;
...
@@ -38,6 +39,24 @@ class QueryBuilder extends Object
...
@@ -38,6 +39,24 @@ class QueryBuilder extends Object
*/
*/
public
$separator
=
" "
;
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.
* Constructor.
...
@@ -55,12 +74,19 @@ class QueryBuilder extends Object
...
@@ -55,12 +74,19 @@ class QueryBuilder extends Object
* @param Query $query the [[Query]] object from which the SQL statement will be generated
* @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
* @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.
* 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
* @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
* parameters to be bound to the SQL statement (the second array element). The parameters returned
* include those provided in `$params`.
* include those provided in `$params`.
*/
*/
public
function
build
(
$query
,
$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
);
$params
=
empty
(
$params
)
?
$query
->
params
:
array_merge
(
$params
,
$query
->
params
);
$from
=
$query
->
from
;
$from
=
$query
->
from
;
...
@@ -76,6 +102,7 @@ class QueryBuilder extends Object
...
@@ -76,6 +102,7 @@ class QueryBuilder extends Object
$this
->
buildWhere
(
$query
->
from
,
$query
->
where
,
$params
,
$query
->
match
),
$this
->
buildWhere
(
$query
->
from
,
$query
->
where
,
$params
,
$query
->
match
),
$this
->
buildGroupBy
(
$query
->
groupBy
),
$this
->
buildGroupBy
(
$query
->
groupBy
),
$this
->
buildWithin
(
$query
->
within
),
$this
->
buildWithin
(
$query
->
within
),
$this
->
buildHaving
(
$query
->
from
,
$query
->
having
,
$params
),
$this
->
buildOrderBy
(
$query
->
orderBy
),
$this
->
buildOrderBy
(
$query
->
orderBy
),
$this
->
buildLimit
(
$query
->
limit
,
$query
->
offset
),
$this
->
buildLimit
(
$query
->
limit
,
$query
->
offset
),
$this
->
buildOption
(
$query
->
options
,
$params
),
$this
->
buildOption
(
$query
->
options
,
$params
),
...
@@ -501,15 +528,7 @@ class QueryBuilder extends Object
...
@@ -501,15 +528,7 @@ class QueryBuilder extends Object
if
(
empty
(
$condition
))
{
if
(
empty
(
$condition
))
{
return
''
;
return
''
;
}
}
$indexSchemas
=
[];
$indexSchemas
=
$this
->
getIndexSchemas
(
$indexes
);
if
(
!
empty
(
$indexes
))
{
foreach
(
$indexes
as
$indexName
)
{
$index
=
$this
->
db
->
getIndexSchema
(
$indexName
);
if
(
$index
!==
null
)
{
$indexSchemas
[]
=
$index
;
}
}
}
$where
=
$this
->
buildCondition
(
$indexSchemas
,
$condition
,
$params
);
$where
=
$this
->
buildCondition
(
$indexSchemas
,
$condition
,
$params
);
return
$where
===
''
?
''
:
'WHERE '
.
$where
;
return
$where
===
''
?
''
:
'WHERE '
.
$where
;
...
@@ -525,6 +544,24 @@ class QueryBuilder extends Object
...
@@ -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.
* 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 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.
* @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
...
@@ -623,19 +660,6 @@ class QueryBuilder extends Object
*/
*/
public
function
buildCondition
(
$indexes
,
$condition
,
&
$params
)
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
))
{
if
(
!
is_array
(
$condition
))
{
return
(
string
)
$condition
;
return
(
string
)
$condition
;
}
elseif
(
empty
(
$condition
))
{
}
elseif
(
empty
(
$condition
))
{
...
@@ -643,15 +667,14 @@ class QueryBuilder extends Object
...
@@ -643,15 +667,14 @@ class QueryBuilder extends Object
}
}
if
(
isset
(
$condition
[
0
]))
{
// operator format: operator, operand 1, operand 2, ...
if
(
isset
(
$condition
[
0
]))
{
// operator format: operator, operand 1, operand 2, ...
$operator
=
strtoupper
(
$condition
[
0
]);
$operator
=
strtoupper
(
$condition
[
0
]);
if
(
isset
(
$
b
uilders
[
$operator
]))
{
if
(
isset
(
$
this
->
conditionB
uilders
[
$operator
]))
{
$method
=
$
b
uilders
[
$operator
];
$method
=
$
this
->
conditionB
uilders
[
$operator
];
}
else
{
}
else
{
$method
=
'buildSimpleCondition'
;
$method
=
'buildSimpleCondition'
;
}
}
array_shift
(
$condition
);
array_shift
(
$condition
);
return
$this
->
$method
(
$indexes
,
$operator
,
$condition
,
$params
);
return
$this
->
$method
(
$indexes
,
$operator
,
$condition
,
$params
);
}
else
{
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
}
else
{
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return
$this
->
buildHashCondition
(
$indexes
,
$condition
,
$params
);
return
$this
->
buildHashCondition
(
$indexes
,
$condition
,
$params
);
}
}
}
}
...
@@ -714,6 +737,32 @@ class QueryBuilder extends Object
...
@@ -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.
* Creates an SQL expressions with the `BETWEEN` operator.
* @param IndexSchema[] $indexes list of indexes, which affected by query
* @param IndexSchema[] $indexes list of indexes, which affected by query
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
...
@@ -1042,4 +1091,22 @@ class QueryBuilder extends Object
...
@@ -1042,4 +1091,22 @@ class QueryBuilder extends Object
return
"
$column
$operator
$phName
"
;
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
;
}
}
}
tests/unit/extensions/sphinx/QueryTest.php
View file @
eb1b5578
...
@@ -132,6 +132,22 @@ class QueryTest extends SphinxTestCase
...
@@ -132,6 +132,22 @@ class QueryTest extends SphinxTestCase
$this
->
assertEquals
([
'team'
,
'company'
,
'age'
],
$query
->
groupBy
);
$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
()
public
function
testOrder
()
{
{
$query
=
new
Query
;
$query
=
new
Query
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment