Query.php 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\db;
  8. use Yii;
  9. use yii\base\Component;
  10. use yii\base\InvalidArgumentException;
  11. use yii\helpers\ArrayHelper;
  12. use yii\base\InvalidConfigException;
  13. /**
  14. * Query represents a SELECT SQL statement in a way that is independent of DBMS.
  15. *
  16. * Query provides a set of methods to facilitate the specification of different clauses
  17. * in a SELECT statement. These methods can be chained together.
  18. *
  19. * By calling [[createCommand()]], we can get a [[Command]] instance which can be further
  20. * used to perform/execute the DB query against a database.
  21. *
  22. * For example,
  23. *
  24. * ```php
  25. * $query = new Query;
  26. * // compose the query
  27. * $query->select('id, name')
  28. * ->from('user')
  29. * ->limit(10);
  30. * // build and execute the query
  31. * $rows = $query->all();
  32. * // alternatively, you can create DB command and execute it
  33. * $command = $query->createCommand();
  34. * // $command->sql returns the actual SQL
  35. * $rows = $command->queryAll();
  36. * ```
  37. *
  38. * Query internally uses the [[QueryBuilder]] class to generate the SQL statement.
  39. *
  40. * A more detailed usage guide on how to work with Query can be found in the [guide article on Query Builder](guide:db-query-builder).
  41. *
  42. * @property string[] $tablesUsedInFrom Table names indexed by aliases. This property is read-only.
  43. *
  44. * @author Qiang Xue <qiang.xue@gmail.com>
  45. * @author Carsten Brandt <mail@cebe.cc>
  46. * @since 2.0
  47. */
  48. class Query extends Component implements QueryInterface, ExpressionInterface
  49. {
  50. use QueryTrait;
  51. /**
  52. * @var array the columns being selected. For example, `['id', 'name']`.
  53. * This is used to construct the SELECT clause in a SQL statement. If not set, it means selecting all columns.
  54. * @see select()
  55. */
  56. public $select;
  57. /**
  58. * @var string additional option that should be appended to the 'SELECT' keyword. For example,
  59. * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
  60. */
  61. public $selectOption;
  62. /**
  63. * @var bool whether to select distinct rows of data only. If this is set true,
  64. * the SELECT clause would be changed to SELECT DISTINCT.
  65. */
  66. public $distinct;
  67. /**
  68. * @var array the table(s) to be selected from. For example, `['user', 'post']`.
  69. * This is used to construct the FROM clause in a SQL statement.
  70. * @see from()
  71. */
  72. public $from;
  73. /**
  74. * @var array how to group the query results. For example, `['company', 'department']`.
  75. * This is used to construct the GROUP BY clause in a SQL statement.
  76. */
  77. public $groupBy;
  78. /**
  79. * @var array how to join with other tables. Each array element represents the specification
  80. * of one join which has the following structure:
  81. *
  82. * ```php
  83. * [$joinType, $tableName, $joinCondition]
  84. * ```
  85. *
  86. * For example,
  87. *
  88. * ```php
  89. * [
  90. * ['INNER JOIN', 'user', 'user.id = author_id'],
  91. * ['LEFT JOIN', 'team', 'team.id = team_id'],
  92. * ]
  93. * ```
  94. */
  95. public $join;
  96. /**
  97. * @var string|array|ExpressionInterface the condition to be applied in the GROUP BY clause.
  98. * It can be either a string or an array. Please refer to [[where()]] on how to specify the condition.
  99. */
  100. public $having;
  101. /**
  102. * @var array this is used to construct the UNION clause(s) in a SQL statement.
  103. * Each array element is an array of the following structure:
  104. *
  105. * - `query`: either a string or a [[Query]] object representing a query
  106. * - `all`: boolean, whether it should be `UNION ALL` or `UNION`
  107. */
  108. public $union;
  109. /**
  110. * @var array list of query parameter values indexed by parameter placeholders.
  111. * For example, `[':name' => 'Dan', ':age' => 31]`.
  112. */
  113. public $params = [];
  114. /**
  115. * @var int|true the default number of seconds that query results can remain valid in cache.
  116. * Use 0 to indicate that the cached data will never expire.
  117. * Use a negative number to indicate that query cache should not be used.
  118. * Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.
  119. * @see cache()
  120. * @since 2.0.14
  121. */
  122. public $queryCacheDuration;
  123. /**
  124. * @var \yii\caching\Dependency the dependency to be associated with the cached query result for this query
  125. * @see cache()
  126. * @since 2.0.14
  127. */
  128. public $queryCacheDependency;
  129. /**
  130. * Creates a DB command that can be used to execute this query.
  131. * @param Connection $db the database connection used to generate the SQL statement.
  132. * If this parameter is not given, the `db` application component will be used.
  133. * @return Command the created DB command instance.
  134. */
  135. public function createCommand($db = null)
  136. {
  137. if ($db === null) {
  138. $db = Yii::$app->getDb();
  139. }
  140. list($sql, $params) = $db->getQueryBuilder()->build($this);
  141. $command = $db->createCommand($sql, $params);
  142. $this->setCommandCache($command);
  143. return $command;
  144. }
  145. /**
  146. * Prepares for building SQL.
  147. * This method is called by [[QueryBuilder]] when it starts to build SQL from a query object.
  148. * You may override this method to do some final preparation work when converting a query into a SQL statement.
  149. * @param QueryBuilder $builder
  150. * @return $this a prepared query instance which will be used by [[QueryBuilder]] to build the SQL
  151. */
  152. public function prepare($builder)
  153. {
  154. return $this;
  155. }
  156. /**
  157. * Starts a batch query.
  158. *
  159. * A batch query supports fetching data in batches, which can keep the memory usage under a limit.
  160. * This method will return a [[BatchQueryResult]] object which implements the [[\Iterator]] interface
  161. * and can be traversed to retrieve the data in batches.
  162. *
  163. * For example,
  164. *
  165. * ```php
  166. * $query = (new Query)->from('user');
  167. * foreach ($query->batch() as $rows) {
  168. * // $rows is an array of 100 or fewer rows from user table
  169. * }
  170. * ```
  171. *
  172. * @param int $batchSize the number of records to be fetched in each batch.
  173. * @param Connection $db the database connection. If not set, the "db" application component will be used.
  174. * @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface
  175. * and can be traversed to retrieve the data in batches.
  176. */
  177. public function batch($batchSize = 100, $db = null)
  178. {
  179. return Yii::createObject([
  180. 'class' => BatchQueryResult::className(),
  181. 'query' => $this,
  182. 'batchSize' => $batchSize,
  183. 'db' => $db,
  184. 'each' => false,
  185. ]);
  186. }
  187. /**
  188. * Starts a batch query and retrieves data row by row.
  189. *
  190. * This method is similar to [[batch()]] except that in each iteration of the result,
  191. * only one row of data is returned. For example,
  192. *
  193. * ```php
  194. * $query = (new Query)->from('user');
  195. * foreach ($query->each() as $row) {
  196. * }
  197. * ```
  198. *
  199. * @param int $batchSize the number of records to be fetched in each batch.
  200. * @param Connection $db the database connection. If not set, the "db" application component will be used.
  201. * @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface
  202. * and can be traversed to retrieve the data in batches.
  203. */
  204. public function each($batchSize = 100, $db = null)
  205. {
  206. return Yii::createObject([
  207. 'class' => BatchQueryResult::className(),
  208. 'query' => $this,
  209. 'batchSize' => $batchSize,
  210. 'db' => $db,
  211. 'each' => true,
  212. ]);
  213. }
  214. /**
  215. * Executes the query and returns all results as an array.
  216. * @param Connection $db the database connection used to generate the SQL statement.
  217. * If this parameter is not given, the `db` application component will be used.
  218. * @return array the query results. If the query results in nothing, an empty array will be returned.
  219. */
  220. public function all($db = null)
  221. {
  222. if ($this->emulateExecution) {
  223. return [];
  224. }
  225. $rows = $this->createCommand($db)->queryAll();
  226. return $this->populate($rows);
  227. }
  228. /**
  229. * Converts the raw query results into the format as specified by this query.
  230. * This method is internally used to convert the data fetched from database
  231. * into the format as required by this query.
  232. * @param array $rows the raw query result from database
  233. * @return array the converted query result
  234. */
  235. public function populate($rows)
  236. {
  237. if ($this->indexBy === null) {
  238. return $rows;
  239. }
  240. $result = [];
  241. foreach ($rows as $row) {
  242. $result[ArrayHelper::getValue($row, $this->indexBy)] = $row;
  243. }
  244. return $result;
  245. }
  246. /**
  247. * Executes the query and returns a single row of result.
  248. * @param Connection $db the database connection used to generate the SQL statement.
  249. * If this parameter is not given, the `db` application component will be used.
  250. * @return array|bool the first row (in terms of an array) of the query result. False is returned if the query
  251. * results in nothing.
  252. */
  253. public function one($db = null)
  254. {
  255. if ($this->emulateExecution) {
  256. return false;
  257. }
  258. return $this->createCommand($db)->queryOne();
  259. }
  260. /**
  261. * Returns the query result as a scalar value.
  262. * The value returned will be the first column in the first row of the query results.
  263. * @param Connection $db the database connection used to generate the SQL statement.
  264. * If this parameter is not given, the `db` application component will be used.
  265. * @return string|null|false the value of the first column in the first row of the query result.
  266. * False is returned if the query result is empty.
  267. */
  268. public function scalar($db = null)
  269. {
  270. if ($this->emulateExecution) {
  271. return null;
  272. }
  273. return $this->createCommand($db)->queryScalar();
  274. }
  275. /**
  276. * Executes the query and returns the first column of the result.
  277. * @param Connection $db the database connection used to generate the SQL statement.
  278. * If this parameter is not given, the `db` application component will be used.
  279. * @return array the first column of the query result. An empty array is returned if the query results in nothing.
  280. */
  281. public function column($db = null)
  282. {
  283. if ($this->emulateExecution) {
  284. return [];
  285. }
  286. if ($this->indexBy === null) {
  287. return $this->createCommand($db)->queryColumn();
  288. }
  289. if (is_string($this->indexBy) && is_array($this->select) && count($this->select) === 1) {
  290. if (strpos($this->indexBy, '.') === false && count($tables = $this->getTablesUsedInFrom()) > 0) {
  291. $this->select[] = key($tables) . '.' . $this->indexBy;
  292. } else {
  293. $this->select[] = $this->indexBy;
  294. }
  295. }
  296. $rows = $this->createCommand($db)->queryAll();
  297. $results = [];
  298. foreach ($rows as $row) {
  299. $value = reset($row);
  300. if ($this->indexBy instanceof \Closure) {
  301. $results[call_user_func($this->indexBy, $row)] = $value;
  302. } else {
  303. $results[$row[$this->indexBy]] = $value;
  304. }
  305. }
  306. return $results;
  307. }
  308. /**
  309. * Returns the number of records.
  310. * @param string $q the COUNT expression. Defaults to '*'.
  311. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  312. * @param Connection $db the database connection used to generate the SQL statement.
  313. * If this parameter is not given (or null), the `db` application component will be used.
  314. * @return int|string number of records. The result may be a string depending on the
  315. * underlying database engine and to support integer values higher than a 32bit PHP integer can handle.
  316. */
  317. public function count($q = '*', $db = null)
  318. {
  319. if ($this->emulateExecution) {
  320. return 0;
  321. }
  322. return $this->queryScalar("COUNT($q)", $db);
  323. }
  324. /**
  325. * Returns the sum of the specified column values.
  326. * @param string $q the column name or expression.
  327. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  328. * @param Connection $db the database connection used to generate the SQL statement.
  329. * If this parameter is not given, the `db` application component will be used.
  330. * @return mixed the sum of the specified column values.
  331. */
  332. public function sum($q, $db = null)
  333. {
  334. if ($this->emulateExecution) {
  335. return 0;
  336. }
  337. return $this->queryScalar("SUM($q)", $db);
  338. }
  339. /**
  340. * Returns the average of the specified column values.
  341. * @param string $q the column name or expression.
  342. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  343. * @param Connection $db the database connection used to generate the SQL statement.
  344. * If this parameter is not given, the `db` application component will be used.
  345. * @return mixed the average of the specified column values.
  346. */
  347. public function average($q, $db = null)
  348. {
  349. if ($this->emulateExecution) {
  350. return 0;
  351. }
  352. return $this->queryScalar("AVG($q)", $db);
  353. }
  354. /**
  355. * Returns the minimum of the specified column values.
  356. * @param string $q the column name or expression.
  357. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  358. * @param Connection $db the database connection used to generate the SQL statement.
  359. * If this parameter is not given, the `db` application component will be used.
  360. * @return mixed the minimum of the specified column values.
  361. */
  362. public function min($q, $db = null)
  363. {
  364. return $this->queryScalar("MIN($q)", $db);
  365. }
  366. /**
  367. * Returns the maximum of the specified column values.
  368. * @param string $q the column name or expression.
  369. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  370. * @param Connection $db the database connection used to generate the SQL statement.
  371. * If this parameter is not given, the `db` application component will be used.
  372. * @return mixed the maximum of the specified column values.
  373. */
  374. public function max($q, $db = null)
  375. {
  376. return $this->queryScalar("MAX($q)", $db);
  377. }
  378. /**
  379. * Returns a value indicating whether the query result contains any row of data.
  380. * @param Connection $db the database connection used to generate the SQL statement.
  381. * If this parameter is not given, the `db` application component will be used.
  382. * @return bool whether the query result contains any row of data.
  383. */
  384. public function exists($db = null)
  385. {
  386. if ($this->emulateExecution) {
  387. return false;
  388. }
  389. $command = $this->createCommand($db);
  390. $params = $command->params;
  391. $command->setSql($command->db->getQueryBuilder()->selectExists($command->getSql()));
  392. $command->bindValues($params);
  393. return (bool) $command->queryScalar();
  394. }
  395. /**
  396. * Queries a scalar value by setting [[select]] first.
  397. * Restores the value of select to make this query reusable.
  398. * @param string|ExpressionInterface $selectExpression
  399. * @param Connection|null $db
  400. * @return bool|string
  401. */
  402. protected function queryScalar($selectExpression, $db)
  403. {
  404. if ($this->emulateExecution) {
  405. return null;
  406. }
  407. if (
  408. !$this->distinct
  409. && empty($this->groupBy)
  410. && empty($this->having)
  411. && empty($this->union)
  412. ) {
  413. $select = $this->select;
  414. $order = $this->orderBy;
  415. $limit = $this->limit;
  416. $offset = $this->offset;
  417. $this->select = [$selectExpression];
  418. $this->orderBy = null;
  419. $this->limit = null;
  420. $this->offset = null;
  421. $command = $this->createCommand($db);
  422. $this->select = $select;
  423. $this->orderBy = $order;
  424. $this->limit = $limit;
  425. $this->offset = $offset;
  426. return $command->queryScalar();
  427. }
  428. $command = (new self())
  429. ->select([$selectExpression])
  430. ->from(['c' => $this])
  431. ->createCommand($db);
  432. $this->setCommandCache($command);
  433. return $command->queryScalar();
  434. }
  435. /**
  436. * Returns table names used in [[from]] indexed by aliases.
  437. * Both aliases and names are enclosed into {{ and }}.
  438. * @return string[] table names indexed by aliases
  439. * @throws \yii\base\InvalidConfigException
  440. * @since 2.0.12
  441. */
  442. public function getTablesUsedInFrom()
  443. {
  444. if (empty($this->from)) {
  445. return [];
  446. }
  447. if (is_array($this->from)) {
  448. $tableNames = $this->from;
  449. } elseif (is_string($this->from)) {
  450. $tableNames = preg_split('/\s*,\s*/', trim($this->from), -1, PREG_SPLIT_NO_EMPTY);
  451. } elseif ($this->from instanceof Expression) {
  452. $tableNames = [$this->from];
  453. } else {
  454. throw new InvalidConfigException(gettype($this->from) . ' in $from is not supported.');
  455. }
  456. return $this->cleanUpTableNames($tableNames);
  457. }
  458. /**
  459. * Clean up table names and aliases
  460. * Both aliases and names are enclosed into {{ and }}.
  461. * @param array $tableNames non-empty array
  462. * @return string[] table names indexed by aliases
  463. * @since 2.0.14
  464. */
  465. protected function cleanUpTableNames($tableNames)
  466. {
  467. $cleanedUpTableNames = [];
  468. foreach ($tableNames as $alias => $tableName) {
  469. if (is_string($tableName) && !is_string($alias)) {
  470. $pattern = <<<PATTERN
  471. ~
  472. ^
  473. \s*
  474. (
  475. (?:['"`\[]|{{)
  476. .*?
  477. (?:['"`\]]|}})
  478. |
  479. \(.*?\)
  480. |
  481. .*?
  482. )
  483. (?:
  484. (?:
  485. \s+
  486. (?:as)?
  487. \s*
  488. )
  489. (
  490. (?:['"`\[]|{{)
  491. .*?
  492. (?:['"`\]]|}})
  493. |
  494. .*?
  495. )
  496. )?
  497. \s*
  498. $
  499. ~iux
  500. PATTERN;
  501. if (preg_match($pattern, $tableName, $matches)) {
  502. if (isset($matches[2])) {
  503. list(, $tableName, $alias) = $matches;
  504. } else {
  505. $tableName = $alias = $matches[1];
  506. }
  507. }
  508. }
  509. if ($tableName instanceof Expression) {
  510. if (!is_string($alias)) {
  511. throw new InvalidArgumentException('To use Expression in from() method, pass it in array format with alias.');
  512. }
  513. $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;
  514. } elseif ($tableName instanceof self) {
  515. $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;
  516. } else {
  517. $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $this->ensureNameQuoted($tableName);
  518. }
  519. }
  520. return $cleanedUpTableNames;
  521. }
  522. /**
  523. * Ensures name is wrapped with {{ and }}
  524. * @param string $name
  525. * @return string
  526. */
  527. private function ensureNameQuoted($name)
  528. {
  529. $name = str_replace(["'", '"', '`', '[', ']'], '', $name);
  530. if ($name && !preg_match('/^{{.*}}$/', $name)) {
  531. return '{{' . $name . '}}';
  532. }
  533. return $name;
  534. }
  535. /**
  536. * Sets the SELECT part of the query.
  537. * @param string|array|ExpressionInterface $columns the columns to be selected.
  538. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
  539. * Columns can be prefixed with table names (e.g. "user.id") and/or contain column aliases (e.g. "user.id AS user_id").
  540. * The method will automatically quote the column names unless a column contains some parenthesis
  541. * (which means the column contains a DB expression). A DB expression may also be passed in form of
  542. * an [[ExpressionInterface]] object.
  543. *
  544. * Note that if you are selecting an expression like `CONCAT(first_name, ' ', last_name)`, you should
  545. * use an array to specify the columns. Otherwise, the expression may be incorrectly split into several parts.
  546. *
  547. * When the columns are specified as an array, you may also use array keys as the column aliases (if a column
  548. * does not need alias, do not use a string key).
  549. *
  550. * Starting from version 2.0.1, you may also select sub-queries as columns by specifying each such column
  551. * as a `Query` instance representing the sub-query.
  552. *
  553. * @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
  554. * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
  555. * @return $this the query object itself
  556. */
  557. public function select($columns, $option = null)
  558. {
  559. if ($columns instanceof ExpressionInterface) {
  560. $columns = [$columns];
  561. } elseif (!is_array($columns)) {
  562. $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
  563. }
  564. // this sequantial assignment is needed in order to make sure select is being reset
  565. // before using getUniqueColumns() that checks it
  566. $this->select = [];
  567. $this->select = $this->getUniqueColumns($columns);
  568. $this->selectOption = $option;
  569. return $this;
  570. }
  571. /**
  572. * Add more columns to the SELECT part of the query.
  573. *
  574. * Note, that if [[select]] has not been specified before, you should include `*` explicitly
  575. * if you want to select all remaining columns too:
  576. *
  577. * ```php
  578. * $query->addSelect(["*", "CONCAT(first_name, ' ', last_name) AS full_name"])->one();
  579. * ```
  580. *
  581. * @param string|array|ExpressionInterface $columns the columns to add to the select. See [[select()]] for more
  582. * details about the format of this parameter.
  583. * @return $this the query object itself
  584. * @see select()
  585. */
  586. public function addSelect($columns)
  587. {
  588. if ($columns instanceof ExpressionInterface) {
  589. $columns = [$columns];
  590. } elseif (!is_array($columns)) {
  591. $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
  592. }
  593. $columns = $this->getUniqueColumns($columns);
  594. if ($this->select === null) {
  595. $this->select = $columns;
  596. } else {
  597. $this->select = array_merge($this->select, $columns);
  598. }
  599. return $this;
  600. }
  601. /**
  602. * Returns unique column names excluding duplicates.
  603. * Columns to be removed:
  604. * - if column definition already present in SELECT part with same alias
  605. * - if column definition without alias already present in SELECT part without alias too
  606. * @param array $columns the columns to be merged to the select.
  607. * @since 2.0.14
  608. */
  609. protected function getUniqueColumns($columns)
  610. {
  611. $unaliasedColumns = $this->getUnaliasedColumnsFromSelect();
  612. $result = [];
  613. foreach ($columns as $columnAlias => $columnDefinition) {
  614. if (!$columnDefinition instanceof Query) {
  615. if (is_string($columnAlias)) {
  616. $existsInSelect = isset($this->select[$columnAlias]) && $this->select[$columnAlias] === $columnDefinition;
  617. if ($existsInSelect) {
  618. continue;
  619. }
  620. } elseif (is_int($columnAlias)) {
  621. $existsInSelect = in_array($columnDefinition, $unaliasedColumns, true);
  622. $existsInResultSet = in_array($columnDefinition, $result, true);
  623. if ($existsInSelect || $existsInResultSet) {
  624. continue;
  625. }
  626. }
  627. }
  628. $result[$columnAlias] = $columnDefinition;
  629. }
  630. return $result;
  631. }
  632. /**
  633. * @return array List of columns without aliases from SELECT statement.
  634. * @since 2.0.14
  635. */
  636. protected function getUnaliasedColumnsFromSelect()
  637. {
  638. $result = [];
  639. if (is_array($this->select)) {
  640. foreach ($this->select as $name => $value) {
  641. if (is_int($name)) {
  642. $result[] = $value;
  643. }
  644. }
  645. }
  646. return array_unique($result);
  647. }
  648. /**
  649. * Sets the value indicating whether to SELECT DISTINCT or not.
  650. * @param bool $value whether to SELECT DISTINCT or not.
  651. * @return $this the query object itself
  652. */
  653. public function distinct($value = true)
  654. {
  655. $this->distinct = $value;
  656. return $this;
  657. }
  658. /**
  659. * Sets the FROM part of the query.
  660. * @param string|array|ExpressionInterface $tables the table(s) to be selected from. This can be either a string (e.g. `'user'`)
  661. * or an array (e.g. `['user', 'profile']`) specifying one or several table names.
  662. * Table names can contain schema prefixes (e.g. `'public.user'`) and/or table aliases (e.g. `'user u'`).
  663. * The method will automatically quote the table names unless it contains some parenthesis
  664. * (which means the table is given as a sub-query or DB expression).
  665. *
  666. * When the tables are specified as an array, you may also use the array keys as the table aliases
  667. * (if a table does not need alias, do not use a string key).
  668. *
  669. * Use a Query object to represent a sub-query. In this case, the corresponding array key will be used
  670. * as the alias for the sub-query.
  671. *
  672. * To specify the `FROM` part in plain SQL, you may pass an instance of [[ExpressionInterface]].
  673. *
  674. * Here are some examples:
  675. *
  676. * ```php
  677. * // SELECT * FROM `user` `u`, `profile`;
  678. * $query = (new \yii\db\Query)->from(['u' => 'user', 'profile']);
  679. *
  680. * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;
  681. * $subquery = (new \yii\db\Query)->from('user')->where(['active' => true])
  682. * $query = (new \yii\db\Query)->from(['activeusers' => $subquery]);
  683. *
  684. * // subquery can also be a string with plain SQL wrapped in parenthesis
  685. * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;
  686. * $subquery = "(SELECT * FROM `user` WHERE `active` = 1)";
  687. * $query = (new \yii\db\Query)->from(['activeusers' => $subquery]);
  688. * ```
  689. *
  690. * @return $this the query object itself
  691. */
  692. public function from($tables)
  693. {
  694. if ($tables instanceof ExpressionInterface) {
  695. $tables = [$tables];
  696. }
  697. if (is_string($tables)) {
  698. $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
  699. }
  700. $this->from = $tables;
  701. return $this;
  702. }
  703. /**
  704. * Sets the WHERE part of the query.
  705. *
  706. * The method requires a `$condition` parameter, and optionally a `$params` parameter
  707. * specifying the values to be bound to the query.
  708. *
  709. * The `$condition` parameter should be either a string (e.g. `'id=1'`) or an array.
  710. *
  711. * {@inheritdoc}
  712. *
  713. * @param string|array|ExpressionInterface $condition the conditions that should be put in the WHERE part.
  714. * @param array $params the parameters (name => value) to be bound to the query.
  715. * @return $this the query object itself
  716. * @see andWhere()
  717. * @see orWhere()
  718. * @see QueryInterface::where()
  719. */
  720. public function where($condition, $params = [])
  721. {
  722. $this->where = $condition;
  723. $this->addParams($params);
  724. return $this;
  725. }
  726. /**
  727. * Adds an additional WHERE condition to the existing one.
  728. * The new condition and the existing one will be joined using the `AND` operator.
  729. * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
  730. * on how to specify this parameter.
  731. * @param array $params the parameters (name => value) to be bound to the query.
  732. * @return $this the query object itself
  733. * @see where()
  734. * @see orWhere()
  735. */
  736. public function andWhere($condition, $params = [])
  737. {
  738. if ($this->where === null) {
  739. $this->where = $condition;
  740. } elseif (is_array($this->where) && isset($this->where[0]) && strcasecmp($this->where[0], 'and') === 0) {
  741. $this->where[] = $condition;
  742. } else {
  743. $this->where = ['and', $this->where, $condition];
  744. }
  745. $this->addParams($params);
  746. return $this;
  747. }
  748. /**
  749. * Adds an additional WHERE condition to the existing one.
  750. * The new condition and the existing one will be joined using the `OR` operator.
  751. * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
  752. * on how to specify this parameter.
  753. * @param array $params the parameters (name => value) to be bound to the query.
  754. * @return $this the query object itself
  755. * @see where()
  756. * @see andWhere()
  757. */
  758. public function orWhere($condition, $params = [])
  759. {
  760. if ($this->where === null) {
  761. $this->where = $condition;
  762. } else {
  763. $this->where = ['or', $this->where, $condition];
  764. }
  765. $this->addParams($params);
  766. return $this;
  767. }
  768. /**
  769. * Adds a filtering condition for a specific column and allow the user to choose a filter operator.
  770. *
  771. * It adds an additional WHERE condition for the given field and determines the comparison operator
  772. * based on the first few characters of the given value.
  773. * The condition is added in the same way as in [[andFilterWhere]] so [[isEmpty()|empty values]] are ignored.
  774. * The new condition and the existing one will be joined using the `AND` operator.
  775. *
  776. * The comparison operator is intelligently determined based on the first few characters in the given value.
  777. * In particular, it recognizes the following operators if they appear as the leading characters in the given value:
  778. *
  779. * - `<`: the column must be less than the given value.
  780. * - `>`: the column must be greater than the given value.
  781. * - `<=`: the column must be less than or equal to the given value.
  782. * - `>=`: the column must be greater than or equal to the given value.
  783. * - `<>`: the column must not be the same as the given value.
  784. * - `=`: the column must be equal to the given value.
  785. * - If none of the above operators is detected, the `$defaultOperator` will be used.
  786. *
  787. * @param string $name the column name.
  788. * @param string $value the column value optionally prepended with the comparison operator.
  789. * @param string $defaultOperator The operator to use, when no operator is given in `$value`.
  790. * Defaults to `=`, performing an exact match.
  791. * @return $this The query object itself
  792. * @since 2.0.8
  793. */
  794. public function andFilterCompare($name, $value, $defaultOperator = '=')
  795. {
  796. if (preg_match('/^(<>|>=|>|<=|<|=)/', $value, $matches)) {
  797. $operator = $matches[1];
  798. $value = substr($value, strlen($operator));
  799. } else {
  800. $operator = $defaultOperator;
  801. }
  802. return $this->andFilterWhere([$operator, $name, $value]);
  803. }
  804. /**
  805. * Appends a JOIN part to the query.
  806. * The first parameter specifies what type of join it is.
  807. * @param string $type the type of join, such as INNER JOIN, LEFT JOIN.
  808. * @param string|array $table the table to be joined.
  809. *
  810. * Use a string to represent the name of the table to be joined.
  811. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  812. * The method will automatically quote the table name unless it contains some parenthesis
  813. * (which means the table is given as a sub-query or DB expression).
  814. *
  815. * Use an array to represent joining with a sub-query. The array must contain only one element.
  816. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  817. * represents the alias for the sub-query.
  818. *
  819. * @param string|array $on the join condition that should appear in the ON part.
  820. * Please refer to [[where()]] on how to specify this parameter.
  821. *
  822. * Note that the array format of [[where()]] is designed to match columns to values instead of columns to columns, so
  823. * the following would **not** work as expected: `['post.author_id' => 'user.id']`, it would
  824. * match the `post.author_id` column value against the string `'user.id'`.
  825. * It is recommended to use the string syntax here which is more suited for a join:
  826. *
  827. * ```php
  828. * 'post.author_id = user.id'
  829. * ```
  830. *
  831. * @param array $params the parameters (name => value) to be bound to the query.
  832. * @return $this the query object itself
  833. */
  834. public function join($type, $table, $on = '', $params = [])
  835. {
  836. $this->join[] = [$type, $table, $on];
  837. return $this->addParams($params);
  838. }
  839. /**
  840. * Appends an INNER JOIN part to the query.
  841. * @param string|array $table the table to be joined.
  842. *
  843. * Use a string to represent the name of the table to be joined.
  844. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  845. * The method will automatically quote the table name unless it contains some parenthesis
  846. * (which means the table is given as a sub-query or DB expression).
  847. *
  848. * Use an array to represent joining with a sub-query. The array must contain only one element.
  849. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  850. * represents the alias for the sub-query.
  851. *
  852. * @param string|array $on the join condition that should appear in the ON part.
  853. * Please refer to [[join()]] on how to specify this parameter.
  854. * @param array $params the parameters (name => value) to be bound to the query.
  855. * @return $this the query object itself
  856. */
  857. public function innerJoin($table, $on = '', $params = [])
  858. {
  859. $this->join[] = ['INNER JOIN', $table, $on];
  860. return $this->addParams($params);
  861. }
  862. /**
  863. * Appends a LEFT OUTER JOIN part to the query.
  864. * @param string|array $table the table to be joined.
  865. *
  866. * Use a string to represent the name of the table to be joined.
  867. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  868. * The method will automatically quote the table name unless it contains some parenthesis
  869. * (which means the table is given as a sub-query or DB expression).
  870. *
  871. * Use an array to represent joining with a sub-query. The array must contain only one element.
  872. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  873. * represents the alias for the sub-query.
  874. *
  875. * @param string|array $on the join condition that should appear in the ON part.
  876. * Please refer to [[join()]] on how to specify this parameter.
  877. * @param array $params the parameters (name => value) to be bound to the query
  878. * @return $this the query object itself
  879. */
  880. public function leftJoin($table, $on = '', $params = [])
  881. {
  882. $this->join[] = ['LEFT JOIN', $table, $on];
  883. return $this->addParams($params);
  884. }
  885. /**
  886. * Appends a RIGHT OUTER JOIN part to the query.
  887. * @param string|array $table the table to be joined.
  888. *
  889. * Use a string to represent the name of the table to be joined.
  890. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  891. * The method will automatically quote the table name unless it contains some parenthesis
  892. * (which means the table is given as a sub-query or DB expression).
  893. *
  894. * Use an array to represent joining with a sub-query. The array must contain only one element.
  895. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  896. * represents the alias for the sub-query.
  897. *
  898. * @param string|array $on the join condition that should appear in the ON part.
  899. * Please refer to [[join()]] on how to specify this parameter.
  900. * @param array $params the parameters (name => value) to be bound to the query
  901. * @return $this the query object itself
  902. */
  903. public function rightJoin($table, $on = '', $params = [])
  904. {
  905. $this->join[] = ['RIGHT JOIN', $table, $on];
  906. return $this->addParams($params);
  907. }
  908. /**
  909. * Sets the GROUP BY part of the query.
  910. * @param string|array|ExpressionInterface $columns the columns to be grouped by.
  911. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
  912. * The method will automatically quote the column names unless a column contains some parenthesis
  913. * (which means the column contains a DB expression).
  914. *
  915. * Note that if your group-by is an expression containing commas, you should always use an array
  916. * to represent the group-by information. Otherwise, the method will not be able to correctly determine
  917. * the group-by columns.
  918. *
  919. * Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
  920. * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
  921. * @return $this the query object itself
  922. * @see addGroupBy()
  923. */
  924. public function groupBy($columns)
  925. {
  926. if ($columns instanceof ExpressionInterface) {
  927. $columns = [$columns];
  928. } elseif (!is_array($columns)) {
  929. $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
  930. }
  931. $this->groupBy = $columns;
  932. return $this;
  933. }
  934. /**
  935. * Adds additional group-by columns to the existing ones.
  936. * @param string|array|ExpressionInterface $columns additional columns to be grouped by.
  937. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
  938. * The method will automatically quote the column names unless a column contains some parenthesis
  939. * (which means the column contains a DB expression).
  940. *
  941. * Note that if your group-by is an expression containing commas, you should always use an array
  942. * to represent the group-by information. Otherwise, the method will not be able to correctly determine
  943. * the group-by columns.
  944. *
  945. * Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
  946. * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
  947. * @return $this the query object itself
  948. * @see groupBy()
  949. */
  950. public function addGroupBy($columns)
  951. {
  952. if ($columns instanceof ExpressionInterface) {
  953. $columns = [$columns];
  954. } elseif (!is_array($columns)) {
  955. $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
  956. }
  957. if ($this->groupBy === null) {
  958. $this->groupBy = $columns;
  959. } else {
  960. $this->groupBy = array_merge($this->groupBy, $columns);
  961. }
  962. return $this;
  963. }
  964. /**
  965. * Sets the HAVING part of the query.
  966. * @param string|array|ExpressionInterface $condition the conditions to be put after HAVING.
  967. * Please refer to [[where()]] on how to specify this parameter.
  968. * @param array $params the parameters (name => value) to be bound to the query.
  969. * @return $this the query object itself
  970. * @see andHaving()
  971. * @see orHaving()
  972. */
  973. public function having($condition, $params = [])
  974. {
  975. $this->having = $condition;
  976. $this->addParams($params);
  977. return $this;
  978. }
  979. /**
  980. * Adds an additional HAVING condition to the existing one.
  981. * The new condition and the existing one will be joined using the `AND` operator.
  982. * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
  983. * on how to specify this parameter.
  984. * @param array $params the parameters (name => value) to be bound to the query.
  985. * @return $this the query object itself
  986. * @see having()
  987. * @see orHaving()
  988. */
  989. public function andHaving($condition, $params = [])
  990. {
  991. if ($this->having === null) {
  992. $this->having = $condition;
  993. } else {
  994. $this->having = ['and', $this->having, $condition];
  995. }
  996. $this->addParams($params);
  997. return $this;
  998. }
  999. /**
  1000. * Adds an additional HAVING condition to the existing one.
  1001. * The new condition and the existing one will be joined using the `OR` operator.
  1002. * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
  1003. * on how to specify this parameter.
  1004. * @param array $params the parameters (name => value) to be bound to the query.
  1005. * @return $this the query object itself
  1006. * @see having()
  1007. * @see andHaving()
  1008. */
  1009. public function orHaving($condition, $params = [])
  1010. {
  1011. if ($this->having === null) {
  1012. $this->having = $condition;
  1013. } else {
  1014. $this->having = ['or', $this->having, $condition];
  1015. }
  1016. $this->addParams($params);
  1017. return $this;
  1018. }
  1019. /**
  1020. * Sets the HAVING part of the query but ignores [[isEmpty()|empty operands]].
  1021. *
  1022. * This method is similar to [[having()]]. The main difference is that this method will
  1023. * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
  1024. * for building query conditions based on filter values entered by users.
  1025. *
  1026. * The following code shows the difference between this method and [[having()]]:
  1027. *
  1028. * ```php
  1029. * // HAVING `age`=:age
  1030. * $query->filterHaving(['name' => null, 'age' => 20]);
  1031. * // HAVING `age`=:age
  1032. * $query->having(['age' => 20]);
  1033. * // HAVING `name` IS NULL AND `age`=:age
  1034. * $query->having(['name' => null, 'age' => 20]);
  1035. * ```
  1036. *
  1037. * Note that unlike [[having()]], you cannot pass binding parameters to this method.
  1038. *
  1039. * @param array $condition the conditions that should be put in the HAVING part.
  1040. * See [[having()]] on how to specify this parameter.
  1041. * @return $this the query object itself
  1042. * @see having()
  1043. * @see andFilterHaving()
  1044. * @see orFilterHaving()
  1045. * @since 2.0.11
  1046. */
  1047. public function filterHaving(array $condition)
  1048. {
  1049. $condition = $this->filterCondition($condition);
  1050. if ($condition !== []) {
  1051. $this->having($condition);
  1052. }
  1053. return $this;
  1054. }
  1055. /**
  1056. * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].
  1057. * The new condition and the existing one will be joined using the `AND` operator.
  1058. *
  1059. * This method is similar to [[andHaving()]]. The main difference is that this method will
  1060. * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
  1061. * for building query conditions based on filter values entered by users.
  1062. *
  1063. * @param array $condition the new HAVING condition. Please refer to [[having()]]
  1064. * on how to specify this parameter.
  1065. * @return $this the query object itself
  1066. * @see filterHaving()
  1067. * @see orFilterHaving()
  1068. * @since 2.0.11
  1069. */
  1070. public function andFilterHaving(array $condition)
  1071. {
  1072. $condition = $this->filterCondition($condition);
  1073. if ($condition !== []) {
  1074. $this->andHaving($condition);
  1075. }
  1076. return $this;
  1077. }
  1078. /**
  1079. * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].
  1080. * The new condition and the existing one will be joined using the `OR` operator.
  1081. *
  1082. * This method is similar to [[orHaving()]]. The main difference is that this method will
  1083. * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
  1084. * for building query conditions based on filter values entered by users.
  1085. *
  1086. * @param array $condition the new HAVING condition. Please refer to [[having()]]
  1087. * on how to specify this parameter.
  1088. * @return $this the query object itself
  1089. * @see filterHaving()
  1090. * @see andFilterHaving()
  1091. * @since 2.0.11
  1092. */
  1093. public function orFilterHaving(array $condition)
  1094. {
  1095. $condition = $this->filterCondition($condition);
  1096. if ($condition !== []) {
  1097. $this->orHaving($condition);
  1098. }
  1099. return $this;
  1100. }
  1101. /**
  1102. * Appends a SQL statement using UNION operator.
  1103. * @param string|Query $sql the SQL statement to be appended using UNION
  1104. * @param bool $all TRUE if using UNION ALL and FALSE if using UNION
  1105. * @return $this the query object itself
  1106. */
  1107. public function union($sql, $all = false)
  1108. {
  1109. $this->union[] = ['query' => $sql, 'all' => $all];
  1110. return $this;
  1111. }
  1112. /**
  1113. * Sets the parameters to be bound to the query.
  1114. * @param array $params list of query parameter values indexed by parameter placeholders.
  1115. * For example, `[':name' => 'Dan', ':age' => 31]`.
  1116. * @return $this the query object itself
  1117. * @see addParams()
  1118. */
  1119. public function params($params)
  1120. {
  1121. $this->params = $params;
  1122. return $this;
  1123. }
  1124. /**
  1125. * Adds additional parameters to be bound to the query.
  1126. * @param array $params list of query parameter values indexed by parameter placeholders.
  1127. * For example, `[':name' => 'Dan', ':age' => 31]`.
  1128. * @return $this the query object itself
  1129. * @see params()
  1130. */
  1131. public function addParams($params)
  1132. {
  1133. if (!empty($params)) {
  1134. if (empty($this->params)) {
  1135. $this->params = $params;
  1136. } else {
  1137. foreach ($params as $name => $value) {
  1138. if (is_int($name)) {
  1139. $this->params[] = $value;
  1140. } else {
  1141. $this->params[$name] = $value;
  1142. }
  1143. }
  1144. }
  1145. }
  1146. return $this;
  1147. }
  1148. /**
  1149. * Enables query cache for this Query.
  1150. * @param int|true $duration the number of seconds that query results can remain valid in cache.
  1151. * Use 0 to indicate that the cached data will never expire.
  1152. * Use a negative number to indicate that query cache should not be used.
  1153. * Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.
  1154. * Defaults to `true`.
  1155. * @param \yii\caching\Dependency $dependency the cache dependency associated with the cached result.
  1156. * @return $this the Query object itself
  1157. * @since 2.0.14
  1158. */
  1159. public function cache($duration = true, $dependency = null)
  1160. {
  1161. $this->queryCacheDuration = $duration;
  1162. $this->queryCacheDependency = $dependency;
  1163. return $this;
  1164. }
  1165. /**
  1166. * Disables query cache for this Query.
  1167. * @return $this the Query object itself
  1168. * @since 2.0.14
  1169. */
  1170. public function noCache()
  1171. {
  1172. $this->queryCacheDuration = -1;
  1173. return $this;
  1174. }
  1175. /**
  1176. * Sets $command cache, if this query has enabled caching.
  1177. *
  1178. * @param Command $command
  1179. * @return Command
  1180. * @since 2.0.14
  1181. */
  1182. protected function setCommandCache($command)
  1183. {
  1184. if ($this->queryCacheDuration !== null || $this->queryCacheDependency !== null) {
  1185. $duration = $this->queryCacheDuration === true ? null : $this->queryCacheDuration;
  1186. $command->cache($duration, $this->queryCacheDependency);
  1187. }
  1188. return $command;
  1189. }
  1190. /**
  1191. * Creates a new Query object and copies its property values from an existing one.
  1192. * The properties being copies are the ones to be used by query builders.
  1193. * @param Query $from the source query object
  1194. * @return Query the new Query object
  1195. */
  1196. public static function create($from)
  1197. {
  1198. return new self([
  1199. 'where' => $from->where,
  1200. 'limit' => $from->limit,
  1201. 'offset' => $from->offset,
  1202. 'orderBy' => $from->orderBy,
  1203. 'indexBy' => $from->indexBy,
  1204. 'select' => $from->select,
  1205. 'selectOption' => $from->selectOption,
  1206. 'distinct' => $from->distinct,
  1207. 'from' => $from->from,
  1208. 'groupBy' => $from->groupBy,
  1209. 'join' => $from->join,
  1210. 'having' => $from->having,
  1211. 'union' => $from->union,
  1212. 'params' => $from->params,
  1213. ]);
  1214. }
  1215. /**
  1216. * Returns the SQL representation of Query
  1217. * @return string
  1218. */
  1219. public function __toString()
  1220. {
  1221. return serialize($this);
  1222. }
  1223. }