mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			1015 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			1015 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
Query Builder
 | 
						||
=============
 | 
						||
 | 
						||
Built on top of [Database Access Objects](db-dao.md), query builder allows you to construct a SQL query
 | 
						||
in a programmatic and DBMS-agnostic way. Compared to writing raw SQL statements, using query builder will help you write 
 | 
						||
more readable SQL-related code and generate more secure SQL statements.  
 | 
						||
 | 
						||
Using query builder usually involves two steps:
 | 
						||
 | 
						||
1. Build a [[yii\db\Query]] object to represent different parts (e.g. `SELECT`, `FROM`) of a SELECT SQL statement.
 | 
						||
2. Execute a query method (e.g. `all()`) of [[yii\db\Query]] to retrieve data from the database.
 | 
						||
 | 
						||
The following code shows a typical way of using query builder:
 | 
						||
 | 
						||
```php
 | 
						||
$rows = (new \yii\db\Query())
 | 
						||
    ->select(['id', 'email'])
 | 
						||
    ->from('user')
 | 
						||
    ->where(['last_name' => 'Smith'])
 | 
						||
    ->limit(10)
 | 
						||
    ->all();
 | 
						||
```
 | 
						||
 | 
						||
The above code generates and executes the following SQL query, where the `:last_name` parameter is bound with the
 | 
						||
string `'Smith'`.
 | 
						||
 | 
						||
```sql
 | 
						||
SELECT `id`, `email` 
 | 
						||
FROM `user`
 | 
						||
WHERE `last_name` = :last_name
 | 
						||
LIMIT 10
 | 
						||
```
 | 
						||
 | 
						||
> Info: You usually mainly work with [[yii\db\Query]] instead of [[yii\db\QueryBuilder]]. The latter is invoked
 | 
						||
  by the former implicitly when you call one of the query methods. [[yii\db\QueryBuilder]] is the class responsible
 | 
						||
  for generating DBMS-dependent SQL statements (e.g. quoting table/column names differently) from DBMS-independent
 | 
						||
  [[yii\db\Query]] objects.
 | 
						||
 | 
						||
 | 
						||
## Building Queries <span id="building-queries"></span>
 | 
						||
 | 
						||
To build a [[yii\db\Query]] object, you call different query building methods to specify different parts of
 | 
						||
a SQL query. The names of these methods resemble the SQL keywords used in the corresponding parts of the SQL
 | 
						||
statement. For example, to specify the `FROM` part of a SQL query, you would call the [[yii\db\Query::from()|from()]] method.
 | 
						||
All the query building methods return the query object itself, which allows you to chain multiple calls together.
 | 
						||
 | 
						||
In the following, we will describe the usage of each query building method.
 | 
						||
 | 
						||
 | 
						||
### [[yii\db\Query::select()|select()]] <span id="select"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::select()|select()]] method specifies the `SELECT` fragment of a SQL statement. You can specify 
 | 
						||
columns to be selected in either an array or a string, like the following. The column names being selected will 
 | 
						||
be automatically quoted when the SQL statement is being generated from a query object.
 | 
						||
 
 | 
						||
```php
 | 
						||
$query->select(['id', 'email']);
 | 
						||
 | 
						||
// equivalent to:
 | 
						||
 | 
						||
$query->select('id, email');
 | 
						||
```
 | 
						||
 | 
						||
The column names being selected may include table prefixes and/or column aliases, like you do when writing raw SQL queries.
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->select(['user.id AS user_id', 'email']);
 | 
						||
 | 
						||
// equivalent to:
 | 
						||
 | 
						||
$query->select('user.id AS user_id, email');
 | 
						||
```
 | 
						||
 | 
						||
If you are using the array format to specify columns, you can also use the array keys to specify the column aliases.
 | 
						||
For example, the above code can be rewritten as follows,
 | 
						||
 | 
						||
```php
 | 
						||
$query->select(['user_id' => 'user.id', 'email']);
 | 
						||
```
 | 
						||
 | 
						||
If you do not call the [[yii\db\Query::select()|select()]] method when building a query, `*` will be selected, which
 | 
						||
means selecting *all* columns.
 | 
						||
 | 
						||
Besides column names, you can also select DB expressions. You must use the array format when selecting a DB expression
 | 
						||
that contains commas to avoid incorrect automatic name quoting. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->select(["CONCAT(first_name, ' ', last_name) AS full_name", 'email']); 
 | 
						||
```
 | 
						||
 | 
						||
As with all places where raw SQL is involved, you may use the [DBMS agnostic quoting syntax](db-dao.md#quoting-table-and-column-names)
 | 
						||
for table and column names when writing DB expressions in select.
 | 
						||
 | 
						||
Starting from version 2.0.1, you may also select sub-queries. You should specify each sub-query in terms of 
 | 
						||
a [[yii\db\Query]] object. For example,
 | 
						||
 
 | 
						||
```php
 | 
						||
$subQuery = (new Query())->select('COUNT(*)')->from('user');
 | 
						||
 | 
						||
// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`
 | 
						||
$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');
 | 
						||
```
 | 
						||
 | 
						||
To select distinct rows, you may call [[yii\db\Query::distinct()|distinct()]], like the following:
 | 
						||
 | 
						||
```php
 | 
						||
// SELECT DISTINCT `user_id` ...
 | 
						||
$query->select('user_id')->distinct();
 | 
						||
```
 | 
						||
 | 
						||
You can call [[yii\db\Query::addSelect()|addSelect()]] to select additional columns. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->select(['id', 'username'])
 | 
						||
    ->addSelect(['email']);
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### [[yii\db\Query::from()|from()]] <span id="from"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::from()|from()]] method specifies the `FROM` fragment of a SQL statement. For example,
 | 
						||
 | 
						||
```php
 | 
						||
// SELECT * FROM `user`
 | 
						||
$query->from('user');
 | 
						||
```
 | 
						||
 | 
						||
You can specify the table(s) being selected from in either a string or an array. The table names may contain
 | 
						||
schema prefixes and/or table aliases, like you do when writing raw SQL statements. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->from(['public.user u', 'public.post p']);
 | 
						||
 | 
						||
// equivalent to:
 | 
						||
 | 
						||
$query->from('public.user u, public.post p');
 | 
						||
```
 | 
						||
 | 
						||
If you are using the array format, you can also use the array keys to specify the table aliases, like the following:
 | 
						||
 | 
						||
```php
 | 
						||
$query->from(['u' => 'public.user', 'p' => 'public.post']);
 | 
						||
```
 | 
						||
 | 
						||
Besides table names, you can also select from sub-queries by specifying them in terms of [[yii\db\Query]] objects.
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
$subQuery = (new Query())->select('id')->from('user')->where('status=1');
 | 
						||
 | 
						||
// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u 
 | 
						||
$query->from(['u' => $subQuery]);
 | 
						||
```
 | 
						||
 | 
						||
#### Prefixes
 | 
						||
Also a default [[yii\db\Connection::$tablePrefix|tablePrefix]] can be applied. Implementation instructions
 | 
						||
are in the ["Quoting Tables" section of the "Database Access Objects" guide](guide-db-dao.html#quoting-table-and-column-names).
 | 
						||
 | 
						||
### [[yii\db\Query::where()|where()]] <span id="where"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::where()|where()]] method specifies the `WHERE` fragment of a SQL query. You can use one of
 | 
						||
the four formats to specify a `WHERE` condition:
 | 
						||
 | 
						||
- string format, e.g., `'status=1'`
 | 
						||
- hash format, e.g. `['status' => 1, 'type' => 2]`
 | 
						||
- operator format, e.g. `['like', 'name', 'test']`
 | 
						||
- object format, e.g. `new LikeCondition('name', 'LIKE', 'test')`
 | 
						||
 | 
						||
#### String Format <span id="string-format"></span>
 | 
						||
 | 
						||
String format is best used to specify very simple conditions or if you need to use built-in functions of the DBMS.
 | 
						||
It works as if you are writing a raw SQL. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->where('status=1');
 | 
						||
 | 
						||
// or use parameter binding to bind dynamic parameter values
 | 
						||
$query->where('status=:status', [':status' => $status]);
 | 
						||
 | 
						||
// raw SQL using MySQL YEAR() function on a date field
 | 
						||
$query->where('YEAR(somedate) = 2015');
 | 
						||
```
 | 
						||
 | 
						||
Do NOT embed variables directly in the condition like the following, especially if the variable values come from 
 | 
						||
end user inputs, because this will make your application subject to SQL injection attacks.
 | 
						||
 
 | 
						||
```php
 | 
						||
// Dangerous! Do NOT do this unless you are very certain $status must be an integer.
 | 
						||
$query->where("status=$status");
 | 
						||
```
 | 
						||
 | 
						||
When using `parameter binding`, you may call [[yii\db\Query::params()|params()]] or [[yii\db\Query::addParams()|addParams()]]
 | 
						||
to specify parameters separately.
 | 
						||
 | 
						||
```php
 | 
						||
$query->where('status=:status')
 | 
						||
    ->addParams([':status' => $status]);
 | 
						||
```
 | 
						||
 | 
						||
As with all places where raw SQL is involved, you may use the [DBMS agnostic quoting syntax](db-dao.md#quoting-table-and-column-names)
 | 
						||
for table and column names when writing conditions in string format. 
 | 
						||
 | 
						||
#### Hash Format <span id="hash-format"></span>
 | 
						||
 | 
						||
Hash format is best used to specify multiple `AND`-concatenated sub-conditions each being a simple equality assertion.
 | 
						||
It is written as an array whose keys are column names and values the corresponding values that the columns should be.
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))
 | 
						||
$query->where([
 | 
						||
    'status' => 10,
 | 
						||
    'type' => null,
 | 
						||
    'id' => [4, 8, 15],
 | 
						||
]);
 | 
						||
```
 | 
						||
 | 
						||
As you can see, the query builder is intelligent enough to properly handle values that are nulls or arrays.
 | 
						||
 | 
						||
You can also use sub-queries with hash format like the following:
 | 
						||
 | 
						||
```php
 | 
						||
$userQuery = (new Query())->select('id')->from('user');
 | 
						||
 | 
						||
// ...WHERE `id` IN (SELECT `id` FROM `user`)
 | 
						||
$query->where(['id' => $userQuery]);
 | 
						||
```
 | 
						||
 | 
						||
Using the Hash Format, Yii internally applies parameter binding for values, so in contrast to the [string format](#string-format),
 | 
						||
here you do not have to add parameters manually. However, note that Yii never escapes column names, so if you pass
 | 
						||
a variable obtained from user side as a column name without any additional checks, the application will become vulnerable
 | 
						||
to SQL injection attack. In order to keep the application secure, either do not use variables as column names or
 | 
						||
filter variable against white list. In case you need to get column name from user, read the [Filtering Data](output-data-widgets.md#filtering-data)
 | 
						||
guide article. For example the following code is vulnerable:
 | 
						||
 | 
						||
```php
 | 
						||
// Vulnerable code:
 | 
						||
$column = $request->get('column');
 | 
						||
$value = $request->get('value');
 | 
						||
$query->where([$column => $value]);
 | 
						||
// $value is safe, but $column name won't be encoded!
 | 
						||
```
 | 
						||
 | 
						||
#### Operator Format <span id="operator-format"></span>
 | 
						||
 | 
						||
Operator format allows you to specify arbitrary conditions in a programmatic way. It takes the following format:
 | 
						||
 | 
						||
```php
 | 
						||
[operator, operand1, operand2, ...]
 | 
						||
```
 | 
						||
 | 
						||
where the operands can each be specified in string format, hash format or operator format recursively, while
 | 
						||
the operator can be one of the following:
 | 
						||
 | 
						||
- `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`.
 | 
						||
 | 
						||
- `not`: requires only operand 1, which will be wrapped in `NOT()`. For example, `['not', 'id=1']` will generate `NOT (id=1)`. Operand 1 may also be an array to describe multiple expressions. For example `['not', ['status' => 'draft', 'name' => 'example']]` will generate `NOT ((status='draft') AND (name='example'))`.
 | 
						||
 | 
						||
- `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`.
 | 
						||
   In case you need to build a condition where value is between two columns (like `11 BETWEEN min_id AND max_id`), 
 | 
						||
   you should use [[yii\db\conditions\BetweenColumnsCondition|BetweenColumnsCondition]]. 
 | 
						||
   See [Conditions – Object Format](#object-format) chapter to learn more about object definition of conditions.
 | 
						||
 | 
						||
- `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. Operand 2 can be either an array or a `Query` object.
 | 
						||
  It will generate an `IN` condition. If Operand 2 is an array, it will represent the range of the values
 | 
						||
  that the column or DB expression should be; If Operand 2 is a `Query` object, a sub-query will be generated
 | 
						||
  and used as the range of the column or DB expression. For example,
 | 
						||
  `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`.
 | 
						||
  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.
 | 
						||
  For example, `['in', ['id', 'name'], [['id' => 1, 'name' => 'oy']]]` will generate `(id, name) IN ((1, 'oy'))`.
 | 
						||
 | 
						||
- `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%'`.
 | 
						||
  You may also provide an optional third operand to specify how to escape special characters in the values.
 | 
						||
  The operand should be an array of mappings from the special characters to their
 | 
						||
  escaped counterparts. If this operand is not provided, a default escape mapping will be used.
 | 
						||
  You may use `false` or an empty array to indicate the values are already escaped and no escape
 | 
						||
  should be applied. Note that when using an escape mapping (or the third operand is not provided),
 | 
						||
  the values will be automatically enclosed within a pair of percentage characters.
 | 
						||
 | 
						||
  > Note: When using PostgreSQL you may also use [`ilike`](http://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE)
 | 
						||
  > instead of `like` for case-insensitive matching.
 | 
						||
 | 
						||
- `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.
 | 
						||
 | 
						||
- `exists`: requires one operand which must be an instance of [[yii\db\Query]] representing the sub-query.
 | 
						||
  It will build an `EXISTS (sub-query)` expression.
 | 
						||
 | 
						||
- `not exists`: similar to the `exists` operator and builds a `NOT EXISTS (sub-query)` expression.
 | 
						||
 | 
						||
- `>`, `<=`, or any other valid DB operator that takes two operands: the first operand must be a column name
 | 
						||
  while the second operand a value. For example, `['>', 'age', 10]` will generate `age>10`.
 | 
						||
 | 
						||
Using the Operator Format, Yii internally uses parameter binding for values, so in contrast to the [string format](#string-format),
 | 
						||
here you do not have to add parameters manually. However, note that Yii never escapes column names, so if you pass
 | 
						||
a variable as a column name, the application will likely become vulnerable to SQL injection attack. In order to keep
 | 
						||
application secure, either do not use variables as column names or filter variable against white list.
 | 
						||
In case you need to get column name from user, read the [Filtering Data](output-data-widgets.md#filtering-data)
 | 
						||
guide article. For example the following code is vulnerable:
 | 
						||
 | 
						||
```php
 | 
						||
// Vulnerable code:
 | 
						||
$column = $request->get('column');
 | 
						||
$value = $request->get('value');
 | 
						||
$query->where(['=', $column, $value]);
 | 
						||
// $value is safe, but $column name won't be encoded!
 | 
						||
```
 | 
						||
 | 
						||
#### Object Format <span id="object-format"></span>
 | 
						||
 | 
						||
Object Form is available since 2.0.14 and is both most powerful and most complex way to define conditions.
 | 
						||
You need to follow it either if you want to build your own abstraction over query builder or if you want to implement
 | 
						||
your own complex conditions.
 | 
						||
 | 
						||
Instances of condition classes are immutable. Their only purpose is to store condition data and provide getters
 | 
						||
for condition builders. Condition builder is a class that holds the logic that transforms data 
 | 
						||
stored in condition into the SQL expression.
 | 
						||
 | 
						||
Internally the formats described above are implicitly converted to object format prior to building raw SQL,
 | 
						||
so it is possible to combine formats in a single condition:
 | 
						||
 | 
						||
```php
 | 
						||
$query->andWhere(new OrCondition([
 | 
						||
    new InCondition('type', 'in', $types),
 | 
						||
    ['like', 'name', '%good%'],
 | 
						||
    'disabled=false'
 | 
						||
]))
 | 
						||
```
 | 
						||
 | 
						||
Conversion from operator format into object format is performed according to
 | 
						||
[[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] property, that maps operators names
 | 
						||
to representative class names:
 | 
						||
 | 
						||
- `AND`, `OR` -> `yii\db\conditions\ConjunctionCondition`
 | 
						||
- `NOT` -> `yii\db\conditions\NotCondition`
 | 
						||
- `IN`, `NOT IN` -> `yii\db\conditions\InCondition`
 | 
						||
- `BETWEEN`, `NOT BETWEEN` -> `yii\db\conditions\BetweenCondition`
 | 
						||
 | 
						||
And so on.
 | 
						||
 | 
						||
Using the object format makes it possible to create your own conditions or to change the way default ones are built.
 | 
						||
See [Adding Custom Conditions and Expressions](#adding-custom-conditions-and-expressions) chapter to learn more.
 | 
						||
 | 
						||
 | 
						||
#### Appending Conditions <span id="appending-conditions"></span>
 | 
						||
 | 
						||
You can use [[yii\db\Query::andWhere()|andWhere()]] or [[yii\db\Query::orWhere()|orWhere()]] to append
 | 
						||
additional conditions to an existing one. You can call them multiple times to append multiple conditions
 | 
						||
separately. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$status = 10;
 | 
						||
$search = 'yii';
 | 
						||
 | 
						||
$query->where(['status' => $status]);
 | 
						||
 | 
						||
if (!empty($search)) {
 | 
						||
    $query->andWhere(['like', 'title', $search]);
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
If `$search` is not empty, the following `WHERE` condition will be generated:
 | 
						||
 | 
						||
```sql
 | 
						||
WHERE (`status` = 10) AND (`title` LIKE '%yii%')
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
#### Filter Conditions <span id="filter-conditions"></span>
 | 
						||
 | 
						||
When building `WHERE` conditions based on input from end users, you usually want to ignore those input values, that are empty.
 | 
						||
For example, in a search form that allows you to search by username and email, you would like to ignore the username/email
 | 
						||
condition if the user does not enter anything in the username/email input field. You can achieve this goal by
 | 
						||
using the [[yii\db\Query::filterWhere()|filterWhere()]] method:
 | 
						||
 | 
						||
```php
 | 
						||
// $username and $email are from user inputs
 | 
						||
$query->filterWhere([
 | 
						||
    'username' => $username,
 | 
						||
    'email' => $email,
 | 
						||
]);
 | 
						||
```
 | 
						||
 | 
						||
The only difference between [[yii\db\Query::filterWhere()|filterWhere()]] and [[yii\db\Query::where()|where()]] 
 | 
						||
is that the former will ignore empty values provided in the condition in [hash format](#hash-format). So if `$email`
 | 
						||
is empty while `$username` is not, the above code will result in the SQL condition `WHERE username=:username`.
 | 
						||
 | 
						||
> Info: A value is considered empty if it is `null`, an empty array, an empty string or a string consisting of whitespaces only.
 | 
						||
 | 
						||
Like [[yii\db\Query::andWhere()|andWhere()]] and [[yii\db\Query::orWhere()|orWhere()]], you can use
 | 
						||
[[yii\db\Query::andFilterWhere()|andFilterWhere()]] and [[yii\db\Query::orFilterWhere()|orFilterWhere()]]
 | 
						||
to append additional filter conditions to the existing one.
 | 
						||
 | 
						||
Additionally, there is [[yii\db\Query::andFilterCompare()]] that can intelligently determine operator based on what's
 | 
						||
in the value:
 | 
						||
 | 
						||
```php
 | 
						||
$query->andFilterCompare('name', 'John Doe');
 | 
						||
$query->andFilterCompare('rating', '>9');
 | 
						||
$query->andFilterCompare('value', '<=100');
 | 
						||
```
 | 
						||
 | 
						||
You can also specify operator explicitly:
 | 
						||
 | 
						||
```php
 | 
						||
$query->andFilterCompare('name', 'Doe', 'like');
 | 
						||
```
 | 
						||
 | 
						||
Since Yii 2.0.11 there are similar methods for `HAVING` condition:
 | 
						||
 | 
						||
- [[yii\db\Query::filterHaving()|filterHaving()]]
 | 
						||
- [[yii\db\Query::andFilterHaving()|andFilterHaving()]]
 | 
						||
- [[yii\db\Query::orFilterHaving()|orFilterHaving()]]
 | 
						||
 | 
						||
### [[yii\db\Query::orderBy()|orderBy()]] <span id="order-by"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::orderBy()|orderBy()]] method specifies the `ORDER BY` fragment of a SQL query. For example,
 | 
						||
 | 
						||
```php
 | 
						||
// ... ORDER BY `id` ASC, `name` DESC
 | 
						||
$query->orderBy([
 | 
						||
    'id' => SORT_ASC,
 | 
						||
    'name' => SORT_DESC,
 | 
						||
]);
 | 
						||
```
 | 
						||
 
 | 
						||
In the above code, the array keys are column names while the array values are the corresponding order by directions.
 | 
						||
The PHP constant `SORT_ASC` specifies ascending sort and `SORT_DESC` descending sort.
 | 
						||
 | 
						||
If `ORDER BY` only involves simple column names, you can specify it using a string, just like you do when writing 
 | 
						||
raw SQL statements. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->orderBy('id ASC, name DESC');
 | 
						||
```
 | 
						||
 | 
						||
> Note: You should use the array format if `ORDER BY` involves some DB expression. 
 | 
						||
 | 
						||
You can call [[yii\db\Query::addOrderBy()|addOrderBy()]] to add additional columns to the `ORDER BY` fragment.
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->orderBy('id ASC')
 | 
						||
    ->addOrderBy('name DESC');
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### [[yii\db\Query::groupBy()|groupBy()]] <span id="group-by"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::groupBy()|groupBy()]] method specifies the `GROUP BY` fragment of a SQL query. For example,
 | 
						||
 | 
						||
```php
 | 
						||
// ... GROUP BY `id`, `status`
 | 
						||
$query->groupBy(['id', 'status']);
 | 
						||
```
 | 
						||
 | 
						||
If `GROUP BY` only involves simple column names, you can specify it using a string, just like you do when writing 
 | 
						||
raw SQL statements. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->groupBy('id, status');
 | 
						||
```
 | 
						||
 | 
						||
> Note: You should use the array format if `GROUP BY` involves some DB expression.
 | 
						||
 
 | 
						||
You can call [[yii\db\Query::addGroupBy()|addGroupBy()]] to add additional columns to the `GROUP BY` fragment.
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->groupBy(['id', 'status'])
 | 
						||
    ->addGroupBy('age');
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### [[yii\db\Query::having()|having()]] <span id="having"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::having()|having()]] method specifies the `HAVING` fragment of a SQL query. It takes
 | 
						||
a condition which can be specified in the same way as that for [where()](#where). For example,
 | 
						||
 | 
						||
```php
 | 
						||
// ... HAVING `status` = 1
 | 
						||
$query->having(['status' => 1]);
 | 
						||
```
 | 
						||
 | 
						||
Please refer to the documentation for [where()](#where) for more details about how to specify a condition.
 | 
						||
 | 
						||
You can call [[yii\db\Query::andHaving()|andHaving()]] or [[yii\db\Query::orHaving()|orHaving()]] to append
 | 
						||
additional conditions to the `HAVING` fragment. For example,
 | 
						||
 | 
						||
```php
 | 
						||
// ... HAVING (`status` = 1) AND (`age` > 30)
 | 
						||
$query->having(['status' => 1])
 | 
						||
    ->andHaving(['>', 'age', 30]);
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### [[yii\db\Query::limit()|limit()]] and [[yii\db\Query::offset()|offset()]] <span id="limit-offset"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::limit()|limit()]] and [[yii\db\Query::offset()|offset()]] methods specify the `LIMIT`
 | 
						||
and `OFFSET` fragments of a SQL query. For example,
 | 
						||
 | 
						||
```php
 | 
						||
// ... LIMIT 10 OFFSET 20
 | 
						||
$query->limit(10)->offset(20);
 | 
						||
```
 | 
						||
 | 
						||
If you specify an invalid limit or offset (e.g. a negative value), it will be ignored.
 | 
						||
 | 
						||
> Info: For DBMS that do not support `LIMIT` and `OFFSET` (e.g. MSSQL), query builder will generate a SQL
 | 
						||
  statement that emulates the `LIMIT`/`OFFSET` behavior.
 | 
						||
 | 
						||
 | 
						||
### [[yii\db\Query::join()|join()]] <span id="join"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::join()|join()]] method specifies the `JOIN` fragment of a SQL query. For example,
 | 
						||
 | 
						||
```php
 | 
						||
// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`
 | 
						||
$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');
 | 
						||
```
 | 
						||
 | 
						||
The [[yii\db\Query::join()|join()]] method takes four parameters:
 | 
						||
 | 
						||
- `$type`: join type, e.g., `'INNER JOIN'`, `'LEFT JOIN'`.
 | 
						||
- `$table`: the name of the table to be joined.
 | 
						||
- `$on`: optional, the join condition, i.e., the `ON` fragment. Please refer to [where()](#where) for details
 | 
						||
   about specifying a condition. Note, that the array syntax does **not** work for specifying a column based
 | 
						||
   condition, e.g. `['user.id' => 'comment.userId']` will result in a condition where the user id must be equal
 | 
						||
   to the string `'comment.userId'`. You should use the string syntax instead and specify the condition as
 | 
						||
   `'user.id = comment.userId'`.
 | 
						||
- `$params`: optional, the parameters to be bound to the join condition.
 | 
						||
 | 
						||
You can use the following shortcut methods to specify `INNER JOIN`, `LEFT JOIN` and `RIGHT JOIN`, respectively.
 | 
						||
 | 
						||
- [[yii\db\Query::innerJoin()|innerJoin()]]
 | 
						||
- [[yii\db\Query::leftJoin()|leftJoin()]]
 | 
						||
- [[yii\db\Query::rightJoin()|rightJoin()]]
 | 
						||
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query->leftJoin('post', 'post.user_id = user.id');
 | 
						||
```
 | 
						||
 | 
						||
To join with multiple tables, call the above join methods multiple times, once for each table.
 | 
						||
 | 
						||
Besides joining with tables, you can also join with sub-queries. To do so, specify the sub-queries to be joined
 | 
						||
as [[yii\db\Query]] objects. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$subQuery = (new \yii\db\Query())->from('post');
 | 
						||
$query->leftJoin(['u' => $subQuery], 'u.id = author_id');
 | 
						||
```
 | 
						||
 | 
						||
In this case, you should put the sub-query in an array and use the array key to specify the alias.
 | 
						||
 | 
						||
 | 
						||
### [[yii\db\Query::union()|union()]] <span id="union"></span>
 | 
						||
 | 
						||
The [[yii\db\Query::union()|union()]] method specifies the `UNION` fragment of a SQL query. For example,
 | 
						||
 | 
						||
```php
 | 
						||
$query1 = (new \yii\db\Query())
 | 
						||
    ->select("id, category_id AS type, name")
 | 
						||
    ->from('post')
 | 
						||
    ->limit(10);
 | 
						||
 | 
						||
$query2 = (new \yii\db\Query())
 | 
						||
    ->select('id, type, name')
 | 
						||
    ->from('user')
 | 
						||
    ->limit(10);
 | 
						||
 | 
						||
$query1->union($query2);
 | 
						||
```
 | 
						||
 | 
						||
You can call [[yii\db\Query::union()|union()]] multiple times to append more `UNION` fragments. 
 | 
						||
 | 
						||
 | 
						||
## Query Methods <span id="query-methods"></span>
 | 
						||
 | 
						||
[[yii\db\Query]] provides a whole set of methods for different query purposes:
 | 
						||
 | 
						||
- [[yii\db\Query::all()|all()]]: returns an array of rows with each row being an associative array of name-value pairs.
 | 
						||
- [[yii\db\Query::one()|one()]]: returns the first row of the result.
 | 
						||
- [[yii\db\Query::column()|column()]]: returns the first column of the result.
 | 
						||
- [[yii\db\Query::scalar()|scalar()]]: returns a scalar value located at the first row and first column of the result.
 | 
						||
- [[yii\db\Query::exists()|exists()]]: returns a value indicating whether the query contains any result.
 | 
						||
- [[yii\db\Query::count()|count()]]: returns the result of a `COUNT` query.
 | 
						||
- Other aggregation query methods, including [[yii\db\Query::sum()|sum($q)]], [[yii\db\Query::average()|average($q)]],
 | 
						||
  [[yii\db\Query::max()|max($q)]], [[yii\db\Query::min()|min($q)]]. The `$q` parameter is mandatory for these methods 
 | 
						||
  and can be either a column name or a DB expression.
 | 
						||
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
// SELECT `id`, `email` FROM `user`
 | 
						||
$rows = (new \yii\db\Query())
 | 
						||
    ->select(['id', 'email'])
 | 
						||
    ->from('user')
 | 
						||
    ->all();
 | 
						||
    
 | 
						||
// SELECT * FROM `user` WHERE `username` LIKE `%test%`
 | 
						||
$row = (new \yii\db\Query())
 | 
						||
    ->from('user')
 | 
						||
    ->where(['like', 'username', 'test'])
 | 
						||
    ->one();
 | 
						||
```
 | 
						||
 | 
						||
> Note: The [[yii\db\Query::one()|one()]] method only returns the first row of the query result. It does NOT
 | 
						||
  add `LIMIT 1` to the generated SQL statement. This is fine and preferred if you know the query will return only one 
 | 
						||
  or a few rows of data (e.g. if you are querying with some primary keys). However, if the query may potentially 
 | 
						||
  result in many rows of data, you should call `limit(1)` explicitly to improve the performance, e.g.,
 | 
						||
  `(new \yii\db\Query())->from('user')->limit(1)->one()`.
 | 
						||
 | 
						||
All these query methods take an optional `$db` parameter representing the [[yii\db\Connection|DB connection]] that
 | 
						||
should be used to perform a DB query. If you omit this parameter, the `db` [application component](structure-application-components.md) will be used
 | 
						||
as the DB connection. Below is another example using the [[yii\db\Query::count()|count()]] query method:
 | 
						||
 | 
						||
```php
 | 
						||
// executes SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name
 | 
						||
$count = (new \yii\db\Query())
 | 
						||
    ->from('user')
 | 
						||
    ->where(['last_name' => 'Smith'])
 | 
						||
    ->count();
 | 
						||
```
 | 
						||
 | 
						||
When you call a query method of [[yii\db\Query]], it actually does the following work internally:
 | 
						||
 | 
						||
* Call [[yii\db\QueryBuilder]] to generate a SQL statement based on the current construct of [[yii\db\Query]];
 | 
						||
* Create a [[yii\db\Command]] object with the generated SQL statement;
 | 
						||
* Call a query method (e.g.  [[yii\db\Command::queryAll()|queryAll()]]) of [[yii\db\Command]] to execute the SQL statement and retrieve the data.
 | 
						||
 | 
						||
Sometimes, you may want to examine or use the SQL statement built from a [[yii\db\Query]] object. You can
 | 
						||
achieve this goal with the following code: 
 | 
						||
 | 
						||
```php
 | 
						||
$command = (new \yii\db\Query())
 | 
						||
    ->select(['id', 'email'])
 | 
						||
    ->from('user')
 | 
						||
    ->where(['last_name' => 'Smith'])
 | 
						||
    ->limit(10)
 | 
						||
    ->createCommand();
 | 
						||
    
 | 
						||
// show the SQL statement
 | 
						||
echo $command->sql;
 | 
						||
// show the parameters to be bound
 | 
						||
print_r($command->params);
 | 
						||
 | 
						||
// returns all rows of the query result
 | 
						||
$rows = $command->queryAll();
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### Indexing Query Results <span id="indexing-query-results"></span>
 | 
						||
 | 
						||
When you call [[yii\db\Query::all()|all()]], it will return an array of rows which are indexed by consecutive integers.
 | 
						||
Sometimes you may want to index them differently, such as indexing by a particular column or expression values.
 | 
						||
You can achieve this goal by calling [[yii\db\Query::indexBy()|indexBy()]] before [[yii\db\Query::all()|all()]].
 | 
						||
For example,
 | 
						||
 | 
						||
```php
 | 
						||
// returns [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]
 | 
						||
$query = (new \yii\db\Query())
 | 
						||
    ->from('user')
 | 
						||
    ->limit(10)
 | 
						||
    ->indexBy('id')
 | 
						||
    ->all();
 | 
						||
```
 | 
						||
 | 
						||
To index by expression values, pass an anonymous function to the [[yii\db\Query::indexBy()|indexBy()]] method:
 | 
						||
 | 
						||
```php
 | 
						||
$query = (new \yii\db\Query())
 | 
						||
    ->from('user')
 | 
						||
    ->indexBy(function ($row) {
 | 
						||
        return $row['id'] . $row['username'];
 | 
						||
    })->all();
 | 
						||
```
 | 
						||
 | 
						||
The anonymous function takes a parameter `$row` which contains the current row data and should return a scalar
 | 
						||
value which will be used as the index value for the current row.
 | 
						||
 | 
						||
> Note: In contrast to query methods like [[yii\db\Query::groupBy()|groupBy()]] or [[yii\db\Query::orderBy()|orderBy()]]
 | 
						||
> which are converted to SQL and are part of the query, this method works after the data has been fetched from the database.
 | 
						||
> That means that only those column names can be used that have been part of SELECT in your query.
 | 
						||
> Also if you selected a column with table prefix, e.g. `customer.id`, the result set will only contain `id` so you have to call
 | 
						||
> `->indexBy('id')` without table prefix.
 | 
						||
 | 
						||
 | 
						||
### Batch Query <span id="batch-query"></span>
 | 
						||
 | 
						||
When working with large amounts of data, methods such as [[yii\db\Query::all()]] are not suitable
 | 
						||
because they require loading the whole query result into the client's memory. To solve this issue
 | 
						||
Yii provides batch query support. The server holds the query result, and the client uses a cursor
 | 
						||
to iterate over the result set one batch at a time.
 | 
						||
 | 
						||
> Warning: There are known limitations and workarounds for the MySQL implementation of batch queries. See below.
 | 
						||
 | 
						||
Batch query can be used like the following:
 | 
						||
 | 
						||
```php
 | 
						||
use yii\db\Query;
 | 
						||
 | 
						||
$query = (new Query())
 | 
						||
    ->from('user')
 | 
						||
    ->orderBy('id');
 | 
						||
 | 
						||
foreach ($query->batch() as $users) {
 | 
						||
    // $users is an array of 100 or fewer rows from the user table
 | 
						||
}
 | 
						||
 | 
						||
// or to iterate the row one by one
 | 
						||
foreach ($query->each() as $user) {
 | 
						||
    // data is being fetched from the server in batches of 100,
 | 
						||
    // but $user represents one row of data from the user table
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
The method [[yii\db\Query::batch()]] and [[yii\db\Query::each()]] return an [[yii\db\BatchQueryResult]] 
 | 
						||
object which implements the `Iterator` interface and thus can be used in the `foreach` construct.
 | 
						||
During the first iteration, a SQL query is made to the database. Data is then fetched in batches
 | 
						||
in the remaining iterations. By default, the batch size is 100, meaning 100 rows of data are being fetched in each batch.
 | 
						||
You can change the batch size by passing the first parameter to the `batch()` or `each()` method.
 | 
						||
 | 
						||
Compared to the [[yii\db\Query::all()]], the batch query only loads 100 rows of data at a time into the memory.
 | 
						||
 | 
						||
If you specify the query result to be indexed by some column via [[yii\db\Query::indexBy()]], 
 | 
						||
the batch query will still keep the proper index.
 | 
						||
 | 
						||
For example:
 | 
						||
 | 
						||
```php
 | 
						||
$query = (new \yii\db\Query())
 | 
						||
    ->from('user')
 | 
						||
    ->indexBy('username');
 | 
						||
 | 
						||
foreach ($query->batch() as $users) {
 | 
						||
    // $users is indexed by the "username" column
 | 
						||
}
 | 
						||
 | 
						||
foreach ($query->each() as $username => $user) {
 | 
						||
    // ...
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
#### Limitations of batch query in MySQL <span id="batch-query-mysql"></span>
 | 
						||
 | 
						||
MySQL implementation of batch queries relies on the PDO driver library. By default, MySQL queries are 
 | 
						||
[`buffered`](https://secure.php.net/manual/en/mysqlinfo.concepts.buffering.php). This defeats the purpose 
 | 
						||
of using the cursor to get the data, because it doesn't prevent the whole result set from being 
 | 
						||
loaded into the client's memory by the driver.
 | 
						||
 | 
						||
> Note: When `libmysqlclient` is used (typical of PHP5), PHP's memory limit won't count the memory 
 | 
						||
  used for result sets. It may seem that batch queries work correctly, but in reality the whole 
 | 
						||
  dataset is loaded into client's memory, and has the potential of using it up.
 | 
						||
 | 
						||
To disable buffering and reduce client memory requirements, PDO connection property 
 | 
						||
`PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` must be set to `false`. However, until the whole dataset has 
 | 
						||
been retrieved, no other query can be made through the same connection. This may prevent `ActiveRecord` 
 | 
						||
from making a query to get the table schema when it needs to. If this is not a problem 
 | 
						||
(the table schema is cached already), it is possible to switch the original connection into 
 | 
						||
unbuffered mode, and then roll back when the batch query is done.
 | 
						||
 | 
						||
```php
 | 
						||
Yii::$app->db->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
 | 
						||
 | 
						||
// Do batch query
 | 
						||
 | 
						||
Yii::$app->db->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
 | 
						||
```
 | 
						||
 | 
						||
> Note: In the case of MyISAM, for the duration of the batch query, the table may become locked, 
 | 
						||
  delaying or denying write access for other connections. When using unbuffered queries, 
 | 
						||
  try to keep the cursor open for as little time as possible.
 | 
						||
 | 
						||
If the schema is not cached, or it is necessary to run other queries while the batch query is 
 | 
						||
being processed, you can create a separate unbuffered connection to the database:
 | 
						||
 | 
						||
```php
 | 
						||
$unbufferedDb = new \yii\db\Connection([
 | 
						||
    'dsn' => Yii::$app->db->dsn,
 | 
						||
    'username' => Yii::$app->db->username,
 | 
						||
    'password' => Yii::$app->db->password,
 | 
						||
    'charset' => Yii::$app->db->charset,
 | 
						||
]);
 | 
						||
$unbufferedDb->open();
 | 
						||
$unbufferedDb->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
 | 
						||
```
 | 
						||
 | 
						||
If you want to ensure that the `$unbufferedDb` has exactly the same PDO attributes like the original 
 | 
						||
buffered `$db` but the `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` is `false`, 
 | 
						||
[consider a deep copy of `$db`](https://github.com/yiisoft/yii2/issues/8420#issuecomment-301423833), 
 | 
						||
set it to false manually.
 | 
						||
 | 
						||
Then, queries are created normally. The new connection is used to run batch queries and retrieve 
 | 
						||
results either in batches or one by one:
 | 
						||
 | 
						||
```php
 | 
						||
// getting data in batches of 1000
 | 
						||
foreach ($query->batch(1000, $unbufferedDb) as $users) {
 | 
						||
    // ...
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
// data is fetched from server in batches of 1000, but is iterated one by one 
 | 
						||
foreach ($query->each(1000, $unbufferedDb) as $user) {
 | 
						||
    // ...
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
When the connection is no longer necessary and the result set has been retrieved, it can be closed:
 | 
						||
 | 
						||
```php
 | 
						||
$unbufferedDb->close();
 | 
						||
```
 | 
						||
 | 
						||
> Note: unbuffered query uses less memory on the PHP-side, but can increase the load on the MySQL server. 
 | 
						||
It is recommended to design your own code with your production practice for extra massive data,
 | 
						||
[for example, divide the range for integer keys, loop them with Unbuffered Queries](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257).
 | 
						||
 | 
						||
### Adding custom Conditions and Expressions <span id="adding-custom-conditions-and-expressions"></span>
 | 
						||
 | 
						||
As it was mentioned in [Conditions – Object Format](#object-format) chapter, it is possible to create custom condition
 | 
						||
classes. For example, let's create a condition that will check that specific columns are less than some value.
 | 
						||
Using the operator format, it would look like the following:
 | 
						||
 | 
						||
```php
 | 
						||
[
 | 
						||
    'and',
 | 
						||
    '>', 'posts', $minLimit,
 | 
						||
    '>', 'comments', $minLimit,
 | 
						||
    '>', 'reactions', $minLimit,
 | 
						||
    '>', 'subscriptions', $minLimit
 | 
						||
]
 | 
						||
```
 | 
						||
 | 
						||
When such condition applied once, it is fine. In case it is used multiple times in a single query it can
 | 
						||
be optimized a lot. Let's create a custom condition object to demonstrate it.
 | 
						||
 | 
						||
Yii has a [[yii\db\conditions\ConditionInterface|ConditionInterface]], that must be used to mark classes, that represent
 | 
						||
a condition. It requires `fromArrayDefinition()` method implementation, in order to make possible to create condition
 | 
						||
from array format. In case you don't need it, you can implement this method with exception throwing.
 | 
						||
 | 
						||
Since we create our custom condition class, we can build API that suits our task the most. 
 | 
						||
 | 
						||
```php
 | 
						||
namespace app\db\conditions;
 | 
						||
 | 
						||
class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
 | 
						||
{
 | 
						||
    private $columns;
 | 
						||
    private $value;
 | 
						||
 | 
						||
    /**
 | 
						||
     * @param string[] $columns Array of columns that must be greater, than $value
 | 
						||
     * @param mixed $value the value to compare each $column against.
 | 
						||
     */
 | 
						||
    public function __construct(array $columns, $value)
 | 
						||
    {
 | 
						||
        $this->columns = $columns;
 | 
						||
        $this->value = $value;
 | 
						||
    }
 | 
						||
    
 | 
						||
    public static function fromArrayDefinition($operator, $operands)
 | 
						||
    {
 | 
						||
        throw new InvalidArgumentException('Not implemented yet, but we will do it later');
 | 
						||
    }
 | 
						||
    
 | 
						||
    public function getColumns() { return $this->columns; }
 | 
						||
    public function getValue() { return $this->vaule; }
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
So we can create a condition object:
 | 
						||
 | 
						||
```php
 | 
						||
$conditon = new AllGreaterCondition(['col1', 'col2'], 42);
 | 
						||
```
 | 
						||
 | 
						||
But `QueryBuilder` still does not know, to make an SQL condition out of this object.
 | 
						||
Now we need to create a builder for this condition. It must implement [[yii\db\ExpressionBuilderInterface]]
 | 
						||
that requires us to implement a `build()` method. 
 | 
						||
 | 
						||
```php
 | 
						||
namespace app\db\conditions;
 | 
						||
 | 
						||
class AllGreaterConditionBuilder implements \yii\db\ExpressionBuilderInterface
 | 
						||
{
 | 
						||
    use \yii\db\ExpressionBuilderTrait; // Contains constructor and `queryBuilder` property.
 | 
						||
 | 
						||
    /**
 | 
						||
     * @param ExpressionInterface $condition the condition to be built
 | 
						||
     * @param array $params the binding parameters.
 | 
						||
     * @return AllGreaterCondition
 | 
						||
     */ 
 | 
						||
    public function build(ExpressionInterface $expression, array &$params = [])
 | 
						||
    {
 | 
						||
        $value = $condition->getValue();
 | 
						||
        
 | 
						||
        $conditions = [];
 | 
						||
        foreach ($expression->getColumns() as $column) {
 | 
						||
            $conditions[] = new SimpleCondition($column, '>', $value);
 | 
						||
        }
 | 
						||
 | 
						||
        return $this->queryBuilder->buildCondition(new AndCondition($conditions), $params);
 | 
						||
    }
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
Then simple let [[yii\db\QueryBuilder|QueryBuilder]] know about our new condition – add a mapping for it to 
 | 
						||
the `expressionBuilders` array. It could be done right from the application configuration:
 | 
						||
 | 
						||
```php
 | 
						||
'db' => [
 | 
						||
    'class' => 'yii\db\mysql\Connection',
 | 
						||
    // ...
 | 
						||
    'queryBuilder' => [
 | 
						||
        'expressionBuilders' => [
 | 
						||
            'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
 | 
						||
        ],
 | 
						||
    ],
 | 
						||
],
 | 
						||
```
 | 
						||
 | 
						||
Now we can use our condition in `where()`:
 | 
						||
 | 
						||
```php
 | 
						||
$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));
 | 
						||
```
 | 
						||
 | 
						||
If we want to make it possible to create our custom condition using operator format, we should declare it in
 | 
						||
[[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]]:
 | 
						||
 | 
						||
```php
 | 
						||
'db' => [
 | 
						||
    'class' => 'yii\db\mysql\Connection',
 | 
						||
    // ...
 | 
						||
    'queryBuilder' => [
 | 
						||
        'expressionBuilders' => [
 | 
						||
            'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
 | 
						||
        ],
 | 
						||
        'conditionClasses' => [
 | 
						||
            'ALL>' => 'app\db\conditions\AllGreaterCondition',
 | 
						||
        ],
 | 
						||
    ],
 | 
						||
],
 | 
						||
```
 | 
						||
 | 
						||
And create a real implementation of `AllGreaterCondition::fromArrayDefinition()` method 
 | 
						||
in `app\db\conditions\AllGreaterCondition`:
 | 
						||
 | 
						||
```php
 | 
						||
namespace app\db\conditions;
 | 
						||
 | 
						||
class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
 | 
						||
{
 | 
						||
    // ... see the implementation above
 | 
						||
     
 | 
						||
    public static function fromArrayDefinition($operator, $operands)
 | 
						||
    {
 | 
						||
        return new static($operands[0], $operands[1]);
 | 
						||
    }
 | 
						||
}
 | 
						||
```
 | 
						||
    
 | 
						||
After that, we can create our custom condition using shorter operator format:
 | 
						||
 | 
						||
```php
 | 
						||
$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);
 | 
						||
```
 | 
						||
 | 
						||
You might notice, that there was two concepts used: Expressions and Conditions. There is a [[yii\db\ExpressionInterface]]
 | 
						||
that should be used to mark objects, that require an Expression Builder class, that implements
 | 
						||
[[yii\db\ExpressionBuilderInterface]] to be built. Also there is a [[yii\db\condition\ConditionInterface]], that extends
 | 
						||
[[yii\db\ExpressionInterface|ExpressionInterface]] and should be used to objects, that can be created from array definition
 | 
						||
as it was shown above, but require builder as well.
 | 
						||
 | 
						||
To summarise:
 | 
						||
 | 
						||
- Expression – is a Data Transfer Object (DTO) for a dataset, that can be somehow compiled to some SQL 
 | 
						||
statement (an operator, string, array, JSON, etc).
 | 
						||
- Condition – is an Expression superset, that aggregates multiple Expressions (or scalar values) that can be compiled
 | 
						||
to a single SQL condition.
 | 
						||
 | 
						||
You can create your own classes that implement [[yii\db\ExpressionInterface|ExpressionInterface]] to hide the complexity
 | 
						||
of transforming data to SQL statements. You will learn more about other examples of Expressions in the
 | 
						||
[next article](db-active-record.md);
 |