mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			1013 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			1013 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.
 | ||
| 
 | ||
| - `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`](http://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\Condition\ExpressionBuilderTrait; // Contains constructor and `queryBuilder` property.
 | ||
| 
 | ||
|     /**
 | ||
|      * @param AllGreaterCondition $condition the condition to be built
 | ||
|      * @param array $params the binding parameters.
 | ||
|      */ 
 | ||
|     public function build(ConditionInterface $condition, &$params)
 | ||
|     {
 | ||
|         $value = $condition->getValue();
 | ||
|         
 | ||
|         $conditions = [];
 | ||
|         foreach ($condition->getColumns() as $column) {
 | ||
|             $conditions[] = new SimpleCondition($column, '>', $value);
 | ||
|         }
 | ||
| 
 | ||
|         return $this->queryBuider->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);
 | 
