PageRenderTime 65ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Doctrine/Doctrine/Query/Abstract.php

https://github.com/ostric/e-learning
PHP | 2162 lines | 1135 code | 208 blank | 819 comment | 133 complexity | cd09e94e643c03d2c5c34c46630c8d60 MD5 | raw file
  1. <?php
  2. /*
  3. * $Id: Query.php 1393 2007-05-19 17:49:16Z zYne $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information, see
  19. * <http://www.phpdoctrine.org>.
  20. */
  21. /**
  22. * Doctrine_Query_Abstract
  23. *
  24. * @package Doctrine
  25. * @subpackage Query
  26. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  27. * @link www.phpdoctrine.org
  28. * @since 1.0
  29. * @version $Revision: 1393 $
  30. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
  31. * @todo See {@link Doctrine_Query}
  32. */
  33. abstract class Doctrine_Query_Abstract
  34. {
  35. /**
  36. * QUERY TYPE CONSTANTS
  37. */
  38. /**
  39. * constant for SELECT queries
  40. */
  41. const SELECT = 0;
  42. /**
  43. * constant for DELETE queries
  44. */
  45. const DELETE = 1;
  46. /**
  47. * constant for UPDATE queries
  48. */
  49. const UPDATE = 2;
  50. /**
  51. * constant for INSERT queries
  52. */
  53. const INSERT = 3;
  54. /**
  55. * constant for CREATE queries
  56. */
  57. const CREATE = 4;
  58. /** @todo document the query states (and the transitions between them). */
  59. /**
  60. * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
  61. */
  62. const STATE_CLEAN = 1;
  63. /**
  64. * A query object is in state DIRTY when it has DQL parts that have not yet been
  65. * parsed/processed.
  66. */
  67. const STATE_DIRTY = 2;
  68. /**
  69. * A query is in DIRECT state when ... ?
  70. */
  71. const STATE_DIRECT = 3;
  72. /**
  73. * A query object is on LOCKED state when ... ?
  74. */
  75. const STATE_LOCKED = 4;
  76. /**
  77. * @var array Table alias map. Keys are SQL aliases and values DQL aliases.
  78. */
  79. protected $_tableAliasMap = array();
  80. /**
  81. * @var Doctrine_View The view object used by this query, if any.
  82. */
  83. protected $_view;
  84. /**
  85. * @var integer $_state The current state of this query.
  86. */
  87. protected $_state = Doctrine_Query::STATE_CLEAN;
  88. /**
  89. * @var array $params The parameters of this query.
  90. */
  91. protected $_params = array('join' => array(),
  92. 'where' => array(),
  93. 'set' => array(),
  94. 'having' => array());
  95. /* Caching properties */
  96. /**
  97. * @var Doctrine_Cache_Interface The cache driver used for caching result sets.
  98. */
  99. protected $_resultCache;
  100. /**
  101. * @var boolean $_expireResultCache A boolean value that indicates whether or not
  102. * expire the result cache.
  103. */
  104. protected $_expireResultCache = false;
  105. protected $_resultCacheTTL;
  106. /**
  107. * @var Doctrine_Cache_Interface The cache driver used for caching queries.
  108. */
  109. protected $_queryCache;
  110. protected $_expireQueryCache = false;
  111. protected $_queryCacheTTL;
  112. /**
  113. * @var Doctrine_Connection The connection used by this query object.
  114. */
  115. protected $_conn;
  116. /**
  117. * @var array $_sqlParts The SQL query string parts. Filled during the DQL parsing process.
  118. */
  119. protected $_sqlParts = array(
  120. 'select' => array(),
  121. 'distinct' => false,
  122. 'forUpdate' => false,
  123. 'from' => array(),
  124. 'set' => array(),
  125. 'join' => array(),
  126. 'where' => array(),
  127. 'groupby' => array(),
  128. 'having' => array(),
  129. 'orderby' => array(),
  130. 'limit' => false,
  131. 'offset' => false,
  132. );
  133. /**
  134. * @var array $_dqlParts an array containing all DQL query parts
  135. */
  136. protected $_dqlParts = array(
  137. 'from' => array(),
  138. 'select' => array(),
  139. 'forUpdate' => false,
  140. 'set' => array(),
  141. 'join' => array(),
  142. 'where' => array(),
  143. 'groupby' => array(),
  144. 'having' => array(),
  145. 'orderby' => array(),
  146. 'limit' => array(),
  147. 'offset' => array(),
  148. );
  149. /**
  150. * @var array $_queryComponents Two dimensional array containing the components of this query,
  151. * informations about their relations and other related information.
  152. * The components are constructed during query parsing.
  153. *
  154. * Keys are component aliases and values the following:
  155. *
  156. * table table object associated with given alias
  157. *
  158. * relation the relation object owned by the parent
  159. *
  160. * parent the alias of the parent
  161. *
  162. * agg the aggregates of this component
  163. *
  164. * map the name of the column / aggregate value this
  165. * component is mapped to a collection
  166. */
  167. protected $_queryComponents = array();
  168. /**
  169. * @var integer $type the query type
  170. *
  171. * @see Doctrine_Query::* constants
  172. */
  173. protected $_type = self::SELECT;
  174. /**
  175. * @var Doctrine_Hydrator The hydrator object used to hydrate query results.
  176. */
  177. protected $_hydrator;
  178. /**
  179. * @var Doctrine_Query_Tokenizer The tokenizer that is used during the query parsing process.
  180. */
  181. protected $_tokenizer;
  182. /**
  183. * @var Doctrine_Query_Parser The parser that is used for query parsing.
  184. */
  185. protected $_parser;
  186. /**
  187. * @var array $_tableAliasSeeds A simple array keys representing table aliases and values
  188. * table alias seeds. The seeds are used for generating short table
  189. * aliases.
  190. */
  191. protected $_tableAliasSeeds = array();
  192. /**
  193. * @var array $_options an array of options
  194. */
  195. protected $_options = array(
  196. 'hydrationMode' => Doctrine::HYDRATE_RECORD
  197. );
  198. /**
  199. * @var boolean
  200. */
  201. protected $_isLimitSubqueryUsed = false;
  202. /**
  203. * @var array components used in the DQL statement
  204. */
  205. protected $_components;
  206. /**
  207. * @var bool Boolean variable for whether or not the preQuery process has been executed
  208. */
  209. protected $_preQueried = false;
  210. /**
  211. * Constructor.
  212. *
  213. * @param Doctrine_Connection The connection object the query will use.
  214. * @param Doctrine_Hydrator_Abstract The hydrator that will be used for generating result sets.
  215. */
  216. public function __construct(Doctrine_Connection $connection = null,
  217. Doctrine_Hydrator_Abstract $hydrator = null)
  218. {
  219. if ($connection === null) {
  220. $connection = Doctrine_Manager::getInstance()->getCurrentConnection();
  221. }
  222. if ($hydrator === null) {
  223. $hydrator = new Doctrine_Hydrator();
  224. }
  225. $this->_conn = $connection;
  226. $this->_hydrator = $hydrator;
  227. $this->_tokenizer = new Doctrine_Query_Tokenizer();
  228. $this->_resultCacheTTL = $this->_conn->getAttribute(Doctrine::ATTR_RESULT_CACHE_LIFESPAN);
  229. $this->_queryCacheTTL = $this->_conn->getAttribute(Doctrine::ATTR_QUERY_CACHE_LIFESPAN);
  230. }
  231. /**
  232. * setOption
  233. *
  234. * @param string $name option name
  235. * @param string $value option value
  236. * @return Doctrine_Query this object
  237. */
  238. public function setOption($name, $value)
  239. {
  240. if ( ! isset($this->_options[$name])) {
  241. throw new Doctrine_Query_Exception('Unknown option ' . $name);
  242. }
  243. $this->_options[$name] = $value;
  244. }
  245. /**
  246. * hasTableAlias
  247. * whether or not this object has given tableAlias
  248. *
  249. * @param string $tableAlias the table alias to be checked
  250. * @return boolean true if this object has given alias, otherwise false
  251. * @deprecated
  252. */
  253. public function hasTableAlias($sqlTableAlias)
  254. {
  255. return $this->hasSqlTableAlias($sqlTableAlias);
  256. }
  257. /**
  258. * hasSqlTableAlias
  259. * whether or not this object has given tableAlias
  260. *
  261. * @param string $tableAlias the table alias to be checked
  262. * @return boolean true if this object has given alias, otherwise false
  263. */
  264. public function hasSqlTableAlias($sqlTableAlias)
  265. {
  266. return (isset($this->_tableAliasMap[$sqlTableAlias]));
  267. }
  268. /**
  269. * getTableAliases
  270. * returns all table aliases
  271. *
  272. * @return array table aliases as an array
  273. * @deprecated
  274. */
  275. public function getTableAliases()
  276. {
  277. return $this->getTableAliasMap();
  278. }
  279. /**
  280. * getTableAliasMap
  281. * returns all table aliases
  282. *
  283. * @return array table aliases as an array
  284. */
  285. public function getTableAliasMap()
  286. {
  287. return $this->_tableAliasMap;
  288. }
  289. /**
  290. * getDql
  291. * returns the DQL query that is represented by this query object.
  292. *
  293. * the query is built from $_dqlParts
  294. *
  295. * @return string the DQL query
  296. */
  297. public function getDql()
  298. {
  299. $q = '';
  300. if ($this->_type == self::SELECT) {
  301. $q .= ( ! empty($this->_dqlParts['select'])) ? 'SELECT ' . implode(', ', $this->_dqlParts['select']) : '';
  302. $q .= ( ! empty($this->_dqlParts['from'])) ? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
  303. } else if ($this->_type == self::DELETE) {
  304. $q .= 'DELETE';
  305. $q .= ( ! empty($this->_dqlParts['from'])) ? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
  306. } else if ($this->_type == self::UPDATE) {
  307. $q .= 'UPDATE ';
  308. $q .= ( ! empty($this->_dqlParts['from'])) ? implode(' ', $this->_dqlParts['from']) : '';
  309. $q .= ( ! empty($this->_dqlParts['set'])) ? ' SET ' . implode(' ', $this->_dqlParts['set']) : '';
  310. }
  311. $q .= ( ! empty($this->_dqlParts['where'])) ? ' WHERE ' . implode(' ', $this->_dqlParts['where']) : '';
  312. $q .= ( ! empty($this->_dqlParts['groupby'])) ? ' GROUP BY ' . implode(', ', $this->_dqlParts['groupby']) : '';
  313. $q .= ( ! empty($this->_dqlParts['having'])) ? ' HAVING ' . implode(' AND ', $this->_dqlParts['having']) : '';
  314. $q .= ( ! empty($this->_dqlParts['orderby'])) ? ' ORDER BY ' . implode(', ', $this->_dqlParts['orderby']) : '';
  315. $q .= ( ! empty($this->_dqlParts['limit'])) ? ' LIMIT ' . implode(' ', $this->_dqlParts['limit']) : '';
  316. $q .= ( ! empty($this->_dqlParts['offset'])) ? ' OFFSET ' . implode(' ', $this->_dqlParts['offset']) : '';
  317. return $q;
  318. }
  319. /**
  320. * getQueryPart
  321. * gets a query part from the query part array
  322. *
  323. * @param string $name the name of the query part to be set
  324. * @param string $part query part string
  325. * @throws Doctrine_Query_Exception if trying to set unknown query part
  326. * @return Doctrine_Query_Abstract this object
  327. * @deprecated
  328. */
  329. public function getQueryPart($part)
  330. {
  331. return $this->getSqlQueryPart($part);
  332. }
  333. /**
  334. * getSqlQueryPart
  335. * gets an SQL query part from the SQL query part array
  336. *
  337. * @param string $name the name of the query part to be set
  338. * @param string $part query part string
  339. * @throws Doctrine_Query_Exception if trying to set unknown query part
  340. * @return Doctrine_Hydrate this object
  341. */
  342. public function getSqlQueryPart($part)
  343. {
  344. if ( ! isset($this->_sqlParts[$part])) {
  345. throw new Doctrine_Query_Exception('Unknown SQL query part ' . $part);
  346. }
  347. return $this->_sqlParts[$part];
  348. }
  349. /**
  350. * setQueryPart
  351. * sets a query part in the query part array
  352. *
  353. * @param string $name the name of the query part to be set
  354. * @param string $part query part string
  355. * @throws Doctrine_Query_Exception if trying to set unknown query part
  356. * @return Doctrine_Hydrate this object
  357. * @deprecated
  358. */
  359. public function setQueryPart($name, $part)
  360. {
  361. return $this->setSqlQueryPart($name, $part);
  362. }
  363. /**
  364. * setSqlQueryPart
  365. * sets an SQL query part in the SQL query part array
  366. *
  367. * @param string $name the name of the query part to be set
  368. * @param string $part query part string
  369. * @throws Doctrine_Query_Exception if trying to set unknown query part
  370. * @return Doctrine_Hydrate this object
  371. */
  372. public function setSqlQueryPart($name, $part)
  373. {
  374. if ( ! isset($this->_sqlParts[$name])) {
  375. throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  376. }
  377. if ($name !== 'limit' && $name !== 'offset') {
  378. if (is_array($part)) {
  379. $this->_sqlParts[$name] = $part;
  380. } else {
  381. $this->_sqlParts[$name] = array($part);
  382. }
  383. } else {
  384. $this->_sqlParts[$name] = $part;
  385. }
  386. return $this;
  387. }
  388. /**
  389. * addQueryPart
  390. * adds a query part in the query part array
  391. *
  392. * @param string $name the name of the query part to be added
  393. * @param string $part query part string
  394. * @throws Doctrine_Query_Exception if trying to add unknown query part
  395. * @return Doctrine_Hydrate this object
  396. * @deprecated
  397. */
  398. public function addQueryPart($name, $part)
  399. {
  400. return $this->addSqlQueryPart($name, $part);
  401. }
  402. /**
  403. * addSqlQueryPart
  404. * adds an SQL query part to the SQL query part array
  405. *
  406. * @param string $name the name of the query part to be added
  407. * @param string $part query part string
  408. * @throws Doctrine_Query_Exception if trying to add unknown query part
  409. * @return Doctrine_Hydrate this object
  410. */
  411. public function addSqlQueryPart($name, $part)
  412. {
  413. if ( ! isset($this->_sqlParts[$name])) {
  414. throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  415. }
  416. if (is_array($part)) {
  417. $this->_sqlParts[$name] = array_merge($this->_sqlParts[$name], $part);
  418. } else {
  419. $this->_sqlParts[$name][] = $part;
  420. }
  421. return $this;
  422. }
  423. /**
  424. * removeQueryPart
  425. * removes a query part from the query part array
  426. *
  427. * @param string $name the name of the query part to be removed
  428. * @throws Doctrine_Query_Exception if trying to remove unknown query part
  429. * @return Doctrine_Hydrate this object
  430. * @deprecated
  431. */
  432. public function removeQueryPart($name)
  433. {
  434. return $this->removeSqlQueryPart($name);
  435. }
  436. /**
  437. * removeSqlQueryPart
  438. * removes a query part from the query part array
  439. *
  440. * @param string $name the name of the query part to be removed
  441. * @throws Doctrine_Query_Exception if trying to remove unknown query part
  442. * @return Doctrine_Hydrate this object
  443. */
  444. public function removeSqlQueryPart($name)
  445. {
  446. if ( ! isset($this->_sqlParts[$name])) {
  447. throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  448. }
  449. if ($name == 'limit' || $name == 'offset') {
  450. $this->_sqlParts[$name] = false;
  451. } else {
  452. $this->_sqlParts[$name] = array();
  453. }
  454. return $this;
  455. }
  456. /**
  457. * removeDqlQueryPart
  458. * removes a dql query part from the dql query part array
  459. *
  460. * @param string $name the name of the query part to be removed
  461. * @throws Doctrine_Query_Exception if trying to remove unknown query part
  462. * @return Doctrine_Hydrate this object
  463. */
  464. public function removeDqlQueryPart($name)
  465. {
  466. if ( ! isset($this->_dqlParts[$name])) {
  467. throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  468. }
  469. if ($name == 'limit' || $name == 'offset') {
  470. $this->_dqlParts[$name] = false;
  471. } else {
  472. $this->_dqlParts[$name] = array();
  473. }
  474. return $this;
  475. }
  476. /**
  477. * getParams
  478. *
  479. * @return array
  480. */
  481. public function getParams($params = array())
  482. {
  483. return array_merge((array) $params, $this->_params['join'], $this->_params['set'], $this->_params['where'], $this->_params['having']);
  484. }
  485. /**
  486. * Get the raw array of parameters
  487. *
  488. * @return array
  489. */
  490. public function getRawParams()
  491. {
  492. return $this->_params;
  493. }
  494. /**
  495. * setParams
  496. *
  497. * @param array $params
  498. */
  499. public function setParams(array $params = array())
  500. {
  501. $this->_params = $params;
  502. }
  503. /**
  504. * setView
  505. * sets a database view this query object uses
  506. * this method should only be called internally by doctrine
  507. *
  508. * @param Doctrine_View $view database view
  509. * @return void
  510. */
  511. public function setView(Doctrine_View $view)
  512. {
  513. $this->_view = $view;
  514. }
  515. /**
  516. * getView
  517. * returns the view associated with this query object (if any)
  518. *
  519. * @return Doctrine_View the view associated with this query object
  520. */
  521. public function getView()
  522. {
  523. return $this->_view;
  524. }
  525. /**
  526. * limitSubqueryUsed
  527. *
  528. * @return boolean
  529. */
  530. public function isLimitSubqueryUsed()
  531. {
  532. return $this->_isLimitSubqueryUsed;
  533. }
  534. /**
  535. * Returns the inheritance condition for the passed componentAlias
  536. * If no component alias is specified it defaults to the root component
  537. *
  538. * This function is used to append a SQL condition to models which have inheritance mapping
  539. * The condition is applied to the FROM component in the WHERE, but the condition is applied to
  540. * JOINS in the ON condition and not the WHERE
  541. *
  542. * @return string $str SQL condition string
  543. */
  544. public function getInheritanceCondition($componentAlias)
  545. {
  546. $map = $this->_queryComponents[$componentAlias]['table']->inheritanceMap;
  547. // No inheritance map so lets just return
  548. if (empty($map)) {
  549. return;
  550. }
  551. $tableAlias = $this->getSqlTableAlias($componentAlias);
  552. if ($this->_type !== Doctrine_Query::SELECT) {
  553. $tableAlias = '';
  554. } else {
  555. $tableAlias .= '.';
  556. }
  557. $field = key($map);
  558. $value = current($map);
  559. $identifier = $this->_conn->quoteIdentifier($tableAlias . $field);
  560. return $identifier . ' = ' . $this->_conn->quote($value);;
  561. }
  562. /**
  563. * getTableAlias
  564. * some database such as Oracle need the identifier lengths to be < ~30 chars
  565. * hence Doctrine creates as short identifier aliases as possible
  566. *
  567. * this method is used for the creation of short table aliases, its also
  568. * smart enough to check if an alias already exists for given component (componentAlias)
  569. *
  570. * @param string $componentAlias the alias for the query component to search table alias for
  571. * @param string $tableName the table name from which the table alias is being created
  572. * @return string the generated / fetched short alias
  573. * @deprecated
  574. */
  575. public function getTableAlias($componentAlias, $tableName = null)
  576. {
  577. return $this->getSqlTableAlias($componentAlias, $tableName);
  578. }
  579. /**
  580. * getSqlTableAlias
  581. * some database such as Oracle need the identifier lengths to be < ~30 chars
  582. * hence Doctrine creates as short identifier aliases as possible
  583. *
  584. * this method is used for the creation of short table aliases, its also
  585. * smart enough to check if an alias already exists for given component (componentAlias)
  586. *
  587. * @param string $componentAlias the alias for the query component to search table alias for
  588. * @param string $tableName the table name from which the table alias is being created
  589. * @return string the generated / fetched short alias
  590. */
  591. public function getSqlTableAlias($componentAlias, $tableName = null)
  592. {
  593. $alias = array_search($componentAlias, $this->_tableAliasMap);
  594. if ($alias !== false) {
  595. return $alias;
  596. }
  597. if ($tableName === null) {
  598. throw new Doctrine_Query_Exception("Couldn't get short alias for " . $componentAlias);
  599. }
  600. return $this->generateTableAlias($componentAlias, $tableName);
  601. }
  602. /**
  603. * generateNewTableAlias
  604. * generates a new alias from given table alias
  605. *
  606. * @param string $tableAlias table alias from which to generate the new alias from
  607. * @return string the created table alias
  608. * @deprecated
  609. */
  610. public function generateNewTableAlias($oldAlias)
  611. {
  612. return $this->generateNewSqlTableAlias($oldAlias);
  613. }
  614. /**
  615. * generateNewSqlTableAlias
  616. * generates a new alias from given table alias
  617. *
  618. * @param string $tableAlias table alias from which to generate the new alias from
  619. * @return string the created table alias
  620. */
  621. public function generateNewSqlTableAlias($oldAlias)
  622. {
  623. if (isset($this->_tableAliasMap[$oldAlias])) {
  624. // generate a new alias
  625. $name = substr($oldAlias, 0, 1);
  626. $i = ((int) substr($oldAlias, 1));
  627. // Fix #1530: It was reaching unexistent seeds index
  628. if ( ! isset($this->_tableAliasSeeds[$name])) {
  629. $this->_tableAliasSeeds[$name] = 1;
  630. }
  631. $newIndex = ($this->_tableAliasSeeds[$name] + (($i == 0) ? 1 : $i));
  632. return $name . $newIndex;
  633. }
  634. return $oldAlias;
  635. }
  636. /**
  637. * getTableAliasSeed
  638. * returns the alias seed for given table alias
  639. *
  640. * @param string $tableAlias table alias that identifies the alias seed
  641. * @return integer table alias seed
  642. * @deprecated
  643. */
  644. public function getTableAliasSeed($sqlTableAlias)
  645. {
  646. return $this->getSqlTableAliasSeed($sqlTableAlias);
  647. }
  648. /**
  649. * getSqlTableAliasSeed
  650. * returns the alias seed for given table alias
  651. *
  652. * @param string $tableAlias table alias that identifies the alias seed
  653. * @return integer table alias seed
  654. */
  655. public function getSqlTableAliasSeed($sqlTableAlias)
  656. {
  657. if ( ! isset($this->_tableAliasSeeds[$sqlTableAlias])) {
  658. return 0;
  659. }
  660. return $this->_tableAliasSeeds[$sqlTableAlias];
  661. }
  662. /**
  663. * hasAliasDeclaration
  664. * whether or not this object has a declaration for given component alias
  665. *
  666. * @param string $componentAlias the component alias the retrieve the declaration from
  667. * @return boolean
  668. */
  669. public function hasAliasDeclaration($componentAlias)
  670. {
  671. return isset($this->_queryComponents[$componentAlias]);
  672. }
  673. /**
  674. * getAliasDeclaration
  675. * get the declaration for given component alias
  676. *
  677. * @param string $componentAlias the component alias the retrieve the declaration from
  678. * @return array the alias declaration
  679. * @deprecated
  680. */
  681. public function getAliasDeclaration($componentAlias)
  682. {
  683. return $this->getQueryComponent($componentAlias);
  684. }
  685. /**
  686. * getQueryComponent
  687. * get the declaration for given component alias
  688. *
  689. * @param string $componentAlias the component alias the retrieve the declaration from
  690. * @return array the alias declaration
  691. */
  692. public function getQueryComponent($componentAlias)
  693. {
  694. if ( ! isset($this->_queryComponents[$componentAlias])) {
  695. throw new Doctrine_Query_Exception('Unknown component alias ' . $componentAlias);
  696. }
  697. return $this->_queryComponents[$componentAlias];
  698. }
  699. /**
  700. * copyAliases
  701. * copy aliases from another Hydrate object
  702. *
  703. * this method is needed by DQL subqueries which need the aliases
  704. * of the parent query
  705. *
  706. * @param Doctrine_Hydrate $query the query object from which the
  707. * aliases are copied from
  708. * @return Doctrine_Hydrate this object
  709. */
  710. public function copySubqueryInfo(Doctrine_Query_Abstract $query)
  711. {
  712. $this->_params =& $query->_params;
  713. $this->_tableAliasMap =& $query->_tableAliasMap;
  714. $this->_queryComponents =& $query->_queryComponents;
  715. $this->_tableAliasSeeds = $query->_tableAliasSeeds;
  716. return $this;
  717. }
  718. /**
  719. * getRootAlias
  720. * returns the alias of the the root component
  721. *
  722. * @return array
  723. */
  724. public function getRootAlias()
  725. {
  726. if ( ! $this->_queryComponents) {
  727. $this->getSql();
  728. }
  729. reset($this->_queryComponents);
  730. return key($this->_queryComponents);
  731. }
  732. /**
  733. * getRootDeclaration
  734. * returns the root declaration
  735. *
  736. * @return array
  737. */
  738. public function getRootDeclaration()
  739. {
  740. $map = reset($this->_queryComponents);
  741. return $map;
  742. }
  743. /**
  744. * getRoot
  745. * returns the root component for this object
  746. *
  747. * @return Doctrine_Table root components table
  748. */
  749. public function getRoot()
  750. {
  751. $map = reset($this->_queryComponents);
  752. if ( ! isset($map['table'])) {
  753. throw new Doctrine_Query_Exception('Root component not initialized.');
  754. }
  755. return $map['table'];
  756. }
  757. /**
  758. * generateTableAlias
  759. * generates a table alias from given table name and associates
  760. * it with given component alias
  761. *
  762. * @param string $componentAlias the component alias to be associated with generated table alias
  763. * @param string $tableName the table name from which to generate the table alias
  764. * @return string the generated table alias
  765. * @deprecated
  766. */
  767. public function generateTableAlias($componentAlias, $tableName)
  768. {
  769. return $this->generateSqlTableAlias($componentAlias, $tableName);
  770. }
  771. /**
  772. * generateSqlTableAlias
  773. * generates a table alias from given table name and associates
  774. * it with given component alias
  775. *
  776. * @param string $componentAlias the component alias to be associated with generated table alias
  777. * @param string $tableName the table name from which to generate the table alias
  778. * @return string the generated table alias
  779. */
  780. public function generateSqlTableAlias($componentAlias, $tableName)
  781. {
  782. preg_match('/([^_])/', $tableName, $matches);
  783. $char = strtolower($matches[0]);
  784. $alias = $char;
  785. if ( ! isset($this->_tableAliasSeeds[$alias])) {
  786. $this->_tableAliasSeeds[$alias] = 1;
  787. }
  788. while (isset($this->_tableAliasMap[$alias])) {
  789. if ( ! isset($this->_tableAliasSeeds[$alias])) {
  790. $this->_tableAliasSeeds[$alias] = 1;
  791. }
  792. $alias = $char . ++$this->_tableAliasSeeds[$alias];
  793. }
  794. $this->_tableAliasMap[$alias] = $componentAlias;
  795. return $alias;
  796. }
  797. /**
  798. * getComponentAlias
  799. * get component alias associated with given table alias
  800. *
  801. * @param string $sqlTableAlias the SQL table alias that identifies the component alias
  802. * @return string component alias
  803. */
  804. public function getComponentAlias($sqlTableAlias)
  805. {
  806. $sqlTableAlias = trim($sqlTableAlias, '[]`"');
  807. if ( ! isset($this->_tableAliasMap[$sqlTableAlias])) {
  808. throw new Doctrine_Query_Exception('Unknown table alias ' . $sqlTableAlias);
  809. }
  810. return $this->_tableAliasMap[$sqlTableAlias];
  811. }
  812. /**
  813. * calculateQueryCacheHash
  814. * calculate hash key for query cache
  815. *
  816. * @return string the hash
  817. */
  818. public function calculateQueryCacheHash()
  819. {
  820. $dql = $this->getDql();
  821. $hash = md5($dql . 'DOCTRINE_QUERY_CACHE_SALT');
  822. return $hash;
  823. }
  824. /**
  825. * calculateResultCacheHash
  826. * calculate hash key for result cache
  827. *
  828. * @param array $params
  829. * @return string the hash
  830. */
  831. public function calculateResultCacheHash($params = array())
  832. {
  833. $dql = $this->getDql();
  834. $params = $this->getParams($params);
  835. $conn = $this->getConnection();
  836. $hash = md5($conn->getName() . $conn->getOption('dsn') . $dql . var_export($params, true));
  837. return $hash;
  838. }
  839. /**
  840. * _execute
  841. *
  842. * @param array $params
  843. * @return PDOStatement The executed PDOStatement.
  844. */
  845. protected function _execute($params)
  846. {
  847. $params = $this->_conn->convertBooleans($params);
  848. if ( ! $this->_view) {
  849. if ($this->_queryCache !== false && ($this->_queryCache || $this->_conn->getAttribute(Doctrine::ATTR_QUERY_CACHE))) {
  850. $queryCacheDriver = $this->getQueryCacheDriver();
  851. $hash = $this->calculateQueryCacheHash();
  852. $cached = $queryCacheDriver->fetch($hash);
  853. if ($cached) {
  854. $query = $this->_constructQueryFromCache($cached);
  855. } else {
  856. $query = $this->getSqlQuery($params);
  857. // Check again because getSqlQuery() above could have flipped the _queryCache flag
  858. // if this query contains the limit sub query algorithm we don't need to cache it
  859. if ($this->_queryCache !== false && ($this->_queryCache || $this->_conn->getAttribute(Doctrine::ATTR_QUERY_CACHE))) {
  860. $serializedQuery = $this->getCachedForm($query);
  861. $queryCacheDriver->save($hash, $serializedQuery, $this->getQueryCacheLifeSpan());
  862. }
  863. }
  864. } else {
  865. $query = $this->getSqlQuery($params);
  866. }
  867. } else {
  868. $query = $this->_view->getSelectSql();
  869. }
  870. if ($this->isLimitSubqueryUsed() &&
  871. $this->_conn->getAttribute(Doctrine::ATTR_DRIVER_NAME) !== 'mysql') {
  872. $params = array_merge($params, $params);
  873. }
  874. if ($this->_type !== self::SELECT) {
  875. return $this->_conn->exec($query, $params);
  876. }
  877. $stmt = $this->_conn->execute($query, $params);
  878. return $stmt;
  879. }
  880. /**
  881. * execute
  882. * executes the query and populates the data set
  883. *
  884. * @param array $params
  885. * @return Doctrine_Collection the root collection
  886. */
  887. public function execute($params = array(), $hydrationMode = null)
  888. {
  889. if (empty($this->_dqlParts['from']) && empty($this->_sqlParts['from'])) {
  890. throw new Doctrine_Query_Exception('You must have at least one component specified in your from.');
  891. }
  892. $preQueryParams = $this->getParams($params);
  893. $this->_preQuery($preQueryParams);
  894. if ($hydrationMode !== null) {
  895. $this->_hydrator->setHydrationMode($hydrationMode);
  896. }
  897. $params = $this->getParams($params);
  898. if ($this->_resultCache && $this->_type == self::SELECT) {
  899. $cacheDriver = $this->getResultCacheDriver();
  900. $hash = $this->calculateResultCacheHash($params);
  901. $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
  902. if ($cached === false) {
  903. // cache miss
  904. $stmt = $this->_execute($params);
  905. $this->_hydrator->setQueryComponents($this->_queryComponents);
  906. $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
  907. $cached = $this->getCachedForm($result);
  908. $cacheDriver->save($hash, $cached, $this->getResultCacheLifeSpan());
  909. } else {
  910. $result = $this->_constructQueryFromCache($cached);
  911. }
  912. } else {
  913. $stmt = $this->_execute($params);
  914. if (is_integer($stmt)) {
  915. $result = $stmt;
  916. } else {
  917. $this->_hydrator->setQueryComponents($this->_queryComponents);
  918. $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
  919. }
  920. }
  921. return $result;
  922. }
  923. /**
  924. * Get the dql call back for this query
  925. *
  926. * @return array $callback
  927. */
  928. protected function _getDqlCallback()
  929. {
  930. $callback = false;
  931. if ( ! empty($this->_dqlParts['from'])) {
  932. switch ($this->_type) {
  933. case self::DELETE:
  934. $callback = array(
  935. 'callback' => 'preDqlDelete',
  936. 'const' => Doctrine_Event::RECORD_DQL_DELETE
  937. );
  938. break;
  939. case self::UPDATE:
  940. $callback = array(
  941. 'callback' => 'preDqlUpdate',
  942. 'const' => Doctrine_Event::RECORD_DQL_UPDATE
  943. );
  944. break;
  945. case self::SELECT:
  946. $callback = array(
  947. 'callback' => 'preDqlSelect',
  948. 'const' => Doctrine_Event::RECORD_DQL_SELECT
  949. );
  950. break;
  951. }
  952. }
  953. return $callback;
  954. }
  955. /**
  956. * Pre query method which invokes the pre*Query() methods on the model instance or any attached
  957. * record listeners
  958. *
  959. * @return void
  960. */
  961. protected function _preQuery($params = array())
  962. {
  963. if ( ! $this->_preQueried && $this->getConnection()->getAttribute('use_dql_callbacks')) {
  964. $this->_preQueried = true;
  965. $callback = $this->_getDqlCallback();
  966. // if there is no callback for the query type, then we can return early
  967. if ( ! $callback) {
  968. return;
  969. }
  970. foreach ($this->_getDqlCallbackComponents($params) as $alias => $component) {
  971. $table = $component['table'];
  972. $record = $table->getRecordInstance();
  973. // Trigger preDql*() callback event
  974. $params = array('component' => $component, 'alias' => $alias);
  975. $event = new Doctrine_Event($record, $callback['const'], $this, $params);
  976. $record->$callback['callback']($event);
  977. $table->getRecordListener()->$callback['callback']($event);
  978. }
  979. }
  980. // Invoke preQuery() hook on Doctrine_Query for child classes which implement this hook
  981. $this->preQuery();
  982. }
  983. /**
  984. * Returns an array of components to execute the query callbacks for
  985. *
  986. * @param array $params
  987. * @return array $components
  988. */
  989. protected function _getDqlCallbackComponents($params = array())
  990. {
  991. $componentsBefore = array();
  992. if ($this->isSubquery()) {
  993. $componentsBefore = $this->getQueryComponents();
  994. }
  995. $copy = $this->copy();
  996. $copy->getSqlQuery($params);
  997. $componentsAfter = $copy->getQueryComponents();
  998. if ($componentsBefore !== $componentsAfter) {
  999. return array_diff($componentsAfter, $componentsBefore);
  1000. } else {
  1001. return $componentsAfter;
  1002. }
  1003. }
  1004. /**
  1005. * Blank hook methods which can be implemented in Doctrine_Query child classes
  1006. *
  1007. * @return void
  1008. */
  1009. public function preQuery()
  1010. {
  1011. }
  1012. /**
  1013. * Constructs the query from the cached form.
  1014. *
  1015. * @param string The cached query, in a serialized form.
  1016. * @return array The custom component that was cached together with the essential
  1017. * query data. This can be either a result set (result caching)
  1018. * or an SQL query string (query caching).
  1019. */
  1020. protected function _constructQueryFromCache($cached)
  1021. {
  1022. $cached = unserialize($cached);
  1023. $this->_tableAliasMap = $cached[2];
  1024. $customComponent = $cached[0];
  1025. $queryComponents = array();
  1026. $cachedComponents = $cached[1];
  1027. foreach ($cachedComponents as $alias => $components) {
  1028. $e = explode('.', $components[0]);
  1029. if (count($e) === 1) {
  1030. $queryComponents[$alias]['table'] = $this->_conn->getTable($e[0]);
  1031. } else {
  1032. $queryComponents[$alias]['parent'] = $e[0];
  1033. $queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getRelation($e[1]);
  1034. $queryComponents[$alias]['table'] = $queryComponents[$alias]['relation']->getTable();
  1035. }
  1036. if (isset($components[1])) {
  1037. $queryComponents[$alias]['agg'] = $components[1];
  1038. }
  1039. if (isset($components[2])) {
  1040. $queryComponents[$alias]['map'] = $components[2];
  1041. }
  1042. }
  1043. $this->_queryComponents = $queryComponents;
  1044. return $customComponent;
  1045. }
  1046. /**
  1047. * getCachedForm
  1048. * returns the cached form of this query for given resultSet
  1049. *
  1050. * @param array $resultSet
  1051. * @return string serialized string representation of this query
  1052. */
  1053. public function getCachedForm($customComponent = null)
  1054. {
  1055. $componentInfo = array();
  1056. foreach ($this->getQueryComponents() as $alias => $components) {
  1057. if ( ! isset($components['parent'])) {
  1058. $componentInfo[$alias][] = $components['table']->getComponentName();
  1059. } else {
  1060. $componentInfo[$alias][] = $components['parent'] . '.' . $components['relation']->getAlias();
  1061. }
  1062. if (isset($components['agg'])) {
  1063. $componentInfo[$alias][] = $components['agg'];
  1064. }
  1065. if (isset($components['map'])) {
  1066. $componentInfo[$alias][] = $components['map'];
  1067. }
  1068. }
  1069. if ($customComponent instanceof Doctrine_Collection) {
  1070. foreach ($customComponent as $record) {
  1071. $record->serializeReferences(true);
  1072. }
  1073. }
  1074. return serialize(array($customComponent, $componentInfo, $this->getTableAliasMap()));
  1075. }
  1076. /**
  1077. * addSelect
  1078. * adds fields to the SELECT part of the query
  1079. *
  1080. * @param string $select Query SELECT part
  1081. * @return Doctrine_Query
  1082. */
  1083. public function addSelect($select)
  1084. {
  1085. return $this->_addDqlQueryPart('select', $select, true);
  1086. }
  1087. /**
  1088. * addTableAlias
  1089. * adds an alias for table and associates it with given component alias
  1090. *
  1091. * @param string $componentAlias the alias for the query component associated with given tableAlias
  1092. * @param string $tableAlias the table alias to be added
  1093. * @return Doctrine_Hydrate
  1094. * @deprecated
  1095. */
  1096. public function addTableAlias($tableAlias, $componentAlias)
  1097. {
  1098. return $this->addSqlTableAlias($tableAlias, $componentAlias);
  1099. }
  1100. /**
  1101. * addSqlTableAlias
  1102. * adds an SQL table alias and associates it a component alias
  1103. *
  1104. * @param string $componentAlias the alias for the query component associated with given tableAlias
  1105. * @param string $tableAlias the table alias to be added
  1106. * @return Doctrine_Query_Abstract
  1107. */
  1108. public function addSqlTableAlias($sqlTableAlias, $componentAlias)
  1109. {
  1110. $this->_tableAliasMap[$sqlTableAlias] = $componentAlias;
  1111. return $this;
  1112. }
  1113. /**
  1114. * addFrom
  1115. * adds fields to the FROM part of the query
  1116. *
  1117. * @param string $from Query FROM part
  1118. * @return Doctrine_Query
  1119. */
  1120. public function addFrom($from)
  1121. {
  1122. return $this->_addDqlQueryPart('from', $from, true);
  1123. }
  1124. /**
  1125. * addWhere
  1126. * adds conditions to the WHERE part of the query
  1127. *
  1128. * @param string $where Query WHERE part
  1129. * @param mixed $params an array of parameters or a simple scalar
  1130. * @return Doctrine_Query
  1131. */
  1132. public function addWhere($where, $params = array())
  1133. {
  1134. return $this->andWhere($where, $params);
  1135. }
  1136. /**
  1137. * Adds conditions to the WHERE part of the query
  1138. *
  1139. * @param string $where Query WHERE part
  1140. * @param mixed $params An array of parameters or a simple scalar
  1141. * @return Doctrine_Query
  1142. */
  1143. public function andWhere($where, $params = array())
  1144. {
  1145. if (is_array($params)) {
  1146. $this->_params['where'] = array_merge($this->_params['where'], $params);
  1147. } else {
  1148. $this->_params['where'][] = $params;
  1149. }
  1150. if ($this->_hasDqlQueryPart('where')) {
  1151. $this->_addDqlQueryPart('where', 'AND', true);
  1152. }
  1153. return $this->_addDqlQueryPart('where', $where, true);
  1154. }
  1155. /**
  1156. * Adds conditions to the WHERE part of the query
  1157. *
  1158. * @param string $where Query WHERE part
  1159. * @param mixed $params An array of parameters or a simple scalar
  1160. * @return Doctrine_Query
  1161. */
  1162. public function orWhere($where, $params = array())
  1163. {
  1164. if (is_array($params)) {
  1165. $this->_params['where'] = array_merge($this->_params['where'], $params);
  1166. } else {
  1167. $this->_params['where'][] = $params;
  1168. }
  1169. if ($this->_hasDqlQueryPart('where')) {
  1170. $this->_addDqlQueryPart('where', 'OR', true);
  1171. }
  1172. return $this->_addDqlQueryPart('where', $where, true);
  1173. }
  1174. /**
  1175. * whereIn
  1176. * adds IN condition to the query WHERE part
  1177. *
  1178. * @param string $expr the operand of the IN
  1179. * @param mixed $params an array of parameters or a simple scalar
  1180. * @param boolean $not whether or not to use NOT in front of IN
  1181. * @return Doctrine_Query
  1182. */
  1183. public function whereIn($expr, $params = array(), $not = false)
  1184. {
  1185. return $this->andWhereIn($expr, $params, $not);
  1186. }
  1187. /**
  1188. * Adds IN condition to the query WHERE part
  1189. *
  1190. * @param string $expr The operand of the IN
  1191. * @param mixed $params An array of parameters or a simple scalar
  1192. * @param boolean $not Whether or not to use NOT in front of IN
  1193. * @return Doctrine_Query
  1194. */
  1195. public function andWhereIn($expr, $params = array(), $not = false)
  1196. {
  1197. // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
  1198. if ( ! count($params)) {
  1199. return $this;
  1200. }
  1201. if ($this->_hasDqlQueryPart('where')) {
  1202. $this->_addDqlQueryPart('where', 'AND', true);
  1203. }
  1204. return $this->_addDqlQueryPart('where', $this->_processWhereIn($expr, $params, $not), true);
  1205. }
  1206. /**
  1207. * Adds IN condition to the query WHERE part
  1208. *
  1209. * @param string $expr The operand of the IN
  1210. * @param mixed $params An array of parameters or a simple scalar
  1211. * @param boolean $not Whether or not to use NOT in front of IN
  1212. * @return Doctrine_Query
  1213. */
  1214. public function orWhereIn($expr, $params = array(), $not = false)
  1215. {
  1216. // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
  1217. if ( ! count($params)) {
  1218. return $this;
  1219. }
  1220. if ($this->_hasDqlQueryPart('where')) {
  1221. $this->_addDqlQueryPart('where', 'OR', true);
  1222. }
  1223. return $this->_addDqlQueryPart('where', $this->_processWhereIn($expr, $params, $not), true);
  1224. }
  1225. /**
  1226. * @nodoc
  1227. */
  1228. protected function _processWhereIn($expr, $params = array(), $not = false)
  1229. {
  1230. $params = (array) $params;
  1231. // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
  1232. if ( ! count($params)) {
  1233. return $this;
  1234. }
  1235. $a = array();
  1236. foreach ($params as $k => $value) {
  1237. if ($value instanceof Doctrine_Expression) {
  1238. $value = $value->getSql();
  1239. unset($params[$k]);
  1240. } else {
  1241. $value = '?';
  1242. }
  1243. $a[] = $value;
  1244. }
  1245. $this->_params['where'] = array_merge($this->_params['where'], $params);
  1246. return $expr . ($not === true ? ' NOT ':'') . ' IN (' . implode(', ', $a) . ')';
  1247. }
  1248. /**
  1249. * whereNotIn
  1250. * adds NOT IN condition to the query WHERE part
  1251. *
  1252. * @param string $expr the operand of the NOT IN
  1253. * @param mixed $params an array of parameters or a simple scalar
  1254. * @return Doctrine_Query
  1255. */
  1256. public function whereNotIn($expr, $params = array())
  1257. {
  1258. return $this->whereIn($expr, $params, true);
  1259. }
  1260. /**
  1261. * Adds NOT IN condition to the query WHERE part
  1262. *
  1263. * @param string $expr The operand of the NOT IN
  1264. * @param mixed $params An array of parameters or a simple scalar
  1265. * @return Doctrine_Query
  1266. */
  1267. public function andWhereNotIn($expr, $params = array())
  1268. {
  1269. return $this->andWhereIn($expr, $params, true);
  1270. }
  1271. /**
  1272. * Adds NOT IN condition to the query WHERE part
  1273. *
  1274. * @param string $expr The operand of the NOT IN
  1275. * @param mixed $params An array of parameters or a simple scalar
  1276. * @return Doctrine_Query
  1277. */
  1278. public function orWhereNotIn($expr, $params = array())
  1279. {
  1280. return $this->orWhereIn($expr, $params, true);
  1281. }
  1282. /**
  1283. * addGroupBy
  1284. * adds fields to the GROUP BY part of the query
  1285. *
  1286. * @param string $groupby Query GROUP BY part
  1287. * @return Doctrine_Query
  1288. */
  1289. public function addGroupBy($groupby)
  1290. {
  1291. return $this->_addDqlQueryPart('groupby', $groupby, true);
  1292. }
  1293. /**
  1294. * addHaving
  1295. * adds conditions to the HAVING part of the query
  1296. *
  1297. * @param string $having Query HAVING part
  1298. * @param mixed $params an array of parameters or a simple scalar
  1299. * @return Doctrine_Query
  1300. */
  1301. public function addHaving($having, $params = array())
  1302. {
  1303. if (is_array($params)) {
  1304. $this->_params['having'] = array_merge($this->_params['having'], $params);
  1305. } else {
  1306. $this->_params['having'][] = $params;
  1307. }
  1308. return $this->_addDqlQueryPart('having', $having, true);
  1309. }
  1310. /**
  1311. * addOrderBy
  1312. * adds fields to the ORDER BY part of the query
  1313. *
  1314. * @param string $orderby Query ORDER BY part
  1315. * @return Doctrine_Query
  1316. */
  1317. public function addOrderBy($orderby)
  1318. {
  1319. return $this->_addDqlQueryPart('orderby', $orderby, true);
  1320. }
  1321. /**
  1322. * select
  1323. * sets the SELECT part of the query
  1324. *
  1325. * @param string $select Query SELECT part
  1326. * @return Doctrine_Query
  1327. */
  1328. public function select($select)
  1329. {
  1330. return $this->_addDqlQueryPart('select', $select);
  1331. }
  1332. /**
  1333. * distinct
  1334. * Makes the query SELECT DISTINCT.
  1335. *
  1336. * @param bool $flag Whether or not the SELECT is DISTINCT (default true).
  1337. * @return Doctrine_Query
  1338. */
  1339. public function distinct($flag = true)
  1340. {
  1341. $this->_sqlParts['distinct'] = (bool) $flag;
  1342. return $this;
  1343. }
  1344. /**
  1345. * forUpdate
  1346. * Makes the query SELECT FOR UPDATE.
  1347. *
  1348. * @param bool $flag Whether or not the SELECT is FOR UPDATE (default true).
  1349. * @return Doctrine_Query
  1350. */
  1351. public function forUpdate($flag = true)
  1352. {
  1353. $this->_sqlParts['forUpdate'] = (bool) $flag;
  1354. return $this;
  1355. }
  1356. /**
  1357. * delete
  1358. * sets the query type to DELETE
  1359. *
  1360. * @return Doctrine_Query
  1361. */
  1362. public function delete($from = null)
  1363. {
  1364. $this->_type = self::DELETE;
  1365. if ($from != null) {
  1366. return $this->_addDqlQueryPart('from', $from);
  1367. }
  1368. return $this;
  1369. }
  1370. /**
  1371. * update
  1372. * sets the UPDATE part of the query
  1373. *
  1374. * @param string $update Query UPDATE part
  1375. * @return Doctrine_Query
  1376. */
  1377. public function update($from = null)
  1378. {
  1379. $this->_type = self::UPDATE;
  1380. if ($from != null) {
  1381. return $this->_addDqlQueryPart('from', $from);
  1382. }
  1383. return $this;
  1384. }
  1385. /**
  1386. * set
  1387. * sets the SET part of the query
  1388. *
  1389. * @param string $update Query UPDATE part
  1390. * @return Doctrine_Query
  1391. */
  1392. public function set($key, $value, $params = null)
  1393. {
  1394. if (is_array($key)) {
  1395. foreach ($key as $k => $v) {
  1396. $this->set($k, '?', array($v));
  1397. }
  1398. return $this;
  1399. } else {
  1400. if ($params !== null) {
  1401. if (is_array($params)) {
  1402. $this->_params['set'] = array_merge($this->_params['set'], $params);
  1403. } else {
  1404. $this->_params['set'][] = $params;
  1405. }
  1406. }
  1407. return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
  1408. }
  1409. }
  1410. /**
  1411. * from
  1412. * sets the FROM part of the query
  1413. *
  1414. * @param string $from Query FROM part
  1415. * @return Doctrine_Query
  1416. */
  1417. public function from($from)
  1418. {
  1419. return $this->_addDqlQueryPart('from', $from);
  1420. }
  1421. /**
  1422. * innerJoin
  1423. * appends an INNER JOIN to the FROM part of the query
  1424. *
  1425. * @param string $join Query INNER JOIN
  1426. * @return Doctrine_Query
  1427. */
  1428. public function innerJoin($join, $params = array())
  1429. {
  1430. if (is_array($params)) {
  1431. $this->_params['join'] = array_merge($this->_params['join'], $params);
  1432. } else {
  1433. $this->_params['join'][] = $params;
  1434. }
  1435. return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
  1436. }
  1437. /**
  1438. * leftJoin
  1439. * appends a LEFT JOIN to the FROM part of the query
  1440. *
  1441. * @param string $join Query LEFT JOIN
  1442. * @return Doctrine_Query
  1443. */
  1444. public function leftJoin($join, $params = array())
  1445. {
  1446. if (is_array($params)) {
  1447. $this->_params['join'] = array_merge($this->_params['join'], $params);
  1448. } else {
  1449. $this->_params['join'][] = $params;
  1450. }
  1451. return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true);
  1452. }
  1453. /**
  1454. * groupBy
  1455. * sets the GROUP BY part of the query
  1456. *
  1457. * @param string $groupby Query GROUP BY part
  1458. * @return Doctrine_Query
  1459. */
  1460. public function groupBy($groupby)
  1461. {
  1462. return $this->_addDqlQueryPart('groupby', $groupby);
  1463. }
  1464. /**
  1465. * where
  1466. * sets the WHERE part of the query
  1467. *
  1468. * @param string $join Query WHERE part
  1469. * @param mixed $params an array of parameters or a simple scalar
  1470. * @return Doctrine_Query
  1471. */
  1472. public function where($where, $params = array())
  1473. {
  1474. $this->_params['where'] = array();
  1475. if (is_array($params)) {
  1476. $this->_params['where'] = $params;
  1477. } else {
  1478. $this->_params['where'][] = $params;
  1479. }
  1480. return $this->_addDqlQueryPart('where', $where);
  1481. }
  1482. /**
  1483. * having
  1484. * sets the HAVING part of the query
  1485. *
  1486. * @param string $having Query HAVING part
  1487. * @param mixed $params an array of parameters or a simple scalar
  1488. * @return Doctrine_Query
  1489. */
  1490. public function having($having, $params = array())
  1491. {
  1492. $this->_params['having'] = array();
  1493. if (is_array($params)) {
  1494. $this->_params['having'] = $params;
  1495. } else {
  1496. $this->_params['having'][] = $params;
  1497. }
  1498. return $this->_addDqlQueryPart('having', $having);
  1499. }
  1500. /**
  1501. * orderBy
  1502. * sets the ORDER BY part of the query
  1503. *
  1504. * @param string $orderby Query ORDER BY part
  1505. * @return Doctrine_Query
  1506. */
  1507. public function orderBy($orderby)
  1508. {
  1509. return $this->_addDqlQueryPart('orderby', $orderby);
  1510. }
  1511. /**
  1512. * limit
  1513. * sets the Query query limit
  1514. *
  1515. * @param integer $limit limit to be used for limiting the query results
  1516. * @return Doctrine_Query
  1517. */
  1518. public function limit($limit)
  1519. {
  1520. return $this->_addDqlQueryPart('limit', $limit);
  1521. }
  1522. /**
  1523. * offset
  1524. * sets the Query query offset
  1525. *
  1526. * @param integer $offset offset to be used for paginating the query
  1527. * @return Doctrine_Query
  1528. */
  1529. public function offset($offset)
  1530. {
  1531. return $this->_addDqlQueryPart('offset', $offset);
  1532. }
  1533. /**
  1534. * getSql
  1535. * shortcut for {@link getSqlQuery()}.
  1536. *
  1537. * @param array $params (optional)
  1538. * @return string sql query string
  1539. */
  1540. public function getSql($params = array())
  1541. {
  1542. return $this->getSqlQuery($params);
  1543. }
  1544. /**
  1545. * clear
  1546. * resets all the variables
  1547. *
  1548. * @return void
  1549. */
  1550. protected function clear()
  1551. {
  1552. $this->_sqlParts = array(
  1553. 'select' => array(),
  1554. 'distinct' => false,
  1555. 'forUpdate' => false,
  1556. 'from' => array(),
  1557. 'set' => array(),
  1558. 'join' => array(),
  1559. 'where' => array(),
  1560. 'groupby' => array(),
  1561. 'having' => array(),
  1562. 'orderby' => array(),
  1563. 'limit' => false,
  1564. 'offset' => false,
  1565. );
  1566. }
  1567. public function setHydrationMode($hydrationMode)
  1568. {
  1569. $this->_hydrator->setHydrationMode($hydrationMode);
  1570. return $this;
  1571. }
  1572. /**
  1573. * @deprecated
  1574. */
  1575. public function getAliasMap()
  1576. {
  1577. return $this->_queryComponents;
  1578. }
  1579. /**
  1580. * Gets the components of this query.
  1581. */
  1582. public function getQueryComponents()
  1583. {
  1584. return $this->_queryComponents;
  1585. }
  1586. /**
  1587. * Return the SQL parts.
  1588. *
  1589. * @return array The parts
  1590. * @deprecated
  1591. */
  1592. public function getParts()
  1593. {
  1594. return $this->getSqlParts();
  1595. }
  1596. /**
  1597. * Return the SQL parts.
  1598. *
  1599. * @return array The parts
  1600. */
  1601. public function getSqlParts()
  1602. {
  1603. return $this->_sqlParts;
  1604. }
  1605. /**
  1606. * getType
  1607. *
  1608. * returns the type of this query object
  1609. * by default the type is Doctrine_Query_Abstract::SELECT but if update() or delete()
  1610. * are being called the type is Doctrine_Query_Abstract::UPDATE and Doctrine_Query_Abstract::DELETE,
  1611. * respectively
  1612. *
  1613. * @see Doctrine_Query_Abstract::SELECT
  1614. * @see Doctrine_Query_Abstract::UPDATE
  1615. * @see Doctrine_Query_Abstract::DELETE
  1616. *
  1617. * @return integer return the query type
  1618. */
  1619. public function getType()
  1620. {
  1621. return $this->_type;
  1622. }
  1623. /**
  1624. * useCache
  1625. *
  1626. * @param Doctrine_Cache_Interface|bool $driver cache driver
  1627. * @param integer $timeToLive how long the cache entry is valid
  1628. * @return Doctrine_Hydrate this object
  1629. * @deprecated Use useResultCache()
  1630. */
  1631. public function useCache($driver = true, $timeToLive = null)
  1632. {
  1633. return $this->useResultCache($driver, $timeToLive);
  1634. }
  1635. /**
  1636. * useResultCache
  1637. *
  1638. * @param Doctrine_Cache_Interface|bool $driver cache driver
  1639. * @param integer $timeToLive how long the cache entry is valid
  1640. * @return Doctrine_Hydrate this object
  1641. */
  1642. public function useResultCache($driver = true, $timeToLive = null)
  1643. {
  1644. if ($driver !== null && $driver !== true && ! ($driver instanceOf Doctrine_Cache_Interface)) {
  1645. $msg = 'First argument should be instance of Doctrine_Cache_Interface or null.';
  1646. throw new Doctrine_Query_Exception($msg);
  1647. }
  1648. $this->_resultCache = $driver;
  1649. if ($timeToLive !== null) {
  1650. $this->setResultCacheLifeSpan($timeToLive);
  1651. }
  1652. return $this;
  1653. }
  1654. /**
  1655. * useQueryCache
  1656. *
  1657. * @param Doctrine_Cache_Interface|bool $driver cache driver
  1658. * @param integer $timeToLive how long the cache entry is valid
  1659. * @return Doctrine_Hydrate this object
  1660. */
  1661. public function useQueryCache($driver = true, $timeToLive = null)
  1662. {
  1663. if ($driver !== null && $driver !== true && $driver !== false && ! ($driver instanceOf Doctrine_Cache_Interface)) {
  1664. $msg = 'First argument should be instance of Doctrine_Cache_Interface or null.';
  1665. throw new Doctrine_Query_Exception($msg);
  1666. }
  1667. $this->_queryCache = $driver;
  1668. if ($timeToLive !== null) {
  1669. $this->setQueryCacheLifeSpan($timeToLive);
  1670. }
  1671. return $this;
  1672. }
  1673. /**
  1674. * expireCache
  1675. *
  1676. * @param boolean $expire whether or not to force cache expiration
  1677. * @return Doctrine_Hydrate this object
  1678. * @deprecated Use expireResultCache()
  1679. */
  1680. public function expireCache($expire = true)
  1681. {
  1682. return $this->expireResultCache($expire);
  1683. }
  1684. /**
  1685. * expireCache
  1686. *
  1687. * @param boolean $expire whether or not to force cache expiration
  1688. * @return Doctrine_Hydrate this object
  1689. */
  1690. public function expireResultCache($expire = true)
  1691. {
  1692. $this->_expireResultCache = true;
  1693. return $this;
  1694. }
  1695. /**
  1696. * expireQueryCache
  1697. *
  1698. * @param boolean $expire whether or not to force cache expiration
  1699. * @return Doctrine_Hydrate this object
  1700. */
  1701. public function expireQueryCache($expire = true)
  1702. {
  1703. $this->_expireQueryCache = true;
  1704. return $this;
  1705. }
  1706. /**
  1707. * setCacheLifeSpan
  1708. *
  1709. * @param integer $timeToLive how long the cache entry is valid
  1710. * @return Doctrine_Hydrate this object
  1711. * @deprecated Use setResultCacheLifeSpan()
  1712. */
  1713. public function setCacheLifeSpan($timeToLive)
  1714. {
  1715. return $this->setResultCacheLifeSpan($timeToLive);
  1716. }
  1717. /**
  1718. * setResultCacheLifeSpan
  1719. *
  1720. * @param integer $timeToLive how long the cache entry is valid (in seconds)
  1721. * @return Doctrine_Hydrate this object
  1722. */
  1723. public function setResultCacheLifeSpan($timeToLive)
  1724. {
  1725. if ($timeToLive !== null) {
  1726. $timeToLive = (int) $timeToLive;
  1727. }
  1728. $this->_resultCacheTTL = $timeToLive;
  1729. return $this;
  1730. }
  1731. /**
  1732. * Gets the life span of the result cache in seconds.
  1733. *
  1734. * @return integer
  1735. */
  1736. public function getResultCacheLifeSpan()
  1737. {
  1738. return $this->_resultCacheTTL;
  1739. }
  1740. /**
  1741. * setQueryCacheLifeSpan
  1742. *
  1743. * @param integer $timeToLive how long the cache entry is valid
  1744. * @return Doctrine_Hydrate this object
  1745. */
  1746. public function setQueryCacheLifeSpan($timeToLive)
  1747. {
  1748. if ($timeToLive !== null) {
  1749. $timeToLive = (int) $timeToLive;
  1750. }
  1751. $this->_queryCacheTTL = $timeToLive;
  1752. return $this;
  1753. }
  1754. /**
  1755. * Gets the life span of the query cache the Query object is using.
  1756. *
  1757. * @return integer The life span in seconds.
  1758. */
  1759. public function getQueryCacheLifeSpan()
  1760. {
  1761. return $this->_queryCacheTTL;
  1762. }
  1763. /**
  1764. * getCacheDriver
  1765. * returns the cache driver associated with this object
  1766. *
  1767. * @return Doctrine_Cache_Interface|boolean|null cache driver
  1768. * @deprecated Use getResultCacheDriver()
  1769. */
  1770. public function getCacheDriver()
  1771. {
  1772. return $this->getResultCacheDriver();
  1773. }
  1774. /**
  1775. * getResultCacheDriver
  1776. * returns the cache driver used for caching result sets
  1777. *
  1778. * @return Doctrine_Cache_Interface|boolean|null cache driver
  1779. */
  1780. public function getResultCacheDriver()
  1781. {
  1782. if ($this->_resultCache instanceof Doctrine_Cache_Interface) {
  1783. return $this->_resultCache;
  1784. } else {
  1785. return $this->_conn->getResultCacheDriver();
  1786. }
  1787. }
  1788. /**
  1789. * getQueryCacheDriver
  1790. * returns the cache driver used for caching queries
  1791. *
  1792. * @return Doctrine_Cache_Interface|boolean|null cache driver
  1793. */
  1794. public function getQueryCacheDriver()
  1795. {
  1796. if ($this->_queryCache instanceof Doctrine_Cache_Interface) {
  1797. return $this->_queryCache;
  1798. } else {
  1799. return $this->_conn->getQueryCacheDriver();
  1800. }
  1801. }
  1802. /**
  1803. * getConnection
  1804. *
  1805. * @return Doctrine_Connection
  1806. */
  1807. public function getConnection()
  1808. {
  1809. return $this->_conn;
  1810. }
  1811. /**
  1812. * Checks if there's at least one DQL part defined to the internal parts collection.
  1813. *
  1814. * @param string $queryPartName The name of the query part.
  1815. * @return boolean
  1816. */
  1817. protected function _hasDqlQueryPart($queryPartName)
  1818. {
  1819. return count($this->_dqlParts[$queryPartName]) > 0;
  1820. }
  1821. /**
  1822. * Adds a DQL part to the internal parts collection.
  1823. *
  1824. * @param string $queryPartName The name of the query part.
  1825. * @param string $queryPart The actual query part to add.
  1826. * @param boolean $append Whether to append $queryPart to already existing
  1827. * parts under the same $queryPartName. Defaults to FALSE
  1828. * (previously added parts with the same name get overridden).
  1829. */
  1830. protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
  1831. {
  1832. // We should prevent nullable query parts
  1833. if ($queryPart === null) {
  1834. throw new Doctrine_Query_Exception('Cannot define NULL as part of query when defining \'' . $queryPartName . '\'.');
  1835. }
  1836. if ($append) {
  1837. $this->_dqlParts[$queryPartName][] = $queryPart;
  1838. } else {
  1839. $this->_dqlParts[$queryPartName] = array($queryPart);
  1840. }
  1841. $this->_state = Doctrine_Query::STATE_DIRTY;
  1842. return $this;
  1843. }
  1844. /**
  1845. * _processDqlQueryPart
  1846. * parses given query part
  1847. *
  1848. * @param string $queryPartName the name of the query part
  1849. * @param array $queryParts an array containing the query part data
  1850. * @return Doctrine_Query this object
  1851. * @todo Better description. "parses given query part" ??? Then wheres the difference
  1852. * between process/parseQueryPart? I suppose this does something different.
  1853. */
  1854. protected function _processDqlQueryPart($queryPartName, $queryParts)
  1855. {
  1856. $this->removeSqlQueryPart($queryPartName);
  1857. if (is_array($queryParts) && ! empty($queryParts)) {
  1858. foreach ($queryParts as $queryPart) {
  1859. $parser = $this->_getParser($queryPartName);
  1860. $sql = $parser->parse($queryPart);
  1861. if (isset($sql)) {
  1862. if ($queryPartName == 'limit' || $queryPartName == 'offset') {
  1863. $this->setSqlQueryPart($queryPartName, $sql);
  1864. } else {
  1865. $this->addSqlQueryPart($queryPartName, $sql);
  1866. }
  1867. }
  1868. }
  1869. }
  1870. }
  1871. /**
  1872. * _getParser
  1873. * parser lazy-loader
  1874. *
  1875. * @throws Doctrine_Query_Exception if unknown parser name given
  1876. * @return Doctrine_Query_Part
  1877. * @todo Doc/Description: What is the parameter for? Which parsers are available?
  1878. */
  1879. protected function _getParser($name)
  1880. {
  1881. if ( ! isset($this->_parsers[$name])) {
  1882. $class = 'Doctrine_Query_' . ucwords(strtolower($name));
  1883. Doctrine::autoload($class);
  1884. if ( ! class_exists($class)) {
  1885. throw new Doctrine_Query_Exception('Unknown parser ' . $name);
  1886. }
  1887. $this->_parsers[$name] = new $class($this, $this->_tokenizer);
  1888. }
  1889. return $this->_parsers[$name];
  1890. }
  1891. /**
  1892. * Gets the SQL query that corresponds to this query object.
  1893. * The returned SQL syntax depends on the connection driver that is used
  1894. * by this query object at the time of this method call.
  1895. *
  1896. * @param array $params
  1897. */
  1898. abstract public function getSqlQuery($params = array());
  1899. /**
  1900. * parseDqlQuery
  1901. * parses a dql query
  1902. *
  1903. * @param string $query query to be parsed
  1904. * @return Doctrine_Query_Abstract this object
  1905. */
  1906. abstract public function parseDqlQuery($query);
  1907. /**
  1908. * @deprecated
  1909. */
  1910. public function parseQuery($query)
  1911. {
  1912. return $this->parseDqlQuery($query);
  1913. }
  1914. /**
  1915. * @deprecated
  1916. */
  1917. public function getQuery($params = array())
  1918. {
  1919. return $this->getSqlQuery($params);
  1920. }
  1921. }