Query.php 52 KB

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