Query.php 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312
  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 Expression) {
  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 $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. }