Query.php 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364
  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. $column = null;
  299. if (is_string($this->indexBy)) {
  300. if (($dotPos = strpos($this->indexBy, '.')) === false) {
  301. $column = $this->indexBy;
  302. } else {
  303. $column = substr($this->indexBy, $dotPos + 1);
  304. }
  305. }
  306. foreach ($rows as $row) {
  307. $value = reset($row);
  308. if ($this->indexBy instanceof \Closure) {
  309. $results[call_user_func($this->indexBy, $row)] = $value;
  310. } else {
  311. $results[$row[$column]] = $value;
  312. }
  313. }
  314. return $results;
  315. }
  316. /**
  317. * Returns the number of records.
  318. * @param string $q the COUNT expression. Defaults to '*'.
  319. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  320. * @param Connection $db the database connection used to generate the SQL statement.
  321. * If this parameter is not given (or null), the `db` application component will be used.
  322. * @return int|string number of records. The result may be a string depending on the
  323. * underlying database engine and to support integer values higher than a 32bit PHP integer can handle.
  324. */
  325. public function count($q = '*', $db = null)
  326. {
  327. if ($this->emulateExecution) {
  328. return 0;
  329. }
  330. return $this->queryScalar("COUNT($q)", $db);
  331. }
  332. /**
  333. * Returns the sum of the specified column values.
  334. * @param string $q the column name or expression.
  335. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  336. * @param Connection $db the database connection used to generate the SQL statement.
  337. * If this parameter is not given, the `db` application component will be used.
  338. * @return mixed the sum of the specified column values.
  339. */
  340. public function sum($q, $db = null)
  341. {
  342. if ($this->emulateExecution) {
  343. return 0;
  344. }
  345. return $this->queryScalar("SUM($q)", $db);
  346. }
  347. /**
  348. * Returns the average of the specified column values.
  349. * @param string $q the column name or expression.
  350. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  351. * @param Connection $db the database connection used to generate the SQL statement.
  352. * If this parameter is not given, the `db` application component will be used.
  353. * @return mixed the average of the specified column values.
  354. */
  355. public function average($q, $db = null)
  356. {
  357. if ($this->emulateExecution) {
  358. return 0;
  359. }
  360. return $this->queryScalar("AVG($q)", $db);
  361. }
  362. /**
  363. * Returns the minimum of the specified column values.
  364. * @param string $q the column name or expression.
  365. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  366. * @param Connection $db the database connection used to generate the SQL statement.
  367. * If this parameter is not given, the `db` application component will be used.
  368. * @return mixed the minimum of the specified column values.
  369. */
  370. public function min($q, $db = null)
  371. {
  372. return $this->queryScalar("MIN($q)", $db);
  373. }
  374. /**
  375. * Returns the maximum of the specified column values.
  376. * @param string $q the column name or expression.
  377. * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
  378. * @param Connection $db the database connection used to generate the SQL statement.
  379. * If this parameter is not given, the `db` application component will be used.
  380. * @return mixed the maximum of the specified column values.
  381. */
  382. public function max($q, $db = null)
  383. {
  384. return $this->queryScalar("MAX($q)", $db);
  385. }
  386. /**
  387. * Returns a value indicating whether the query result contains any row of data.
  388. * @param Connection $db the database connection used to generate the SQL statement.
  389. * If this parameter is not given, the `db` application component will be used.
  390. * @return bool whether the query result contains any row of data.
  391. */
  392. public function exists($db = null)
  393. {
  394. if ($this->emulateExecution) {
  395. return false;
  396. }
  397. $command = $this->createCommand($db);
  398. $params = $command->params;
  399. $command->setSql($command->db->getQueryBuilder()->selectExists($command->getSql()));
  400. $command->bindValues($params);
  401. return (bool) $command->queryScalar();
  402. }
  403. /**
  404. * Queries a scalar value by setting [[select]] first.
  405. * Restores the value of select to make this query reusable.
  406. * @param string|ExpressionInterface $selectExpression
  407. * @param Connection|null $db
  408. * @return bool|string
  409. */
  410. protected function queryScalar($selectExpression, $db)
  411. {
  412. if ($this->emulateExecution) {
  413. return null;
  414. }
  415. if (
  416. !$this->distinct
  417. && empty($this->groupBy)
  418. && empty($this->having)
  419. && empty($this->union)
  420. ) {
  421. $select = $this->select;
  422. $order = $this->orderBy;
  423. $limit = $this->limit;
  424. $offset = $this->offset;
  425. $this->select = [$selectExpression];
  426. $this->orderBy = null;
  427. $this->limit = null;
  428. $this->offset = null;
  429. $e = null;
  430. try {
  431. $command = $this->createCommand($db);
  432. } catch (\Exception $e) {
  433. // throw it later
  434. } catch (\Throwable $e) {
  435. // throw it later
  436. }
  437. $this->select = $select;
  438. $this->orderBy = $order;
  439. $this->limit = $limit;
  440. $this->offset = $offset;
  441. if ($e !== null) {
  442. throw $e;
  443. }
  444. return $command->queryScalar();
  445. }
  446. $command = (new self())
  447. ->select([$selectExpression])
  448. ->from(['c' => $this])
  449. ->createCommand($db);
  450. $this->setCommandCache($command);
  451. return $command->queryScalar();
  452. }
  453. /**
  454. * Returns table names used in [[from]] indexed by aliases.
  455. * Both aliases and names are enclosed into {{ and }}.
  456. * @return string[] table names indexed by aliases
  457. * @throws \yii\base\InvalidConfigException
  458. * @since 2.0.12
  459. */
  460. public function getTablesUsedInFrom()
  461. {
  462. if (empty($this->from)) {
  463. return [];
  464. }
  465. if (is_array($this->from)) {
  466. $tableNames = $this->from;
  467. } elseif (is_string($this->from)) {
  468. $tableNames = preg_split('/\s*,\s*/', trim($this->from), -1, PREG_SPLIT_NO_EMPTY);
  469. } elseif ($this->from instanceof Expression) {
  470. $tableNames = [$this->from];
  471. } else {
  472. throw new InvalidConfigException(gettype($this->from) . ' in $from is not supported.');
  473. }
  474. return $this->cleanUpTableNames($tableNames);
  475. }
  476. /**
  477. * Clean up table names and aliases
  478. * Both aliases and names are enclosed into {{ and }}.
  479. * @param array $tableNames non-empty array
  480. * @return string[] table names indexed by aliases
  481. * @since 2.0.14
  482. */
  483. protected function cleanUpTableNames($tableNames)
  484. {
  485. $cleanedUpTableNames = [];
  486. foreach ($tableNames as $alias => $tableName) {
  487. if (is_string($tableName) && !is_string($alias)) {
  488. $pattern = <<<PATTERN
  489. ~
  490. ^
  491. \s*
  492. (
  493. (?:['"`\[]|{{)
  494. .*?
  495. (?:['"`\]]|}})
  496. |
  497. \(.*?\)
  498. |
  499. .*?
  500. )
  501. (?:
  502. (?:
  503. \s+
  504. (?:as)?
  505. \s*
  506. )
  507. (
  508. (?:['"`\[]|{{)
  509. .*?
  510. (?:['"`\]]|}})
  511. |
  512. .*?
  513. )
  514. )?
  515. \s*
  516. $
  517. ~iux
  518. PATTERN;
  519. if (preg_match($pattern, $tableName, $matches)) {
  520. if (isset($matches[2])) {
  521. list(, $tableName, $alias) = $matches;
  522. } else {
  523. $tableName = $alias = $matches[1];
  524. }
  525. }
  526. }
  527. if ($tableName instanceof Expression) {
  528. if (!is_string($alias)) {
  529. throw new InvalidArgumentException('To use Expression in from() method, pass it in array format with alias.');
  530. }
  531. $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;
  532. } elseif ($tableName instanceof self) {
  533. $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;
  534. } else {
  535. $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $this->ensureNameQuoted($tableName);
  536. }
  537. }
  538. return $cleanedUpTableNames;
  539. }
  540. /**
  541. * Ensures name is wrapped with {{ and }}
  542. * @param string $name
  543. * @return string
  544. */
  545. private function ensureNameQuoted($name)
  546. {
  547. $name = str_replace(["'", '"', '`', '[', ']'], '', $name);
  548. if ($name && !preg_match('/^{{.*}}$/', $name)) {
  549. return '{{' . $name . '}}';
  550. }
  551. return $name;
  552. }
  553. /**
  554. * Sets the SELECT part of the query.
  555. * @param string|array|ExpressionInterface $columns the columns to be selected.
  556. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
  557. * Columns can be prefixed with table names (e.g. "user.id") and/or contain column aliases (e.g. "user.id AS user_id").
  558. * The method will automatically quote the column names unless a column contains some parenthesis
  559. * (which means the column contains a DB expression). A DB expression may also be passed in form of
  560. * an [[ExpressionInterface]] object.
  561. *
  562. * Note that if you are selecting an expression like `CONCAT(first_name, ' ', last_name)`, you should
  563. * use an array to specify the columns. Otherwise, the expression may be incorrectly split into several parts.
  564. *
  565. * When the columns are specified as an array, you may also use array keys as the column aliases (if a column
  566. * does not need alias, do not use a string key).
  567. *
  568. * Starting from version 2.0.1, you may also select sub-queries as columns by specifying each such column
  569. * as a `Query` instance representing the sub-query.
  570. *
  571. * @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
  572. * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
  573. * @return $this the query object itself
  574. */
  575. public function select($columns, $option = null)
  576. {
  577. $this->select = $this->normalizeSelect($columns);
  578. $this->selectOption = $option;
  579. return $this;
  580. }
  581. /**
  582. * Add more columns to the SELECT part of the query.
  583. *
  584. * Note, that if [[select]] has not been specified before, you should include `*` explicitly
  585. * if you want to select all remaining columns too:
  586. *
  587. * ```php
  588. * $query->addSelect(["*", "CONCAT(first_name, ' ', last_name) AS full_name"])->one();
  589. * ```
  590. *
  591. * @param string|array|ExpressionInterface $columns the columns to add to the select. See [[select()]] for more
  592. * details about the format of this parameter.
  593. * @return $this the query object itself
  594. * @see select()
  595. */
  596. public function addSelect($columns)
  597. {
  598. if ($this->select === null) {
  599. return $this->select($columns);
  600. }
  601. if (!is_array($this->select)) {
  602. $this->select = $this->normalizeSelect($this->select);
  603. }
  604. $this->select = array_merge($this->select, $this->normalizeSelect($columns));
  605. return $this;
  606. }
  607. /**
  608. * Normalizes the SELECT columns passed to [[select()]] or [[addSelect()]].
  609. *
  610. * @param string|array|ExpressionInterface $columns
  611. * @return array
  612. * @since 2.0.21
  613. */
  614. protected function normalizeSelect($columns)
  615. {
  616. if ($columns instanceof ExpressionInterface) {
  617. $columns = [$columns];
  618. } elseif (!is_array($columns)) {
  619. $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
  620. }
  621. $select = [];
  622. foreach ($columns as $columnAlias => $columnDefinition) {
  623. if (is_string($columnAlias)) {
  624. // Already in the normalized format, good for them
  625. $select[$columnAlias] = $columnDefinition;
  626. continue;
  627. }
  628. if (is_string($columnDefinition)) {
  629. if (
  630. preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-_\.]+)$/', $columnDefinition, $matches) &&
  631. !preg_match('/^\d+$/', $matches[2]) &&
  632. strpos($matches[2], '.') === false
  633. ) {
  634. // Using "columnName as alias" or "columnName alias" syntax
  635. $select[$matches[2]] = $matches[1];
  636. continue;
  637. }
  638. if (strpos($columnDefinition, '(') === false) {
  639. // Normal column name, just alias it to itself to ensure it's not selected twice
  640. $select[$columnDefinition] = $columnDefinition;
  641. continue;
  642. }
  643. }
  644. // Either a string calling a function, DB expression, or sub-query
  645. $select[] = $columnDefinition;
  646. }
  647. return $select;
  648. }
  649. /**
  650. * Returns unique column names excluding duplicates.
  651. * Columns to be removed:
  652. * - if column definition already present in SELECT part with same alias
  653. * - if column definition without alias already present in SELECT part without alias too
  654. * @param array $columns the columns to be merged to the select.
  655. * @since 2.0.14
  656. * @deprecated in 2.0.21
  657. */
  658. protected function getUniqueColumns($columns)
  659. {
  660. $unaliasedColumns = $this->getUnaliasedColumnsFromSelect();
  661. $result = [];
  662. foreach ($columns as $columnAlias => $columnDefinition) {
  663. if (!$columnDefinition instanceof Query) {
  664. if (is_string($columnAlias)) {
  665. $existsInSelect = isset($this->select[$columnAlias]) && $this->select[$columnAlias] === $columnDefinition;
  666. if ($existsInSelect) {
  667. continue;
  668. }
  669. } elseif (is_int($columnAlias)) {
  670. $existsInSelect = in_array($columnDefinition, $unaliasedColumns, true);
  671. $existsInResultSet = in_array($columnDefinition, $result, true);
  672. if ($existsInSelect || $existsInResultSet) {
  673. continue;
  674. }
  675. }
  676. }
  677. $result[$columnAlias] = $columnDefinition;
  678. }
  679. return $result;
  680. }
  681. /**
  682. * @return array List of columns without aliases from SELECT statement.
  683. * @since 2.0.14
  684. * @deprecated in 2.0.21
  685. */
  686. protected function getUnaliasedColumnsFromSelect()
  687. {
  688. $result = [];
  689. if (is_array($this->select)) {
  690. foreach ($this->select as $name => $value) {
  691. if (is_int($name)) {
  692. $result[] = $value;
  693. }
  694. }
  695. }
  696. return array_unique($result);
  697. }
  698. /**
  699. * Sets the value indicating whether to SELECT DISTINCT or not.
  700. * @param bool $value whether to SELECT DISTINCT or not.
  701. * @return $this the query object itself
  702. */
  703. public function distinct($value = true)
  704. {
  705. $this->distinct = $value;
  706. return $this;
  707. }
  708. /**
  709. * Sets the FROM part of the query.
  710. * @param string|array|ExpressionInterface $tables the table(s) to be selected from. This can be either a string (e.g. `'user'`)
  711. * or an array (e.g. `['user', 'profile']`) specifying one or several table names.
  712. * Table names can contain schema prefixes (e.g. `'public.user'`) and/or table aliases (e.g. `'user u'`).
  713. * The method will automatically quote the table names unless it contains some parenthesis
  714. * (which means the table is given as a sub-query or DB expression).
  715. *
  716. * When the tables are specified as an array, you may also use the array keys as the table aliases
  717. * (if a table does not need alias, do not use a string key).
  718. *
  719. * Use a Query object to represent a sub-query. In this case, the corresponding array key will be used
  720. * as the alias for the sub-query.
  721. *
  722. * To specify the `FROM` part in plain SQL, you may pass an instance of [[ExpressionInterface]].
  723. *
  724. * Here are some examples:
  725. *
  726. * ```php
  727. * // SELECT * FROM `user` `u`, `profile`;
  728. * $query = (new \yii\db\Query)->from(['u' => 'user', 'profile']);
  729. *
  730. * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;
  731. * $subquery = (new \yii\db\Query)->from('user')->where(['active' => true])
  732. * $query = (new \yii\db\Query)->from(['activeusers' => $subquery]);
  733. *
  734. * // subquery can also be a string with plain SQL wrapped in parenthesis
  735. * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;
  736. * $subquery = "(SELECT * FROM `user` WHERE `active` = 1)";
  737. * $query = (new \yii\db\Query)->from(['activeusers' => $subquery]);
  738. * ```
  739. *
  740. * @return $this the query object itself
  741. */
  742. public function from($tables)
  743. {
  744. if ($tables instanceof ExpressionInterface) {
  745. $tables = [$tables];
  746. }
  747. if (is_string($tables)) {
  748. $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
  749. }
  750. $this->from = $tables;
  751. return $this;
  752. }
  753. /**
  754. * Sets the WHERE part of the query.
  755. *
  756. * The method requires a `$condition` parameter, and optionally a `$params` parameter
  757. * specifying the values to be bound to the query.
  758. *
  759. * The `$condition` parameter should be either a string (e.g. `'id=1'`) or an array.
  760. *
  761. * {@inheritdoc}
  762. *
  763. * @param string|array|ExpressionInterface $condition the conditions that should be put in the WHERE part.
  764. * @param array $params the parameters (name => value) to be bound to the query.
  765. * @return $this the query object itself
  766. * @see andWhere()
  767. * @see orWhere()
  768. * @see QueryInterface::where()
  769. */
  770. public function where($condition, $params = [])
  771. {
  772. $this->where = $condition;
  773. $this->addParams($params);
  774. return $this;
  775. }
  776. /**
  777. * Adds an additional WHERE condition to the existing one.
  778. * The new condition and the existing one will be joined using the `AND` operator.
  779. * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
  780. * on how to specify this parameter.
  781. * @param array $params the parameters (name => value) to be bound to the query.
  782. * @return $this the query object itself
  783. * @see where()
  784. * @see orWhere()
  785. */
  786. public function andWhere($condition, $params = [])
  787. {
  788. if ($this->where === null) {
  789. $this->where = $condition;
  790. } elseif (is_array($this->where) && isset($this->where[0]) && strcasecmp($this->where[0], 'and') === 0) {
  791. $this->where[] = $condition;
  792. } else {
  793. $this->where = ['and', $this->where, $condition];
  794. }
  795. $this->addParams($params);
  796. return $this;
  797. }
  798. /**
  799. * Adds an additional WHERE condition to the existing one.
  800. * The new condition and the existing one will be joined using the `OR` operator.
  801. * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
  802. * on how to specify this parameter.
  803. * @param array $params the parameters (name => value) to be bound to the query.
  804. * @return $this the query object itself
  805. * @see where()
  806. * @see andWhere()
  807. */
  808. public function orWhere($condition, $params = [])
  809. {
  810. if ($this->where === null) {
  811. $this->where = $condition;
  812. } else {
  813. $this->where = ['or', $this->where, $condition];
  814. }
  815. $this->addParams($params);
  816. return $this;
  817. }
  818. /**
  819. * Adds a filtering condition for a specific column and allow the user to choose a filter operator.
  820. *
  821. * It adds an additional WHERE condition for the given field and determines the comparison operator
  822. * based on the first few characters of the given value.
  823. * The condition is added in the same way as in [[andFilterWhere]] so [[isEmpty()|empty values]] are ignored.
  824. * The new condition and the existing one will be joined using the `AND` operator.
  825. *
  826. * The comparison operator is intelligently determined based on the first few characters in the given value.
  827. * In particular, it recognizes the following operators if they appear as the leading characters in the given value:
  828. *
  829. * - `<`: the column must be less than the given value.
  830. * - `>`: the column must be greater than the given value.
  831. * - `<=`: the column must be less than or equal to the given value.
  832. * - `>=`: the column must be greater than or equal to the given value.
  833. * - `<>`: the column must not be the same as the given value.
  834. * - `=`: the column must be equal to the given value.
  835. * - If none of the above operators is detected, the `$defaultOperator` will be used.
  836. *
  837. * @param string $name the column name.
  838. * @param string $value the column value optionally prepended with the comparison operator.
  839. * @param string $defaultOperator The operator to use, when no operator is given in `$value`.
  840. * Defaults to `=`, performing an exact match.
  841. * @return $this The query object itself
  842. * @since 2.0.8
  843. */
  844. public function andFilterCompare($name, $value, $defaultOperator = '=')
  845. {
  846. if (preg_match('/^(<>|>=|>|<=|<|=)/', $value, $matches)) {
  847. $operator = $matches[1];
  848. $value = substr($value, strlen($operator));
  849. } else {
  850. $operator = $defaultOperator;
  851. }
  852. return $this->andFilterWhere([$operator, $name, $value]);
  853. }
  854. /**
  855. * Appends a JOIN part to the query.
  856. * The first parameter specifies what type of join it is.
  857. * @param string $type the type of join, such as INNER JOIN, LEFT JOIN.
  858. * @param string|array $table the table to be joined.
  859. *
  860. * Use a string to represent the name of the table to be joined.
  861. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  862. * The method will automatically quote the table name unless it contains some parenthesis
  863. * (which means the table is given as a sub-query or DB expression).
  864. *
  865. * Use an array to represent joining with a sub-query. The array must contain only one element.
  866. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  867. * represents the alias for the sub-query.
  868. *
  869. * @param string|array $on the join condition that should appear in the ON part.
  870. * Please refer to [[where()]] on how to specify this parameter.
  871. *
  872. * Note that the array format of [[where()]] is designed to match columns to values instead of columns to columns, so
  873. * the following would **not** work as expected: `['post.author_id' => 'user.id']`, it would
  874. * match the `post.author_id` column value against the string `'user.id'`.
  875. * It is recommended to use the string syntax here which is more suited for a join:
  876. *
  877. * ```php
  878. * 'post.author_id = user.id'
  879. * ```
  880. *
  881. * @param array $params the parameters (name => value) to be bound to the query.
  882. * @return $this the query object itself
  883. */
  884. public function join($type, $table, $on = '', $params = [])
  885. {
  886. $this->join[] = [$type, $table, $on];
  887. return $this->addParams($params);
  888. }
  889. /**
  890. * Appends an INNER JOIN part to the query.
  891. * @param string|array $table the table to be joined.
  892. *
  893. * Use a string to represent the name of the table to be joined.
  894. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  895. * The method will automatically quote the table name unless it contains some parenthesis
  896. * (which means the table is given as a sub-query or DB expression).
  897. *
  898. * Use an array to represent joining with a sub-query. The array must contain only one element.
  899. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  900. * represents the alias for the sub-query.
  901. *
  902. * @param string|array $on the join condition that should appear in the ON part.
  903. * Please refer to [[join()]] on how to specify this parameter.
  904. * @param array $params the parameters (name => value) to be bound to the query.
  905. * @return $this the query object itself
  906. */
  907. public function innerJoin($table, $on = '', $params = [])
  908. {
  909. $this->join[] = ['INNER JOIN', $table, $on];
  910. return $this->addParams($params);
  911. }
  912. /**
  913. * Appends a LEFT OUTER JOIN part to the query.
  914. * @param string|array $table the table to be joined.
  915. *
  916. * Use a string to represent the name of the table to be joined.
  917. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  918. * The method will automatically quote the table name unless it contains some parenthesis
  919. * (which means the table is given as a sub-query or DB expression).
  920. *
  921. * Use an array to represent joining with a sub-query. The array must contain only one element.
  922. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  923. * represents the alias for the sub-query.
  924. *
  925. * @param string|array $on the join condition that should appear in the ON part.
  926. * Please refer to [[join()]] on how to specify this parameter.
  927. * @param array $params the parameters (name => value) to be bound to the query
  928. * @return $this the query object itself
  929. */
  930. public function leftJoin($table, $on = '', $params = [])
  931. {
  932. $this->join[] = ['LEFT JOIN', $table, $on];
  933. return $this->addParams($params);
  934. }
  935. /**
  936. * Appends a RIGHT OUTER JOIN part to the query.
  937. * @param string|array $table the table to be joined.
  938. *
  939. * Use a string to represent the name of the table to be joined.
  940. * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
  941. * The method will automatically quote the table name unless it contains some parenthesis
  942. * (which means the table is given as a sub-query or DB expression).
  943. *
  944. * Use an array to represent joining with a sub-query. The array must contain only one element.
  945. * The value must be a [[Query]] object representing the sub-query while the corresponding key
  946. * represents the alias for the sub-query.
  947. *
  948. * @param string|array $on the join condition that should appear in the ON part.
  949. * Please refer to [[join()]] on how to specify this parameter.
  950. * @param array $params the parameters (name => value) to be bound to the query
  951. * @return $this the query object itself
  952. */
  953. public function rightJoin($table, $on = '', $params = [])
  954. {
  955. $this->join[] = ['RIGHT JOIN', $table, $on];
  956. return $this->addParams($params);
  957. }
  958. /**
  959. * Sets the GROUP BY part of the query.
  960. * @param string|array|ExpressionInterface $columns the columns to be grouped by.
  961. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
  962. * The method will automatically quote the column names unless a column contains some parenthesis
  963. * (which means the column contains a DB expression).
  964. *
  965. * Note that if your group-by is an expression containing commas, you should always use an array
  966. * to represent the group-by information. Otherwise, the method will not be able to correctly determine
  967. * the group-by columns.
  968. *
  969. * Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
  970. * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
  971. * @return $this the query object itself
  972. * @see addGroupBy()
  973. */
  974. public function groupBy($columns)
  975. {
  976. if ($columns instanceof ExpressionInterface) {
  977. $columns = [$columns];
  978. } elseif (!is_array($columns)) {
  979. $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
  980. }
  981. $this->groupBy = $columns;
  982. return $this;
  983. }
  984. /**
  985. * Adds additional group-by columns to the existing ones.
  986. * @param string|array|ExpressionInterface $columns additional columns to be grouped by.
  987. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
  988. * The method will automatically quote the column names unless a column contains some parenthesis
  989. * (which means the column contains a DB expression).
  990. *
  991. * Note that if your group-by is an expression containing commas, you should always use an array
  992. * to represent the group-by information. Otherwise, the method will not be able to correctly determine
  993. * the group-by columns.
  994. *
  995. * Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
  996. * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
  997. * @return $this the query object itself
  998. * @see groupBy()
  999. */
  1000. public function addGroupBy($columns)
  1001. {
  1002. if ($columns instanceof ExpressionInterface) {
  1003. $columns = [$columns];
  1004. } elseif (!is_array($columns)) {
  1005. $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
  1006. }
  1007. if ($this->groupBy === null) {
  1008. $this->groupBy = $columns;
  1009. } else {
  1010. $this->groupBy = array_merge($this->groupBy, $columns);
  1011. }
  1012. return $this;
  1013. }
  1014. /**
  1015. * Sets the HAVING part of the query.
  1016. * @param string|array|ExpressionInterface $condition the conditions to be put after HAVING.
  1017. * Please refer to [[where()]] on how to specify this parameter.
  1018. * @param array $params the parameters (name => value) to be bound to the query.
  1019. * @return $this the query object itself
  1020. * @see andHaving()
  1021. * @see orHaving()
  1022. */
  1023. public function having($condition, $params = [])
  1024. {
  1025. $this->having = $condition;
  1026. $this->addParams($params);
  1027. return $this;
  1028. }
  1029. /**
  1030. * Adds an additional HAVING condition to the existing one.
  1031. * The new condition and the existing one will be joined using the `AND` operator.
  1032. * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
  1033. * on how to specify this parameter.
  1034. * @param array $params the parameters (name => value) to be bound to the query.
  1035. * @return $this the query object itself
  1036. * @see having()
  1037. * @see orHaving()
  1038. */
  1039. public function andHaving($condition, $params = [])
  1040. {
  1041. if ($this->having === null) {
  1042. $this->having = $condition;
  1043. } else {
  1044. $this->having = ['and', $this->having, $condition];
  1045. }
  1046. $this->addParams($params);
  1047. return $this;
  1048. }
  1049. /**
  1050. * Adds an additional HAVING condition to the existing one.
  1051. * The new condition and the existing one will be joined using the `OR` operator.
  1052. * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
  1053. * on how to specify this parameter.
  1054. * @param array $params the parameters (name => value) to be bound to the query.
  1055. * @return $this the query object itself
  1056. * @see having()
  1057. * @see andHaving()
  1058. */
  1059. public function orHaving($condition, $params = [])
  1060. {
  1061. if ($this->having === null) {
  1062. $this->having = $condition;
  1063. } else {
  1064. $this->having = ['or', $this->having, $condition];
  1065. }
  1066. $this->addParams($params);
  1067. return $this;
  1068. }
  1069. /**
  1070. * Sets the HAVING part of the query but ignores [[isEmpty()|empty operands]].
  1071. *
  1072. * This method is similar to [[having()]]. The main difference is that this method will
  1073. * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
  1074. * for building query conditions based on filter values entered by users.
  1075. *
  1076. * The following code shows the difference between this method and [[having()]]:
  1077. *
  1078. * ```php
  1079. * // HAVING `age`=:age
  1080. * $query->filterHaving(['name' => null, 'age' => 20]);
  1081. * // HAVING `age`=:age
  1082. * $query->having(['age' => 20]);
  1083. * // HAVING `name` IS NULL AND `age`=:age
  1084. * $query->having(['name' => null, 'age' => 20]);
  1085. * ```
  1086. *
  1087. * Note that unlike [[having()]], you cannot pass binding parameters to this method.
  1088. *
  1089. * @param array $condition the conditions that should be put in the HAVING part.
  1090. * See [[having()]] on how to specify this parameter.
  1091. * @return $this the query object itself
  1092. * @see having()
  1093. * @see andFilterHaving()
  1094. * @see orFilterHaving()
  1095. * @since 2.0.11
  1096. */
  1097. public function filterHaving(array $condition)
  1098. {
  1099. $condition = $this->filterCondition($condition);
  1100. if ($condition !== []) {
  1101. $this->having($condition);
  1102. }
  1103. return $this;
  1104. }
  1105. /**
  1106. * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].
  1107. * The new condition and the existing one will be joined using the `AND` operator.
  1108. *
  1109. * This method is similar to [[andHaving()]]. The main difference is that this method will
  1110. * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
  1111. * for building query conditions based on filter values entered by users.
  1112. *
  1113. * @param array $condition the new HAVING condition. Please refer to [[having()]]
  1114. * on how to specify this parameter.
  1115. * @return $this the query object itself
  1116. * @see filterHaving()
  1117. * @see orFilterHaving()
  1118. * @since 2.0.11
  1119. */
  1120. public function andFilterHaving(array $condition)
  1121. {
  1122. $condition = $this->filterCondition($condition);
  1123. if ($condition !== []) {
  1124. $this->andHaving($condition);
  1125. }
  1126. return $this;
  1127. }
  1128. /**
  1129. * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].
  1130. * The new condition and the existing one will be joined using the `OR` operator.
  1131. *
  1132. * This method is similar to [[orHaving()]]. The main difference is that this method will
  1133. * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
  1134. * for building query conditions based on filter values entered by users.
  1135. *
  1136. * @param array $condition the new HAVING condition. Please refer to [[having()]]
  1137. * on how to specify this parameter.
  1138. * @return $this the query object itself
  1139. * @see filterHaving()
  1140. * @see andFilterHaving()
  1141. * @since 2.0.11
  1142. */
  1143. public function orFilterHaving(array $condition)
  1144. {
  1145. $condition = $this->filterCondition($condition);
  1146. if ($condition !== []) {
  1147. $this->orHaving($condition);
  1148. }
  1149. return $this;
  1150. }
  1151. /**
  1152. * Appends a SQL statement using UNION operator.
  1153. * @param string|Query $sql the SQL statement to be appended using UNION
  1154. * @param bool $all TRUE if using UNION ALL and FALSE if using UNION
  1155. * @return $this the query object itself
  1156. */
  1157. public function union($sql, $all = false)
  1158. {
  1159. $this->union[] = ['query' => $sql, 'all' => $all];
  1160. return $this;
  1161. }
  1162. /**
  1163. * Sets the parameters to be bound to the query.
  1164. * @param array $params list of query parameter values indexed by parameter placeholders.
  1165. * For example, `[':name' => 'Dan', ':age' => 31]`.
  1166. * @return $this the query object itself
  1167. * @see addParams()
  1168. */
  1169. public function params($params)
  1170. {
  1171. $this->params = $params;
  1172. return $this;
  1173. }
  1174. /**
  1175. * Adds additional parameters to be bound to the query.
  1176. * @param array $params list of query parameter values indexed by parameter placeholders.
  1177. * For example, `[':name' => 'Dan', ':age' => 31]`.
  1178. * @return $this the query object itself
  1179. * @see params()
  1180. */
  1181. public function addParams($params)
  1182. {
  1183. if (!empty($params)) {
  1184. if (empty($this->params)) {
  1185. $this->params = $params;
  1186. } else {
  1187. foreach ($params as $name => $value) {
  1188. if (is_int($name)) {
  1189. $this->params[] = $value;
  1190. } else {
  1191. $this->params[$name] = $value;
  1192. }
  1193. }
  1194. }
  1195. }
  1196. return $this;
  1197. }
  1198. /**
  1199. * Enables query cache for this Query.
  1200. * @param int|true $duration the number of seconds that query results can remain valid in cache.
  1201. * Use 0 to indicate that the cached data will never expire.
  1202. * Use a negative number to indicate that query cache should not be used.
  1203. * Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.
  1204. * Defaults to `true`.
  1205. * @param \yii\caching\Dependency $dependency the cache dependency associated with the cached result.
  1206. * @return $this the Query object itself
  1207. * @since 2.0.14
  1208. */
  1209. public function cache($duration = true, $dependency = null)
  1210. {
  1211. $this->queryCacheDuration = $duration;
  1212. $this->queryCacheDependency = $dependency;
  1213. return $this;
  1214. }
  1215. /**
  1216. * Disables query cache for this Query.
  1217. * @return $this the Query object itself
  1218. * @since 2.0.14
  1219. */
  1220. public function noCache()
  1221. {
  1222. $this->queryCacheDuration = -1;
  1223. return $this;
  1224. }
  1225. /**
  1226. * Sets $command cache, if this query has enabled caching.
  1227. *
  1228. * @param Command $command
  1229. * @return Command
  1230. * @since 2.0.14
  1231. */
  1232. protected function setCommandCache($command)
  1233. {
  1234. if ($this->queryCacheDuration !== null || $this->queryCacheDependency !== null) {
  1235. $duration = $this->queryCacheDuration === true ? null : $this->queryCacheDuration;
  1236. $command->cache($duration, $this->queryCacheDependency);
  1237. }
  1238. return $command;
  1239. }
  1240. /**
  1241. * Creates a new Query object and copies its property values from an existing one.
  1242. * The properties being copies are the ones to be used by query builders.
  1243. * @param Query $from the source query object
  1244. * @return Query the new Query object
  1245. */
  1246. public static function create($from)
  1247. {
  1248. return new self([
  1249. 'where' => $from->where,
  1250. 'limit' => $from->limit,
  1251. 'offset' => $from->offset,
  1252. 'orderBy' => $from->orderBy,
  1253. 'indexBy' => $from->indexBy,
  1254. 'select' => $from->select,
  1255. 'selectOption' => $from->selectOption,
  1256. 'distinct' => $from->distinct,
  1257. 'from' => $from->from,
  1258. 'groupBy' => $from->groupBy,
  1259. 'join' => $from->join,
  1260. 'having' => $from->having,
  1261. 'union' => $from->union,
  1262. 'params' => $from->params,
  1263. ]);
  1264. }
  1265. /**
  1266. * Returns the SQL representation of Query
  1267. * @return string
  1268. */
  1269. public function __toString()
  1270. {
  1271. return serialize($this);
  1272. }
  1273. }