vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php line 749

Open in your IDE?
  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\Collection;
  22. use Doctrine\Common\Collections\ArrayCollection;
  23. use Doctrine\DBAL\Cache\QueryCacheProfile;
  24. use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
  25. use Doctrine\ORM\Query\Parameter;
  26. use Doctrine\ORM\Cache\QueryCacheKey;
  27. use Doctrine\Persistence\Mapping\MappingException;
  28. /**
  29.  * Base contract for ORM queries. Base class for Query and NativeQuery.
  30.  *
  31.  * @link    www.doctrine-project.org
  32.  * @since   2.0
  33.  * @author  Benjamin Eberlei <kontakt@beberlei.de>
  34.  * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
  35.  * @author  Jonathan Wage <jonwage@gmail.com>
  36.  * @author  Roman Borschel <roman@code-factory.org>
  37.  * @author  Konsta Vesterinen <kvesteri@cc.hut.fi>
  38.  */
  39. abstract class AbstractQuery
  40. {
  41.     /* Hydration mode constants */
  42.     /**
  43.      * Hydrates an object graph. This is the default behavior.
  44.      */
  45.     const HYDRATE_OBJECT 1;
  46.     /**
  47.      * Hydrates an array graph.
  48.      */
  49.     const HYDRATE_ARRAY 2;
  50.     /**
  51.      * Hydrates a flat, rectangular result set with scalar values.
  52.      */
  53.     const HYDRATE_SCALAR 3;
  54.     /**
  55.      * Hydrates a single scalar value.
  56.      */
  57.     const HYDRATE_SINGLE_SCALAR 4;
  58.     /**
  59.      * Very simple object hydrator (optimized for performance).
  60.      */
  61.     const HYDRATE_SIMPLEOBJECT 5;
  62.     /**
  63.      * The parameter map of this query.
  64.      *
  65.      * @var ArrayCollection|Parameter[]
  66.      */
  67.     protected $parameters;
  68.     /**
  69.      * The user-specified ResultSetMapping to use.
  70.      *
  71.      * @var \Doctrine\ORM\Query\ResultSetMapping
  72.      */
  73.     protected $_resultSetMapping;
  74.     /**
  75.      * The entity manager used by this query object.
  76.      *
  77.      * @var EntityManagerInterface
  78.      */
  79.     protected $_em;
  80.     /**
  81.      * The map of query hints.
  82.      *
  83.      * @var array
  84.      */
  85.     protected $_hints = [];
  86.     /**
  87.      * The hydration mode.
  88.      *
  89.      * @var string|int
  90.      */
  91.     protected $_hydrationMode self::HYDRATE_OBJECT;
  92.     /**
  93.      * @var \Doctrine\DBAL\Cache\QueryCacheProfile
  94.      */
  95.     protected $_queryCacheProfile;
  96.     /**
  97.      * Whether or not expire the result cache.
  98.      *
  99.      * @var boolean
  100.      */
  101.     protected $_expireResultCache false;
  102.     /**
  103.      * @var \Doctrine\DBAL\Cache\QueryCacheProfile
  104.      */
  105.     protected $_hydrationCacheProfile;
  106.     /**
  107.      * Whether to use second level cache, if available.
  108.      *
  109.      * @var boolean
  110.      */
  111.     protected $cacheable false;
  112.     /**
  113.      * @var boolean
  114.      */
  115.     protected $hasCache false;
  116.     /**
  117.      * Second level cache region name.
  118.      *
  119.      * @var string|null
  120.      */
  121.     protected $cacheRegion;
  122.     /**
  123.      * Second level query cache mode.
  124.      *
  125.      * @var integer|null
  126.      */
  127.     protected $cacheMode;
  128.     /**
  129.      * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null
  130.      */
  131.     protected $cacheLogger;
  132.     /**
  133.      * @var integer
  134.      */
  135.     protected $lifetime 0;
  136.     /**
  137.      * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
  138.      *
  139.      * @param \Doctrine\ORM\EntityManagerInterface $em
  140.      */
  141.     public function __construct(EntityManagerInterface $em)
  142.     {
  143.         $this->_em          $em;
  144.         $this->parameters   = new ArrayCollection();
  145.         $this->_hints       $em->getConfiguration()->getDefaultQueryHints();
  146.         $this->hasCache     $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
  147.         if ($this->hasCache) {
  148.             $this->cacheLogger $em->getConfiguration()
  149.                 ->getSecondLevelCacheConfiguration()
  150.                 ->getCacheLogger();
  151.         }
  152.     }
  153.     /**
  154.      * Enable/disable second level query (result) caching for this query.
  155.      *
  156.      * @param boolean $cacheable
  157.      *
  158.      * @return static This query instance.
  159.      */
  160.     public function setCacheable($cacheable)
  161.     {
  162.         $this->cacheable = (boolean) $cacheable;
  163.         return $this;
  164.     }
  165.     /**
  166.      * @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise.
  167.      */
  168.     public function isCacheable()
  169.     {
  170.         return $this->cacheable;
  171.     }
  172.     /**
  173.      * @param string $cacheRegion
  174.      *
  175.      * @return static This query instance.
  176.      */
  177.     public function setCacheRegion($cacheRegion)
  178.     {
  179.         $this->cacheRegion = (string) $cacheRegion;
  180.         return $this;
  181.     }
  182.     /**
  183.     * Obtain the name of the second level query cache region in which query results will be stored
  184.     *
  185.     * @return string|null The cache region name; NULL indicates the default region.
  186.     */
  187.     public function getCacheRegion()
  188.     {
  189.         return $this->cacheRegion;
  190.     }
  191.     /**
  192.      * @return boolean TRUE if the query cache and second level cache are enabled, FALSE otherwise.
  193.      */
  194.     protected function isCacheEnabled()
  195.     {
  196.         return $this->cacheable && $this->hasCache;
  197.     }
  198.     /**
  199.      * @return integer
  200.      */
  201.     public function getLifetime()
  202.     {
  203.         return $this->lifetime;
  204.     }
  205.     /**
  206.      * Sets the life-time for this query into second level cache.
  207.      *
  208.      * @param integer $lifetime
  209.      *
  210.      * @return \Doctrine\ORM\AbstractQuery This query instance.
  211.      */
  212.     public function setLifetime($lifetime)
  213.     {
  214.         $this->lifetime = (integer) $lifetime;
  215.         return $this;
  216.     }
  217.     /**
  218.      * @return integer
  219.      */
  220.     public function getCacheMode()
  221.     {
  222.         return $this->cacheMode;
  223.     }
  224.     /**
  225.      * @param integer $cacheMode
  226.      *
  227.      * @return \Doctrine\ORM\AbstractQuery This query instance.
  228.      */
  229.     public function setCacheMode($cacheMode)
  230.     {
  231.         $this->cacheMode = (integer) $cacheMode;
  232.         return $this;
  233.     }
  234.     /**
  235.      * Gets the SQL query that corresponds to this query object.
  236.      * The returned SQL syntax depends on the connection driver that is used
  237.      * by this query object at the time of this method call.
  238.      *
  239.      * @return string SQL query
  240.      */
  241.     abstract public function getSQL();
  242.     /**
  243.      * Retrieves the associated EntityManager of this Query instance.
  244.      *
  245.      * @return \Doctrine\ORM\EntityManager
  246.      */
  247.     public function getEntityManager()
  248.     {
  249.         return $this->_em;
  250.     }
  251.     /**
  252.      * Frees the resources used by the query object.
  253.      *
  254.      * Resets Parameters, Parameter Types and Query Hints.
  255.      *
  256.      * @return void
  257.      */
  258.     public function free()
  259.     {
  260.         $this->parameters = new ArrayCollection();
  261.         $this->_hints $this->_em->getConfiguration()->getDefaultQueryHints();
  262.     }
  263.     /**
  264.      * Get all defined parameters.
  265.      *
  266.      * @return ArrayCollection The defined query parameters.
  267.      */
  268.     public function getParameters()
  269.     {
  270.         return $this->parameters;
  271.     }
  272.     /**
  273.      * Gets a query parameter.
  274.      *
  275.      * @param mixed $key The key (index or name) of the bound parameter.
  276.      *
  277.      * @return Query\Parameter|null The value of the bound parameter, or NULL if not available.
  278.      */
  279.     public function getParameter($key)
  280.     {
  281.         $filteredParameters $this->parameters->filter(
  282.             function (Query\Parameter $parameter) use ($key) : bool {
  283.                 $parameterName $parameter->getName();
  284.                 return $key === $parameterName || (string) $key === (string) $parameterName;
  285.             }
  286.         );
  287.         return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null;
  288.     }
  289.     /**
  290.      * Sets a collection of query parameters.
  291.      *
  292.      * @param ArrayCollection|mixed[] $parameters
  293.      *
  294.      * @return static This query instance.
  295.      */
  296.     public function setParameters($parameters)
  297.     {
  298.         // BC compatibility with 2.3-
  299.         if (is_array($parameters)) {
  300.             $parameterCollection = new ArrayCollection();
  301.             foreach ($parameters as $key => $value) {
  302.                 $parameterCollection->add(new Parameter($key$value));
  303.             }
  304.             $parameters $parameterCollection;
  305.         }
  306.         $this->parameters $parameters;
  307.         return $this;
  308.     }
  309.     /**
  310.      * Sets a query parameter.
  311.      *
  312.      * @param string|int  $key   The parameter position or name.
  313.      * @param mixed       $value The parameter value.
  314.      * @param string|null $type  The parameter type. If specified, the given value will be run through
  315.      *                           the type conversion of this type. This is usually not needed for
  316.      *                           strings and numeric types.
  317.      *
  318.      * @return static This query instance.
  319.      */
  320.     public function setParameter($key$value$type null)
  321.     {
  322.         $existingParameter $this->getParameter($key);
  323.         if ($existingParameter !== null) {
  324.             $existingParameter->setValue($value$type);
  325.             return $this;
  326.         }
  327.         $this->parameters->add(new Parameter($key$value$type));
  328.         return $this;
  329.     }
  330.     /**
  331.      * Processes an individual parameter value.
  332.      *
  333.      * @param mixed $value
  334.      *
  335.      * @return array|string
  336.      *
  337.      * @throws \Doctrine\ORM\ORMInvalidArgumentException
  338.      */
  339.     public function processParameterValue($value)
  340.     {
  341.         if (is_scalar($value)) {
  342.             return $value;
  343.         }
  344.         if ($value instanceof Collection) {
  345.             $value $value->toArray();
  346.         }
  347.         if (is_array($value)) {
  348.             foreach ($value as $key => $paramValue) {
  349.                 $paramValue  $this->processParameterValue($paramValue);
  350.                 $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
  351.             }
  352.             return $value;
  353.         }
  354.         if ($value instanceof Mapping\ClassMetadata) {
  355.             return $value->name;
  356.         }
  357.         if (! is_object($value)) {
  358.             return $value;
  359.         }
  360.         try {
  361.             $value $this->_em->getUnitOfWork()->getSingleIdentifierValue($value);
  362.             if ($value === null) {
  363.                 throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
  364.             }
  365.         } catch (MappingException ORMMappingException $e) {
  366.             // Silence any mapping exceptions. These can occur if the object in
  367.             // question is not a mapped entity, in which case we just don't do
  368.             // any preparation on the value.
  369.         }
  370.         return $value;
  371.     }
  372.     /**
  373.      * Sets the ResultSetMapping that should be used for hydration.
  374.      *
  375.      * @param \Doctrine\ORM\Query\ResultSetMapping $rsm
  376.      *
  377.      * @return static This query instance.
  378.      */
  379.     public function setResultSetMapping(Query\ResultSetMapping $rsm)
  380.     {
  381.         $this->translateNamespaces($rsm);
  382.         $this->_resultSetMapping $rsm;
  383.         return $this;
  384.     }
  385.     /**
  386.      * Gets the ResultSetMapping used for hydration.
  387.      *
  388.      * @return \Doctrine\ORM\Query\ResultSetMapping
  389.      */
  390.     protected function getResultSetMapping()
  391.     {
  392.         return $this->_resultSetMapping;
  393.     }
  394.     /**
  395.      * Allows to translate entity namespaces to full qualified names.
  396.      *
  397.      * @param Query\ResultSetMapping $rsm
  398.      *
  399.      * @return void
  400.      */
  401.     private function translateNamespaces(Query\ResultSetMapping $rsm)
  402.     {
  403.         $translate = function ($alias) {
  404.             return $this->_em->getClassMetadata($alias)->getName();
  405.         };
  406.         $rsm->aliasMap array_map($translate$rsm->aliasMap);
  407.         $rsm->declaringClasses array_map($translate$rsm->declaringClasses);
  408.     }
  409.     /**
  410.      * Set a cache profile for hydration caching.
  411.      *
  412.      * If no result cache driver is set in the QueryCacheProfile, the default
  413.      * result cache driver is used from the configuration.
  414.      *
  415.      * Important: Hydration caching does NOT register entities in the
  416.      * UnitOfWork when retrieved from the cache. Never use result cached
  417.      * entities for requests that also flush the EntityManager. If you want
  418.      * some form of caching with UnitOfWork registration you should use
  419.      * {@see AbstractQuery::setResultCacheProfile()}.
  420.      *
  421.      * @example
  422.      * $lifetime = 100;
  423.      * $resultKey = "abc";
  424.      * $query->setHydrationCacheProfile(new QueryCacheProfile());
  425.      * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
  426.      *
  427.      * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
  428.      *
  429.      * @return static This query instance.
  430.      */
  431.     public function setHydrationCacheProfile(QueryCacheProfile $profile null)
  432.     {
  433.         if ($profile !== null && ! $profile->getResultCacheDriver()) {
  434.             $resultCacheDriver $this->_em->getConfiguration()->getHydrationCacheImpl();
  435.             $profile $profile->setResultCacheDriver($resultCacheDriver);
  436.         }
  437.         $this->_hydrationCacheProfile $profile;
  438.         return $this;
  439.     }
  440.     /**
  441.      * @return \Doctrine\DBAL\Cache\QueryCacheProfile
  442.      */
  443.     public function getHydrationCacheProfile()
  444.     {
  445.         return $this->_hydrationCacheProfile;
  446.     }
  447.     /**
  448.      * Set a cache profile for the result cache.
  449.      *
  450.      * If no result cache driver is set in the QueryCacheProfile, the default
  451.      * result cache driver is used from the configuration.
  452.      *
  453.      * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
  454.      *
  455.      * @return static This query instance.
  456.      */
  457.     public function setResultCacheProfile(QueryCacheProfile $profile null)
  458.     {
  459.         if ($profile !== null && ! $profile->getResultCacheDriver()) {
  460.             $resultCacheDriver $this->_em->getConfiguration()->getResultCacheImpl();
  461.             $profile $profile->setResultCacheDriver($resultCacheDriver);
  462.         }
  463.         $this->_queryCacheProfile $profile;
  464.         return $this;
  465.     }
  466.     /**
  467.      * Defines a cache driver to be used for caching result sets and implicitly enables caching.
  468.      *
  469.      * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver
  470.      *
  471.      * @return static This query instance.
  472.      *
  473.      * @throws ORMException
  474.      */
  475.     public function setResultCacheDriver($resultCacheDriver null)
  476.     {
  477.         if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
  478.             throw ORMException::invalidResultCacheDriver();
  479.         }
  480.         $this->_queryCacheProfile $this->_queryCacheProfile
  481.             $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
  482.             : new QueryCacheProfile(0null$resultCacheDriver);
  483.         return $this;
  484.     }
  485.     /**
  486.      * Returns the cache driver used for caching result sets.
  487.      *
  488.      * @deprecated
  489.      *
  490.      * @return \Doctrine\Common\Cache\Cache Cache driver
  491.      */
  492.     public function getResultCacheDriver()
  493.     {
  494.         if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
  495.             return $this->_queryCacheProfile->getResultCacheDriver();
  496.         }
  497.         return $this->_em->getConfiguration()->getResultCacheImpl();
  498.     }
  499.     /**
  500.      * Set whether or not to cache the results of this query and if so, for
  501.      * how long and which ID to use for the cache entry.
  502.      *
  503.      * @deprecated 2.7 Use {@see enableResultCache} and {@see disableResultCache} instead.
  504.      *
  505.      * @param bool   $useCache
  506.      * @param int    $lifetime
  507.      * @param string $resultCacheId
  508.      *
  509.      * @return static This query instance.
  510.      */
  511.     public function useResultCache($useCache$lifetime null$resultCacheId null)
  512.     {
  513.         return $useCache
  514.             $this->enableResultCache($lifetime$resultCacheId)
  515.             : $this->disableResultCache();
  516.     }
  517.     /**
  518.      * Enables caching of the results of this query, for given or default amount of seconds
  519.      * and optionally specifies which ID to use for the cache entry.
  520.      *
  521.      * @param int|null    $lifetime      How long the cache entry is valid, in seconds.
  522.      * @param string|null $resultCacheId ID to use for the cache entry.
  523.      *
  524.      * @return static This query instance.
  525.      */
  526.     public function enableResultCache(?int $lifetime null, ?string $resultCacheId null) : self
  527.     {
  528.         $this->setResultCacheLifetime($lifetime);
  529.         $this->setResultCacheId($resultCacheId);
  530.         return $this;
  531.     }
  532.     /**
  533.      * Disables caching of the results of this query.
  534.      *
  535.      * @return static This query instance.
  536.      */
  537.     public function disableResultCache() : self
  538.     {
  539.         $this->_queryCacheProfile null;
  540.         return $this;
  541.     }
  542.     /**
  543.      * Defines how long the result cache will be active before expire.
  544.      *
  545.      * @param integer $lifetime How long the cache entry is valid.
  546.      *
  547.      * @return static This query instance.
  548.      */
  549.     public function setResultCacheLifetime($lifetime)
  550.     {
  551.         $lifetime = ($lifetime !== null) ? (int) $lifetime 0;
  552.         $this->_queryCacheProfile $this->_queryCacheProfile
  553.             $this->_queryCacheProfile->setLifetime($lifetime)
  554.             : new QueryCacheProfile($lifetimenull$this->_em->getConfiguration()->getResultCacheImpl());
  555.         return $this;
  556.     }
  557.     /**
  558.      * Retrieves the lifetime of resultset cache.
  559.      *
  560.      * @deprecated
  561.      *
  562.      * @return integer
  563.      */
  564.     public function getResultCacheLifetime()
  565.     {
  566.         return $this->_queryCacheProfile $this->_queryCacheProfile->getLifetime() : 0;
  567.     }
  568.     /**
  569.      * Defines if the result cache is active or not.
  570.      *
  571.      * @param boolean $expire Whether or not to force resultset cache expiration.
  572.      *
  573.      * @return static This query instance.
  574.      */
  575.     public function expireResultCache($expire true)
  576.     {
  577.         $this->_expireResultCache $expire;
  578.         return $this;
  579.     }
  580.     /**
  581.      * Retrieves if the resultset cache is active or not.
  582.      *
  583.      * @return boolean
  584.      */
  585.     public function getExpireResultCache()
  586.     {
  587.         return $this->_expireResultCache;
  588.     }
  589.     /**
  590.      * @return QueryCacheProfile
  591.      */
  592.     public function getQueryCacheProfile()
  593.     {
  594.         return $this->_queryCacheProfile;
  595.     }
  596.     /**
  597.      * Change the default fetch mode of an association for this query.
  598.      *
  599.      * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
  600.      *
  601.      * @param string $class
  602.      * @param string $assocName
  603.      * @param int    $fetchMode
  604.      *
  605.      * @return static This query instance.
  606.      */
  607.     public function setFetchMode($class$assocName$fetchMode)
  608.     {
  609.         if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
  610.             $fetchMode Mapping\ClassMetadata::FETCH_LAZY;
  611.         }
  612.         $this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
  613.         return $this;
  614.     }
  615.     /**
  616.      * Defines the processing mode to be used during hydration / result set transformation.
  617.      *
  618.      * @param string|int $hydrationMode Doctrine processing mode to be used during hydration process.
  619.      *                                  One of the Query::HYDRATE_* constants.
  620.      *
  621.      * @return static This query instance.
  622.      */
  623.     public function setHydrationMode($hydrationMode)
  624.     {
  625.         $this->_hydrationMode $hydrationMode;
  626.         return $this;
  627.     }
  628.     /**
  629.      * Gets the hydration mode currently used by the query.
  630.      *
  631.      * @return string|int
  632.      */
  633.     public function getHydrationMode()
  634.     {
  635.         return $this->_hydrationMode;
  636.     }
  637.     /**
  638.      * Gets the list of results for the query.
  639.      *
  640.      * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
  641.      *
  642.      * @param string|int $hydrationMode
  643.      *
  644.      * @return mixed
  645.      */
  646.     public function getResult($hydrationMode self::HYDRATE_OBJECT)
  647.     {
  648.         return $this->execute(null$hydrationMode);
  649.     }
  650.     /**
  651.      * Gets the array of results for the query.
  652.      *
  653.      * Alias for execute(null, HYDRATE_ARRAY).
  654.      *
  655.      * @return array
  656.      */
  657.     public function getArrayResult()
  658.     {
  659.         return $this->execute(nullself::HYDRATE_ARRAY);
  660.     }
  661.     /**
  662.      * Gets the scalar results for the query.
  663.      *
  664.      * Alias for execute(null, HYDRATE_SCALAR).
  665.      *
  666.      * @return array
  667.      */
  668.     public function getScalarResult()
  669.     {
  670.         return $this->execute(nullself::HYDRATE_SCALAR);
  671.     }
  672.     /**
  673.      * Get exactly one result or null.
  674.      *
  675.      * @param string|int $hydrationMode
  676.      *
  677.      * @return mixed
  678.      *
  679.      * @throws NonUniqueResultException
  680.      */
  681.     public function getOneOrNullResult($hydrationMode null)
  682.     {
  683.         try {
  684.             $result $this->execute(null$hydrationMode);
  685.         } catch (NoResultException $e) {
  686.             return null;
  687.         }
  688.         if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
  689.             return null;
  690.         }
  691.         if ( ! is_array($result)) {
  692.             return $result;
  693.         }
  694.         if (count($result) > 1) {
  695.             throw new NonUniqueResultException;
  696.         }
  697.         return array_shift($result);
  698.     }
  699.     /**
  700.      * Gets the single result of the query.
  701.      *
  702.      * Enforces the presence as well as the uniqueness of the result.
  703.      *
  704.      * If the result is not unique, a NonUniqueResultException is thrown.
  705.      * If there is no result, a NoResultException is thrown.
  706.      *
  707.      * @param string|int $hydrationMode
  708.      *
  709.      * @return mixed
  710.      *
  711.      * @throws NonUniqueResultException If the query result is not unique.
  712.      * @throws NoResultException        If the query returned no result and hydration mode is not HYDRATE_SINGLE_SCALAR.
  713.      */
  714.     public function getSingleResult($hydrationMode null)
  715.     {
  716.         $result $this->execute(null$hydrationMode);
  717.         if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
  718.             throw new NoResultException;
  719.         }
  720.         if ( ! is_array($result)) {
  721.             return $result;
  722.         }
  723.         if (count($result) > 1) {
  724.             throw new NonUniqueResultException;
  725.         }
  726.         return array_shift($result);
  727.     }
  728.     /**
  729.      * Gets the single scalar result of the query.
  730.      *
  731.      * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
  732.      *
  733.      * @return mixed The scalar result.
  734.      *
  735.      * @throws NoResultException        If the query returned no result.
  736.      * @throws NonUniqueResultException If the query result is not unique.
  737.      */
  738.     public function getSingleScalarResult()
  739.     {
  740.         return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
  741.     }
  742.     /**
  743.      * Sets a query hint. If the hint name is not recognized, it is silently ignored.
  744.      *
  745.      * @param string $name  The name of the hint.
  746.      * @param mixed  $value The value of the hint.
  747.      *
  748.      * @return static This query instance.
  749.      */
  750.     public function setHint($name$value)
  751.     {
  752.         $this->_hints[$name] = $value;
  753.         return $this;
  754.     }
  755.     /**
  756.      * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
  757.      *
  758.      * @param string $name The name of the hint.
  759.      *
  760.      * @return mixed The value of the hint or FALSE, if the hint name is not recognized.
  761.      */
  762.     public function getHint($name)
  763.     {
  764.         return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
  765.     }
  766.     /**
  767.      * Check if the query has a hint
  768.      *
  769.      * @param string $name The name of the hint
  770.      *
  771.      * @return bool False if the query does not have any hint
  772.      */
  773.     public function hasHint($name)
  774.     {
  775.         return isset($this->_hints[$name]);
  776.     }
  777.     /**
  778.      * Return the key value map of query hints that are currently set.
  779.      *
  780.      * @return array
  781.      */
  782.     public function getHints()
  783.     {
  784.         return $this->_hints;
  785.     }
  786.     /**
  787.      * Executes the query and returns an IterableResult that can be used to incrementally
  788.      * iterate over the result.
  789.      *
  790.      * @param ArrayCollection|array|null $parameters    The query parameters.
  791.      * @param string|int|null            $hydrationMode The hydration mode to use.
  792.      *
  793.      * @return \Doctrine\ORM\Internal\Hydration\IterableResult
  794.      */
  795.     public function iterate($parameters null$hydrationMode null)
  796.     {
  797.         if ($hydrationMode !== null) {
  798.             $this->setHydrationMode($hydrationMode);
  799.         }
  800.         if ( ! empty($parameters)) {
  801.             $this->setParameters($parameters);
  802.         }
  803.         $rsm  $this->getResultSetMapping();
  804.         $stmt $this->_doExecute();
  805.         return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt$rsm$this->_hints);
  806.     }
  807.     /**
  808.      * Executes the query.
  809.      *
  810.      * @param ArrayCollection|array|null $parameters Query parameters.
  811.      * @param string|int|null            $hydrationMode Processing mode to be used during the hydration process.
  812.      *
  813.      * @return mixed
  814.      */
  815.     public function execute($parameters null$hydrationMode null)
  816.     {
  817.         if ($this->cacheable && $this->isCacheEnabled()) {
  818.             return $this->executeUsingQueryCache($parameters$hydrationMode);
  819.         }
  820.         return $this->executeIgnoreQueryCache($parameters$hydrationMode);
  821.     }
  822.     /**
  823.      * Execute query ignoring second level cache.
  824.      *
  825.      * @param ArrayCollection|array|null $parameters
  826.      * @param string|int|null            $hydrationMode
  827.      *
  828.      * @return mixed
  829.      */
  830.     private function executeIgnoreQueryCache($parameters null$hydrationMode null)
  831.     {
  832.         if ($hydrationMode !== null) {
  833.             $this->setHydrationMode($hydrationMode);
  834.         }
  835.         if ( ! empty($parameters)) {
  836.             $this->setParameters($parameters);
  837.         }
  838.         $setCacheEntry = function() {};
  839.         if ($this->_hydrationCacheProfile !== null) {
  840.             list($cacheKey$realCacheKey) = $this->getHydrationCacheId();
  841.             $queryCacheProfile $this->getHydrationCacheProfile();
  842.             $cache             $queryCacheProfile->getResultCacheDriver();
  843.             $result            $cache->fetch($cacheKey);
  844.             if (isset($result[$realCacheKey])) {
  845.                 return $result[$realCacheKey];
  846.             }
  847.             if ( ! $result) {
  848.                 $result = [];
  849.             }
  850.             $setCacheEntry = function($data) use ($cache$result$cacheKey$realCacheKey$queryCacheProfile) {
  851.                 $result[$realCacheKey] = $data;
  852.                 $cache->save($cacheKey$result$queryCacheProfile->getLifetime());
  853.             };
  854.         }
  855.         $stmt $this->_doExecute();
  856.         if (is_numeric($stmt)) {
  857.             $setCacheEntry($stmt);
  858.             return $stmt;
  859.         }
  860.         $rsm  $this->getResultSetMapping();
  861.         $data $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt$rsm$this->_hints);
  862.         $setCacheEntry($data);
  863.         return $data;
  864.     }
  865.     /**
  866.      * Load from second level cache or executes the query and put into cache.
  867.      *
  868.      * @param ArrayCollection|array|null $parameters
  869.      * @param string|int|null            $hydrationMode
  870.      *
  871.      * @return mixed
  872.      */
  873.     private function executeUsingQueryCache($parameters null$hydrationMode null)
  874.     {
  875.         $rsm        $this->getResultSetMapping();
  876.         $queryCache $this->_em->getCache()->getQueryCache($this->cacheRegion);
  877.         $queryKey   = new QueryCacheKey(
  878.             $this->getHash(),
  879.             $this->lifetime,
  880.             $this->cacheMode ?: Cache::MODE_NORMAL,
  881.             $this->getTimestampKey()
  882.         );
  883.         $result     $queryCache->get($queryKey$rsm$this->_hints);
  884.         if ($result !== null) {
  885.             if ($this->cacheLogger) {
  886.                 $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
  887.             }
  888.             return $result;
  889.         }
  890.         $result $this->executeIgnoreQueryCache($parameters$hydrationMode);
  891.         $cached $queryCache->put($queryKey$rsm$result$this->_hints);
  892.         if ($this->cacheLogger) {
  893.             $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);
  894.             if ($cached) {
  895.                 $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
  896.             }
  897.         }
  898.         return $result;
  899.     }
  900.     /**
  901.      * @return \Doctrine\ORM\Cache\TimestampCacheKey|null
  902.      */
  903.     private function getTimestampKey()
  904.     {
  905.         $entityName reset($this->_resultSetMapping->aliasMap);
  906.         if (empty($entityName)) {
  907.             return null;
  908.         }
  909.         $metadata $this->_em->getClassMetadata($entityName);
  910.         return new Cache\TimestampCacheKey($metadata->rootEntityName);
  911.     }
  912.     /**
  913.      * Get the result cache id to use to store the result set cache entry.
  914.      * Will return the configured id if it exists otherwise a hash will be
  915.      * automatically generated for you.
  916.      *
  917.      * @return array ($key, $hash)
  918.      */
  919.     protected function getHydrationCacheId()
  920.     {
  921.         $parameters = [];
  922.         foreach ($this->getParameters() as $parameter) {
  923.             $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
  924.         }
  925.         $sql                    $this->getSQL();
  926.         $queryCacheProfile      $this->getHydrationCacheProfile();
  927.         $hints                  $this->getHints();
  928.         $hints['hydrationMode'] = $this->getHydrationMode();
  929.         ksort($hints);
  930.         return $queryCacheProfile->generateCacheKeys($sql$parameters$hints);
  931.     }
  932.     /**
  933.      * Set the result cache id to use to store the result set cache entry.
  934.      * If this is not explicitly set by the developer then a hash is automatically
  935.      * generated for you.
  936.      *
  937.      * @param string $id
  938.      *
  939.      * @return static This query instance.
  940.      */
  941.     public function setResultCacheId($id)
  942.     {
  943.         $this->_queryCacheProfile $this->_queryCacheProfile
  944.             $this->_queryCacheProfile->setCacheKey($id)
  945.             : new QueryCacheProfile(0$id$this->_em->getConfiguration()->getResultCacheImpl());
  946.         return $this;
  947.     }
  948.     /**
  949.      * Get the result cache id to use to store the result set cache entry if set.
  950.      *
  951.      * @deprecated
  952.      *
  953.      * @return string
  954.      */
  955.     public function getResultCacheId()
  956.     {
  957.         return $this->_queryCacheProfile $this->_queryCacheProfile->getCacheKey() : null;
  958.     }
  959.     /**
  960.      * Executes the query and returns a the resulting Statement object.
  961.      *
  962.      * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
  963.      */
  964.     abstract protected function _doExecute();
  965.     /**
  966.      * Cleanup Query resource when clone is called.
  967.      *
  968.      * @return void
  969.      */
  970.     public function __clone()
  971.     {
  972.         $this->parameters = new ArrayCollection();
  973.         $this->_hints = [];
  974.         $this->_hints $this->_em->getConfiguration()->getDefaultQueryHints();
  975.     }
  976.     /**
  977.      * Generates a string of currently query to use for the cache second level cache.
  978.      *
  979.      * @return string
  980.      */
  981.     protected function getHash()
  982.     {
  983.         $query  $this->getSQL();
  984.         $hints  $this->getHints();
  985.         $params array_map(function(Parameter $parameter) {
  986.             // Small optimization
  987.             // Does not invoke processParameterValue for scalar values
  988.             if (is_scalar($value $parameter->getValue())) {
  989.                 return $value;
  990.             }
  991.             return $this->processParameterValue($value);
  992.         }, $this->parameters->getValues());
  993.         ksort($hints);
  994.         return sha1($query '-' serialize($params) . '-' serialize($hints));
  995.     }
  996. }