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

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