AbstractQuery.php 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ORM;
  20. use Doctrine\Common\Util\ClassUtils;
  21. use Doctrine\Common\Collections\ArrayCollection;
  22. use Doctrine\DBAL\Types\Type;
  23. use Doctrine\DBAL\Cache\QueryCacheProfile;
  24. use Doctrine\ORM\Query\QueryException;
  25. /**
  26. * Base contract for ORM queries. Base class for Query and NativeQuery.
  27. *
  28. * @link www.doctrine-project.org
  29. * @since 2.0
  30. * @author Benjamin Eberlei <kontakt@beberlei.de>
  31. * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
  32. * @author Jonathan Wage <jonwage@gmail.com>
  33. * @author Roman Borschel <roman@code-factory.org>
  34. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
  35. */
  36. abstract class AbstractQuery
  37. {
  38. /* Hydration mode constants */
  39. /**
  40. * Hydrates an object graph. This is the default behavior.
  41. */
  42. const HYDRATE_OBJECT = 1;
  43. /**
  44. * Hydrates an array graph.
  45. */
  46. const HYDRATE_ARRAY = 2;
  47. /**
  48. * Hydrates a flat, rectangular result set with scalar values.
  49. */
  50. const HYDRATE_SCALAR = 3;
  51. /**
  52. * Hydrates a single scalar value.
  53. */
  54. const HYDRATE_SINGLE_SCALAR = 4;
  55. /**
  56. * Very simple object hydrator (optimized for performance).
  57. */
  58. const HYDRATE_SIMPLEOBJECT = 5;
  59. /**
  60. * @var \Doctrine\Common\Collections\ArrayCollection The parameter map of this query.
  61. */
  62. protected $parameters;
  63. /**
  64. * @var ResultSetMapping The user-specified ResultSetMapping to use.
  65. */
  66. protected $_resultSetMapping;
  67. /**
  68. * @var \Doctrine\ORM\EntityManager The entity manager used by this query object.
  69. */
  70. protected $_em;
  71. /**
  72. * @var array The map of query hints.
  73. */
  74. protected $_hints = array();
  75. /**
  76. * @var integer The hydration mode.
  77. */
  78. protected $_hydrationMode = self::HYDRATE_OBJECT;
  79. /**
  80. * @param \Doctrine\DBAL\Cache\QueryCacheProfile
  81. */
  82. protected $_queryCacheProfile;
  83. /**
  84. * @var boolean Boolean value that indicates whether or not expire the result cache.
  85. */
  86. protected $_expireResultCache = false;
  87. /**
  88. * @param \Doctrine\DBAL\Cache\QueryCacheProfile
  89. */
  90. protected $_hydrationCacheProfile;
  91. /**
  92. * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
  93. *
  94. * @param \Doctrine\ORM\EntityManager $entityManager
  95. */
  96. public function __construct(EntityManager $em)
  97. {
  98. $this->_em = $em;
  99. $this->parameters = new ArrayCollection();
  100. }
  101. /**
  102. * Gets the SQL query that corresponds to this query object.
  103. * The returned SQL syntax depends on the connection driver that is used
  104. * by this query object at the time of this method call.
  105. *
  106. * @return string SQL query
  107. */
  108. abstract public function getSQL();
  109. /**
  110. * Retrieves the associated EntityManager of this Query instance.
  111. *
  112. * @return \Doctrine\ORM\EntityManager
  113. */
  114. public function getEntityManager()
  115. {
  116. return $this->_em;
  117. }
  118. /**
  119. * Frees the resources used by the query object.
  120. *
  121. * Resets Parameters, Parameter Types and Query Hints.
  122. *
  123. * @return void
  124. */
  125. public function free()
  126. {
  127. $this->parameters = new ArrayCollection();
  128. $this->_hints = array();
  129. }
  130. /**
  131. * Get all defined parameters.
  132. *
  133. * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters.
  134. */
  135. public function getParameters()
  136. {
  137. return $this->parameters;
  138. }
  139. /**
  140. * Gets a query parameter.
  141. *
  142. * @param mixed $key The key (index or name) of the bound parameter.
  143. *
  144. * @return mixed The value of the bound parameter.
  145. */
  146. public function getParameter($key)
  147. {
  148. $filteredParameters = $this->parameters->filter(
  149. function ($parameter) use ($key)
  150. {
  151. // Must not be identical because of string to integer conversion
  152. return ($key == $parameter->getName());
  153. }
  154. );
  155. return count($filteredParameters) ? $filteredParameters->first() : null;
  156. }
  157. /**
  158. * Sets a collection of query parameters.
  159. *
  160. * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters
  161. *
  162. * @return \Doctrine\ORM\AbstractQuery This query instance.
  163. */
  164. public function setParameters($parameters)
  165. {
  166. // BC compatibility with 2.3-
  167. if (is_array($parameters)) {
  168. $parameterCollection = new ArrayCollection();
  169. foreach ($parameters as $key => $value) {
  170. $parameter = new Query\Parameter($key, $value);
  171. $parameterCollection->add($parameter);
  172. }
  173. $parameters = $parameterCollection;
  174. }
  175. $this->parameters = $parameters;
  176. return $this;
  177. }
  178. /**
  179. * Sets a query parameter.
  180. *
  181. * @param string|integer $key The parameter position or name.
  182. * @param mixed $value The parameter value.
  183. * @param string $type The parameter type. If specified, the given value will be run through
  184. * the type conversion of this type. This is usually not needed for
  185. * strings and numeric types.
  186. *
  187. * @return \Doctrine\ORM\AbstractQuery This query instance.
  188. */
  189. public function setParameter($key, $value, $type = null)
  190. {
  191. $filteredParameters = $this->parameters->filter(
  192. function ($parameter) use ($key)
  193. {
  194. // Must not be identical because of string to integer conversion
  195. return ($key == $parameter->getName());
  196. }
  197. );
  198. if (count($filteredParameters)) {
  199. $parameter = $filteredParameters->first();
  200. $parameter->setValue($value, $type);
  201. return $this;
  202. }
  203. $parameter = new Query\Parameter($key, $value, $type);
  204. $this->parameters->add($parameter);
  205. return $this;
  206. }
  207. /**
  208. * Process an individual parameter value
  209. *
  210. * @param mixed $value
  211. * @return array
  212. */
  213. public function processParameterValue($value)
  214. {
  215. switch (true) {
  216. case is_array($value):
  217. foreach ($value as $key => $paramValue) {
  218. $paramValue = $this->processParameterValue($paramValue);
  219. $value[$key] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
  220. }
  221. return $value;
  222. case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value)):
  223. return $this->convertObjectParameterToScalarValue($value);
  224. default:
  225. return $value;
  226. }
  227. }
  228. private function convertObjectParameterToScalarValue($value)
  229. {
  230. $class = $this->_em->getClassMetadata(get_class($value));
  231. if ($class->isIdentifierComposite) {
  232. throw new \InvalidArgumentException(
  233. "Binding an entity with a composite primary key to a query is not supported. " .
  234. "You should split the parameter into the explicit fields and bind them seperately."
  235. );
  236. }
  237. $values = ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED)
  238. ? $this->_em->getUnitOfWork()->getEntityIdentifier($value)
  239. : $class->getIdentifierValues($value);
  240. $value = $values[$class->getSingleIdentifierFieldName()];
  241. if (null === $value) {
  242. throw new \InvalidArgumentException(
  243. "Binding entities to query parameters only allowed for entities that have an identifier."
  244. );
  245. }
  246. return $value;
  247. }
  248. /**
  249. * Sets the ResultSetMapping that should be used for hydration.
  250. *
  251. * @param ResultSetMapping $rsm
  252. * @return \Doctrine\ORM\AbstractQuery
  253. */
  254. public function setResultSetMapping(Query\ResultSetMapping $rsm)
  255. {
  256. $this->_resultSetMapping = $rsm;
  257. return $this;
  258. }
  259. /**
  260. * Set a cache profile for hydration caching.
  261. *
  262. * If no result cache driver is set in the QueryCacheProfile, the default
  263. * result cache driver is used from the configuration.
  264. *
  265. * Important: Hydration caching does NOT register entities in the
  266. * UnitOfWork when retrieved from the cache. Never use result cached
  267. * entities for requests that also flush the EntityManager. If you want
  268. * some form of caching with UnitOfWork registration you should use
  269. * {@see AbstractQuery::setResultCacheProfile()}.
  270. *
  271. * @example
  272. * $lifetime = 100;
  273. * $resultKey = "abc";
  274. * $query->setHydrationCacheProfile(new QueryCacheProfile());
  275. * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
  276. *
  277. * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
  278. * @return \Doctrine\ORM\AbstractQuery
  279. */
  280. public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
  281. {
  282. if ( ! $profile->getResultCacheDriver()) {
  283. $resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
  284. $profile = $profile->setResultCacheDriver($resultCacheDriver);
  285. }
  286. $this->_hydrationCacheProfile = $profile;
  287. return $this;
  288. }
  289. /**
  290. * @return \Doctrine\DBAL\Cache\QueryCacheProfile
  291. */
  292. public function getHydrationCacheProfile()
  293. {
  294. return $this->_hydrationCacheProfile;
  295. }
  296. /**
  297. * Set a cache profile for the result cache.
  298. *
  299. * If no result cache driver is set in the QueryCacheProfile, the default
  300. * result cache driver is used from the configuration.
  301. *
  302. * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
  303. * @return \Doctrine\ORM\AbstractQuery
  304. */
  305. public function setResultCacheProfile(QueryCacheProfile $profile = null)
  306. {
  307. if ( ! $profile->getResultCacheDriver()) {
  308. $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
  309. $profile = $profile->setResultCacheDriver($resultCacheDriver);
  310. }
  311. $this->_queryCacheProfile = $profile;
  312. return $this;
  313. }
  314. /**
  315. * Defines a cache driver to be used for caching result sets and implictly enables caching.
  316. *
  317. * @param \Doctrine\Common\Cache\Cache $driver Cache driver
  318. * @return \Doctrine\ORM\AbstractQuery
  319. */
  320. public function setResultCacheDriver($resultCacheDriver = null)
  321. {
  322. if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
  323. throw ORMException::invalidResultCacheDriver();
  324. }
  325. $this->_queryCacheProfile = $this->_queryCacheProfile
  326. ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
  327. : new QueryCacheProfile(0, null, $resultCacheDriver);
  328. return $this;
  329. }
  330. /**
  331. * Returns the cache driver used for caching result sets.
  332. *
  333. * @deprecated
  334. * @return \Doctrine\Common\Cache\Cache Cache driver
  335. */
  336. public function getResultCacheDriver()
  337. {
  338. if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
  339. return $this->_queryCacheProfile->getResultCacheDriver();
  340. }
  341. return $this->_em->getConfiguration()->getResultCacheImpl();
  342. }
  343. /**
  344. * Set whether or not to cache the results of this query and if so, for
  345. * how long and which ID to use for the cache entry.
  346. *
  347. * @param boolean $bool
  348. * @param integer $lifetime
  349. * @param string $resultCacheId
  350. * @return \Doctrine\ORM\AbstractQuery This query instance.
  351. */
  352. public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
  353. {
  354. if ($bool) {
  355. $this->setResultCacheLifetime($lifetime);
  356. $this->setResultCacheId($resultCacheId);
  357. return $this;
  358. }
  359. $this->_queryCacheProfile = null;
  360. return $this;
  361. }
  362. /**
  363. * Defines how long the result cache will be active before expire.
  364. *
  365. * @param integer $lifetime How long the cache entry is valid.
  366. * @return \Doctrine\ORM\AbstractQuery This query instance.
  367. */
  368. public function setResultCacheLifetime($lifetime)
  369. {
  370. $lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
  371. $this->_queryCacheProfile = $this->_queryCacheProfile
  372. ? $this->_queryCacheProfile->setLifetime($lifetime)
  373. : new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl());
  374. return $this;
  375. }
  376. /**
  377. * Retrieves the lifetime of resultset cache.
  378. *
  379. * @deprecated
  380. * @return integer
  381. */
  382. public function getResultCacheLifetime()
  383. {
  384. return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
  385. }
  386. /**
  387. * Defines if the result cache is active or not.
  388. *
  389. * @param boolean $expire Whether or not to force resultset cache expiration.
  390. * @return \Doctrine\ORM\AbstractQuery This query instance.
  391. */
  392. public function expireResultCache($expire = true)
  393. {
  394. $this->_expireResultCache = $expire;
  395. return $this;
  396. }
  397. /**
  398. * Retrieves if the resultset cache is active or not.
  399. *
  400. * @return boolean
  401. */
  402. public function getExpireResultCache()
  403. {
  404. return $this->_expireResultCache;
  405. }
  406. /**
  407. * @return QueryCacheProfile
  408. */
  409. public function getQueryCacheProfile()
  410. {
  411. return $this->_queryCacheProfile;
  412. }
  413. /**
  414. * Change the default fetch mode of an association for this query.
  415. *
  416. * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
  417. *
  418. * @param string $class
  419. * @param string $assocName
  420. * @param int $fetchMode
  421. * @return AbstractQuery
  422. */
  423. public function setFetchMode($class, $assocName, $fetchMode)
  424. {
  425. if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
  426. $fetchMode = Mapping\ClassMetadata::FETCH_LAZY;
  427. }
  428. $this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
  429. return $this;
  430. }
  431. /**
  432. * Defines the processing mode to be used during hydration / result set transformation.
  433. *
  434. * @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
  435. * One of the Query::HYDRATE_* constants.
  436. * @return \Doctrine\ORM\AbstractQuery This query instance.
  437. */
  438. public function setHydrationMode($hydrationMode)
  439. {
  440. $this->_hydrationMode = $hydrationMode;
  441. return $this;
  442. }
  443. /**
  444. * Gets the hydration mode currently used by the query.
  445. *
  446. * @return integer
  447. */
  448. public function getHydrationMode()
  449. {
  450. return $this->_hydrationMode;
  451. }
  452. /**
  453. * Gets the list of results for the query.
  454. *
  455. * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
  456. *
  457. * @return array
  458. */
  459. public function getResult($hydrationMode = self::HYDRATE_OBJECT)
  460. {
  461. return $this->execute(null, $hydrationMode);
  462. }
  463. /**
  464. * Gets the array of results for the query.
  465. *
  466. * Alias for execute(null, HYDRATE_ARRAY).
  467. *
  468. * @return array
  469. */
  470. public function getArrayResult()
  471. {
  472. return $this->execute(null, self::HYDRATE_ARRAY);
  473. }
  474. /**
  475. * Gets the scalar results for the query.
  476. *
  477. * Alias for execute(null, HYDRATE_SCALAR).
  478. *
  479. * @return array
  480. */
  481. public function getScalarResult()
  482. {
  483. return $this->execute(null, self::HYDRATE_SCALAR);
  484. }
  485. /**
  486. * Get exactly one result or null.
  487. *
  488. * @throws NonUniqueResultException
  489. * @param int $hydrationMode
  490. * @return mixed
  491. */
  492. public function getOneOrNullResult($hydrationMode = null)
  493. {
  494. $result = $this->execute(null, $hydrationMode);
  495. if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
  496. return null;
  497. }
  498. if ( ! is_array($result)) {
  499. return $result;
  500. }
  501. if (count($result) > 1) {
  502. throw new NonUniqueResultException;
  503. }
  504. return array_shift($result);
  505. }
  506. /**
  507. * Gets the single result of the query.
  508. *
  509. * Enforces the presence as well as the uniqueness of the result.
  510. *
  511. * If the result is not unique, a NonUniqueResultException is thrown.
  512. * If there is no result, a NoResultException is thrown.
  513. *
  514. * @param integer $hydrationMode
  515. * @return mixed
  516. * @throws NonUniqueResultException If the query result is not unique.
  517. * @throws NoResultException If the query returned no result.
  518. */
  519. public function getSingleResult($hydrationMode = null)
  520. {
  521. $result = $this->execute(null, $hydrationMode);
  522. if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
  523. throw new NoResultException;
  524. }
  525. if ( ! is_array($result)) {
  526. return $result;
  527. }
  528. if (count($result) > 1) {
  529. throw new NonUniqueResultException;
  530. }
  531. return array_shift($result);
  532. }
  533. /**
  534. * Gets the single scalar result of the query.
  535. *
  536. * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
  537. *
  538. * @return mixed
  539. * @throws QueryException If the query result is not unique.
  540. */
  541. public function getSingleScalarResult()
  542. {
  543. return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
  544. }
  545. /**
  546. * Sets a query hint. If the hint name is not recognized, it is silently ignored.
  547. *
  548. * @param string $name The name of the hint.
  549. * @param mixed $value The value of the hint.
  550. * @return \Doctrine\ORM\AbstractQuery
  551. */
  552. public function setHint($name, $value)
  553. {
  554. $this->_hints[$name] = $value;
  555. return $this;
  556. }
  557. /**
  558. * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
  559. *
  560. * @param string $name The name of the hint.
  561. * @return mixed The value of the hint or FALSE, if the hint name is not recognized.
  562. */
  563. public function getHint($name)
  564. {
  565. return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
  566. }
  567. /**
  568. * Return the key value map of query hints that are currently set.
  569. *
  570. * @return array
  571. */
  572. public function getHints()
  573. {
  574. return $this->_hints;
  575. }
  576. /**
  577. * Executes the query and returns an IterableResult that can be used to incrementally
  578. * iterate over the result.
  579. *
  580. * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters.
  581. * @param integer $hydrationMode The hydration mode to use.
  582. * @return \Doctrine\ORM\Internal\Hydration\IterableResult
  583. */
  584. public function iterate($parameters = null, $hydrationMode = null)
  585. {
  586. if ($hydrationMode !== null) {
  587. $this->setHydrationMode($hydrationMode);
  588. }
  589. if ( ! empty($parameters)) {
  590. $this->setParameters($parameters);
  591. }
  592. $stmt = $this->_doExecute();
  593. return $this->_em->newHydrator($this->_hydrationMode)->iterate(
  594. $stmt, $this->_resultSetMapping, $this->_hints
  595. );
  596. }
  597. /**
  598. * Executes the query.
  599. *
  600. * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters Query parameters.
  601. * @param integer $hydrationMode Processing mode to be used during the hydration process.
  602. * @return mixed
  603. */
  604. public function execute($parameters = null, $hydrationMode = null)
  605. {
  606. if ($hydrationMode !== null) {
  607. $this->setHydrationMode($hydrationMode);
  608. }
  609. if ( ! empty($parameters)) {
  610. $this->setParameters($parameters);
  611. }
  612. $setCacheEntry = function() {};
  613. if ($this->_hydrationCacheProfile !== null) {
  614. list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
  615. $queryCacheProfile = $this->getHydrationCacheProfile();
  616. $cache = $queryCacheProfile->getResultCacheDriver();
  617. $result = $cache->fetch($cacheKey);
  618. if (isset($result[$realCacheKey])) {
  619. return $result[$realCacheKey];
  620. }
  621. if ( ! $result) {
  622. $result = array();
  623. }
  624. $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
  625. $result[$realCacheKey] = $data;
  626. $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
  627. };
  628. }
  629. $stmt = $this->_doExecute();
  630. if (is_numeric($stmt)) {
  631. $setCacheEntry($stmt);
  632. return $stmt;
  633. }
  634. $data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
  635. $stmt, $this->_resultSetMapping, $this->_hints
  636. );
  637. $setCacheEntry($data);
  638. return $data;
  639. }
  640. /**
  641. * Get the result cache id to use to store the result set cache entry.
  642. * Will return the configured id if it exists otherwise a hash will be
  643. * automatically generated for you.
  644. *
  645. * @return array ($key, $hash)
  646. */
  647. protected function getHydrationCacheId()
  648. {
  649. $parameters = array();
  650. foreach ($this->getParameters() as $parameter) {
  651. $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
  652. }
  653. $sql = $this->getSQL();
  654. $queryCacheProfile = $this->getHydrationCacheProfile();
  655. $hints = $this->getHints();
  656. $hints['hydrationMode'] = $this->getHydrationMode();
  657. ksort($hints);
  658. return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints);
  659. }
  660. /**
  661. * Set the result cache id to use to store the result set cache entry.
  662. * If this is not explicitly set by the developer then a hash is automatically
  663. * generated for you.
  664. *
  665. * @param string $id
  666. * @return \Doctrine\ORM\AbstractQuery This query instance.
  667. */
  668. public function setResultCacheId($id)
  669. {
  670. $this->_queryCacheProfile = $this->_queryCacheProfile
  671. ? $this->_queryCacheProfile->setCacheKey($id)
  672. : new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl());
  673. return $this;
  674. }
  675. /**
  676. * Get the result cache id to use to store the result set cache entry if set.
  677. *
  678. * @deprecated
  679. * @return string
  680. */
  681. public function getResultCacheId()
  682. {
  683. return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
  684. }
  685. /**
  686. * Executes the query and returns a the resulting Statement object.
  687. *
  688. * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
  689. */
  690. abstract protected function _doExecute();
  691. /**
  692. * Cleanup Query resource when clone is called.
  693. *
  694. * @return void
  695. */
  696. public function __clone()
  697. {
  698. $this->parameters = new ArrayCollection();
  699. $this->_hints = array();
  700. }
  701. }