vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php line 53

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\Collections\ArrayCollection;
  21. use Doctrine\Common\Collections\Criteria;
  22. use Doctrine\ORM\Query\Expr;
  23. use Doctrine\ORM\Query\QueryExpressionVisitor;
  24. use InvalidArgumentException;
  25. use RuntimeException;
  26. use function array_keys;
  27. use function array_merge;
  28. use function array_unshift;
  29. use function assert;
  30. use function func_get_args;
  31. use function func_num_args;
  32. use function implode;
  33. use function in_array;
  34. use function is_array;
  35. use function is_numeric;
  36. use function is_object;
  37. use function is_string;
  38. use function key;
  39. use function reset;
  40. use function sprintf;
  41. use function strpos;
  42. use function strrpos;
  43. use function substr;
  44. /**
  45.  * This class is responsible for building DQL query strings via an object oriented
  46.  * PHP interface.
  47.  */
  48. class QueryBuilder
  49. {
  50.     /* The query types. */
  51.     public const SELECT 0;
  52.     public const DELETE 1;
  53.     public const UPDATE 2;
  54.     /* The builder states. */
  55.     public const STATE_DIRTY 0;
  56.     public const STATE_CLEAN 1;
  57.     /**
  58.      * The EntityManager used by this QueryBuilder.
  59.      *
  60.      * @var EntityManagerInterface
  61.      */
  62.     private $_em;
  63.     /**
  64.      * The array of DQL parts collected.
  65.      *
  66.      * @psalm-var array<string, mixed>
  67.      */
  68.     private $_dqlParts = [
  69.         'distinct' => false,
  70.         'select'  => [],
  71.         'from'    => [],
  72.         'join'    => [],
  73.         'set'     => [],
  74.         'where'   => null,
  75.         'groupBy' => [],
  76.         'having'  => null,
  77.         'orderBy' => [],
  78.     ];
  79.     /**
  80.      * The type of query this is. Can be select, update or delete.
  81.      *
  82.      * @var int
  83.      */
  84.     private $_type self::SELECT;
  85.     /**
  86.      * The state of the query object. Can be dirty or clean.
  87.      *
  88.      * @var int
  89.      */
  90.     private $_state self::STATE_CLEAN;
  91.     /**
  92.      * The complete DQL string for this query.
  93.      *
  94.      * @var string
  95.      */
  96.     private $_dql;
  97.     /**
  98.      * The query parameters.
  99.      *
  100.      * @var ArrayCollection
  101.      */
  102.     private $parameters;
  103.     /**
  104.      * The index of the first result to retrieve.
  105.      *
  106.      * @var int|null
  107.      */
  108.     private $_firstResult null;
  109.     /**
  110.      * The maximum number of results to retrieve.
  111.      *
  112.      * @var int|null
  113.      */
  114.     private $_maxResults null;
  115.     /**
  116.      * Keeps root entity alias names for join entities.
  117.      *
  118.      * @psalm-var array<string, string>
  119.      */
  120.     private $joinRootAliases = [];
  121.      /**
  122.       * Whether to use second level cache, if available.
  123.       *
  124.       * @var bool
  125.       */
  126.     protected $cacheable false;
  127.     /**
  128.      * Second level cache region name.
  129.      *
  130.      * @var string|null
  131.      */
  132.     protected $cacheRegion;
  133.     /**
  134.      * Second level query cache mode.
  135.      *
  136.      * @var int|null
  137.      */
  138.     protected $cacheMode;
  139.     /** @var int */
  140.     protected $lifetime 0;
  141.     /**
  142.      * Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>.
  143.      *
  144.      * @param EntityManagerInterface $em The EntityManager to use.
  145.      */
  146.     public function __construct(EntityManagerInterface $em)
  147.     {
  148.         $this->_em        $em;
  149.         $this->parameters = new ArrayCollection();
  150.     }
  151.     /**
  152.      * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
  153.      * This producer method is intended for convenient inline usage. Example:
  154.      *
  155.      * <code>
  156.      *     $qb = $em->createQueryBuilder();
  157.      *     $qb
  158.      *         ->select('u')
  159.      *         ->from('User', 'u')
  160.      *         ->where($qb->expr()->eq('u.id', 1));
  161.      * </code>
  162.      *
  163.      * For more complex expression construction, consider storing the expression
  164.      * builder object in a local variable.
  165.      *
  166.      * @return Query\Expr
  167.      */
  168.     public function expr()
  169.     {
  170.         return $this->_em->getExpressionBuilder();
  171.     }
  172.     /**
  173.      * Enable/disable second level query (result) caching for this query.
  174.      *
  175.      * @param bool $cacheable
  176.      *
  177.      * @return self
  178.      */
  179.     public function setCacheable($cacheable)
  180.     {
  181.         $this->cacheable = (bool) $cacheable;
  182.         return $this;
  183.     }
  184.     /**
  185.      * @return bool TRUE if the query results are enable for second level cache, FALSE otherwise.
  186.      */
  187.     public function isCacheable()
  188.     {
  189.         return $this->cacheable;
  190.     }
  191.     /**
  192.      * @param string $cacheRegion
  193.      *
  194.      * @return self
  195.      */
  196.     public function setCacheRegion($cacheRegion)
  197.     {
  198.         $this->cacheRegion = (string) $cacheRegion;
  199.         return $this;
  200.     }
  201.     /**
  202.      * Obtain the name of the second level query cache region in which query results will be stored
  203.      *
  204.      * @return string|null The cache region name; NULL indicates the default region.
  205.      */
  206.     public function getCacheRegion()
  207.     {
  208.         return $this->cacheRegion;
  209.     }
  210.     /**
  211.      * @return int
  212.      */
  213.     public function getLifetime()
  214.     {
  215.         return $this->lifetime;
  216.     }
  217.     /**
  218.      * Sets the life-time for this query into second level cache.
  219.      *
  220.      * @param int $lifetime
  221.      *
  222.      * @return self
  223.      */
  224.     public function setLifetime($lifetime)
  225.     {
  226.         $this->lifetime = (int) $lifetime;
  227.         return $this;
  228.     }
  229.     /**
  230.      * @return int
  231.      */
  232.     public function getCacheMode()
  233.     {
  234.         return $this->cacheMode;
  235.     }
  236.     /**
  237.      * @param int $cacheMode
  238.      *
  239.      * @return self
  240.      */
  241.     public function setCacheMode($cacheMode)
  242.     {
  243.         $this->cacheMode = (int) $cacheMode;
  244.         return $this;
  245.     }
  246.     /**
  247.      * Gets the type of the currently built query.
  248.      *
  249.      * @return int
  250.      */
  251.     public function getType()
  252.     {
  253.         return $this->_type;
  254.     }
  255.     /**
  256.      * Gets the associated EntityManager for this query builder.
  257.      *
  258.      * @return EntityManager
  259.      */
  260.     public function getEntityManager()
  261.     {
  262.         return $this->_em;
  263.     }
  264.     /**
  265.      * Gets the state of this query builder instance.
  266.      *
  267.      * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
  268.      */
  269.     public function getState()
  270.     {
  271.         return $this->_state;
  272.     }
  273.     /**
  274.      * Gets the complete DQL string formed by the current specifications of this QueryBuilder.
  275.      *
  276.      * <code>
  277.      *     $qb = $em->createQueryBuilder()
  278.      *         ->select('u')
  279.      *         ->from('User', 'u');
  280.      *     echo $qb->getDql(); // SELECT u FROM User u
  281.      * </code>
  282.      *
  283.      * @return string The DQL query string.
  284.      */
  285.     public function getDQL()
  286.     {
  287.         if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) {
  288.             return $this->_dql;
  289.         }
  290.         switch ($this->_type) {
  291.             case self::DELETE:
  292.                 $dql $this->getDQLForDelete();
  293.                 break;
  294.             case self::UPDATE:
  295.                 $dql $this->getDQLForUpdate();
  296.                 break;
  297.             case self::SELECT:
  298.             default:
  299.                 $dql $this->getDQLForSelect();
  300.                 break;
  301.         }
  302.         $this->_state self::STATE_CLEAN;
  303.         $this->_dql   $dql;
  304.         return $dql;
  305.     }
  306.     /**
  307.      * Constructs a Query instance from the current specifications of the builder.
  308.      *
  309.      * <code>
  310.      *     $qb = $em->createQueryBuilder()
  311.      *         ->select('u')
  312.      *         ->from('User', 'u');
  313.      *     $q = $qb->getQuery();
  314.      *     $results = $q->execute();
  315.      * </code>
  316.      *
  317.      * @return Query
  318.      */
  319.     public function getQuery()
  320.     {
  321.         $parameters = clone $this->parameters;
  322.         $query      $this->_em->createQuery($this->getDQL())
  323.             ->setParameters($parameters)
  324.             ->setFirstResult($this->_firstResult)
  325.             ->setMaxResults($this->_maxResults);
  326.         if ($this->lifetime) {
  327.             $query->setLifetime($this->lifetime);
  328.         }
  329.         if ($this->cacheMode) {
  330.             $query->setCacheMode($this->cacheMode);
  331.         }
  332.         if ($this->cacheable) {
  333.             $query->setCacheable($this->cacheable);
  334.         }
  335.         if ($this->cacheRegion) {
  336.             $query->setCacheRegion($this->cacheRegion);
  337.         }
  338.         return $query;
  339.     }
  340.     /**
  341.      * Finds the root entity alias of the joined entity.
  342.      *
  343.      * @param string $alias       The alias of the new join entity
  344.      * @param string $parentAlias The parent entity alias of the join relationship
  345.      *
  346.      * @return string
  347.      */
  348.     private function findRootAlias($alias$parentAlias)
  349.     {
  350.         $rootAlias null;
  351.         if (in_array($parentAlias$this->getRootAliases())) {
  352.             $rootAlias $parentAlias;
  353.         } elseif (isset($this->joinRootAliases[$parentAlias])) {
  354.             $rootAlias $this->joinRootAliases[$parentAlias];
  355.         } else {
  356.             // Should never happen with correct joining order. Might be
  357.             // thoughtful to throw exception instead.
  358.             $rootAlias $this->getRootAlias();
  359.         }
  360.         $this->joinRootAliases[$alias] = $rootAlias;
  361.         return $rootAlias;
  362.     }
  363.     /**
  364.      * Gets the FIRST root alias of the query. This is the first entity alias involved
  365.      * in the construction of the query.
  366.      *
  367.      * <code>
  368.      * $qb = $em->createQueryBuilder()
  369.      *     ->select('u')
  370.      *     ->from('User', 'u');
  371.      *
  372.      * echo $qb->getRootAlias(); // u
  373.      * </code>
  374.      *
  375.      * @deprecated Please use $qb->getRootAliases() instead.
  376.      *
  377.      * @return string
  378.      *
  379.      * @throws RuntimeException
  380.      */
  381.     public function getRootAlias()
  382.     {
  383.         $aliases $this->getRootAliases();
  384.         if (! isset($aliases[0])) {
  385.             throw new RuntimeException('No alias was set before invoking getRootAlias().');
  386.         }
  387.         return $aliases[0];
  388.     }
  389.     /**
  390.      * Gets the root aliases of the query. This is the entity aliases involved
  391.      * in the construction of the query.
  392.      *
  393.      * <code>
  394.      *     $qb = $em->createQueryBuilder()
  395.      *         ->select('u')
  396.      *         ->from('User', 'u');
  397.      *
  398.      *     $qb->getRootAliases(); // array('u')
  399.      * </code>
  400.      *
  401.      * @return mixed[]
  402.      *
  403.      * @psalm-return list<mixed>
  404.      */
  405.     public function getRootAliases()
  406.     {
  407.         $aliases = [];
  408.         foreach ($this->_dqlParts['from'] as &$fromClause) {
  409.             if (is_string($fromClause)) {
  410.                 $spacePos strrpos($fromClause' ');
  411.                 $from     substr($fromClause0$spacePos);
  412.                 $alias    substr($fromClause$spacePos 1);
  413.                 $fromClause = new Query\Expr\From($from$alias);
  414.             }
  415.             $aliases[] = $fromClause->getAlias();
  416.         }
  417.         return $aliases;
  418.     }
  419.     /**
  420.      * Gets all the aliases that have been used in the query.
  421.      * Including all select root aliases and join aliases
  422.      *
  423.      * <code>
  424.      *     $qb = $em->createQueryBuilder()
  425.      *         ->select('u')
  426.      *         ->from('User', 'u')
  427.      *         ->join('u.articles','a');
  428.      *
  429.      *     $qb->getAllAliases(); // array('u','a')
  430.      * </code>
  431.      *
  432.      * @return mixed[]
  433.      *
  434.      * @psalm-return list<mixed>
  435.      */
  436.     public function getAllAliases()
  437.     {
  438.         return array_merge($this->getRootAliases(), array_keys($this->joinRootAliases));
  439.     }
  440.     /**
  441.      * Gets the root entities of the query. This is the entity aliases involved
  442.      * in the construction of the query.
  443.      *
  444.      * <code>
  445.      *     $qb = $em->createQueryBuilder()
  446.      *         ->select('u')
  447.      *         ->from('User', 'u');
  448.      *
  449.      *     $qb->getRootEntities(); // array('User')
  450.      * </code>
  451.      *
  452.      * @return mixed[]
  453.      *
  454.      * @psalm-return list<mixed>
  455.      */
  456.     public function getRootEntities()
  457.     {
  458.         $entities = [];
  459.         foreach ($this->_dqlParts['from'] as &$fromClause) {
  460.             if (is_string($fromClause)) {
  461.                 $spacePos strrpos($fromClause' ');
  462.                 $from     substr($fromClause0$spacePos);
  463.                 $alias    substr($fromClause$spacePos 1);
  464.                 $fromClause = new Query\Expr\From($from$alias);
  465.             }
  466.             $entities[] = $fromClause->getFrom();
  467.         }
  468.         return $entities;
  469.     }
  470.     /**
  471.      * Sets a query parameter for the query being constructed.
  472.      *
  473.      * <code>
  474.      *     $qb = $em->createQueryBuilder()
  475.      *         ->select('u')
  476.      *         ->from('User', 'u')
  477.      *         ->where('u.id = :user_id')
  478.      *         ->setParameter('user_id', 1);
  479.      * </code>
  480.      *
  481.      * @param string|int      $key   The parameter position or name.
  482.      * @param mixed           $value The parameter value.
  483.      * @param string|int|null $type  PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant
  484.      *
  485.      * @return self
  486.      */
  487.     public function setParameter($key$value$type null)
  488.     {
  489.         $existingParameter $this->getParameter($key);
  490.         if ($existingParameter !== null) {
  491.             $existingParameter->setValue($value$type);
  492.             return $this;
  493.         }
  494.         $this->parameters->add(new Query\Parameter($key$value$type));
  495.         return $this;
  496.     }
  497.     /**
  498.      * Sets a collection of query parameters for the query being constructed.
  499.      *
  500.      * <code>
  501.      *     $qb = $em->createQueryBuilder()
  502.      *         ->select('u')
  503.      *         ->from('User', 'u')
  504.      *         ->where('u.id = :user_id1 OR u.id = :user_id2')
  505.      *         ->setParameters(new ArrayCollection(array(
  506.      *             new Parameter('user_id1', 1),
  507.      *             new Parameter('user_id2', 2)
  508.      *        )));
  509.      * </code>
  510.      *
  511.      * @param ArrayCollection|mixed[] $parameters The query parameters to set.
  512.      *
  513.      * @return self
  514.      */
  515.     public function setParameters($parameters)
  516.     {
  517.         // BC compatibility with 2.3-
  518.         if (is_array($parameters)) {
  519.             /** @psalm-var ArrayCollection<int, Query\Parameter> $parameterCollection */
  520.             $parameterCollection = new ArrayCollection();
  521.             foreach ($parameters as $key => $value) {
  522.                 $parameter = new Query\Parameter($key$value);
  523.                 $parameterCollection->add($parameter);
  524.             }
  525.             $parameters $parameterCollection;
  526.         }
  527.         $this->parameters $parameters;
  528.         return $this;
  529.     }
  530.     /**
  531.      * Gets all defined query parameters for the query being constructed.
  532.      *
  533.      * @return ArrayCollection The currently defined query parameters.
  534.      */
  535.     public function getParameters()
  536.     {
  537.         return $this->parameters;
  538.     }
  539.     /**
  540.      * Gets a (previously set) query parameter of the query being constructed.
  541.      *
  542.      * @param mixed $key The key (index or name) of the bound parameter.
  543.      *
  544.      * @return Query\Parameter|null The value of the bound parameter.
  545.      */
  546.     public function getParameter($key)
  547.     {
  548.         $key Query\Parameter::normalizeName($key);
  549.         $filteredParameters $this->parameters->filter(
  550.             static function (Query\Parameter $parameter) use ($key): bool {
  551.                 $parameterName $parameter->getName();
  552.                 return $key === $parameterName;
  553.             }
  554.         );
  555.         return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null;
  556.     }
  557.     /**
  558.      * Sets the position of the first result to retrieve (the "offset").
  559.      *
  560.      * @param int|null $firstResult The first result to return.
  561.      *
  562.      * @return self
  563.      */
  564.     public function setFirstResult($firstResult)
  565.     {
  566.         $this->_firstResult $firstResult;
  567.         return $this;
  568.     }
  569.     /**
  570.      * Gets the position of the first result the query object was set to retrieve (the "offset").
  571.      * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
  572.      *
  573.      * @return int|null The position of the first result.
  574.      */
  575.     public function getFirstResult()
  576.     {
  577.         return $this->_firstResult;
  578.     }
  579.     /**
  580.      * Sets the maximum number of results to retrieve (the "limit").
  581.      *
  582.      * @param int|null $maxResults The maximum number of results to retrieve.
  583.      *
  584.      * @return self
  585.      */
  586.     public function setMaxResults($maxResults)
  587.     {
  588.         $this->_maxResults $maxResults;
  589.         return $this;
  590.     }
  591.     /**
  592.      * Gets the maximum number of results the query object was set to retrieve (the "limit").
  593.      * Returns NULL if {@link setMaxResults} was not applied to this query builder.
  594.      *
  595.      * @return int|null Maximum number of results.
  596.      */
  597.     public function getMaxResults()
  598.     {
  599.         return $this->_maxResults;
  600.     }
  601.     /**
  602.      * Either appends to or replaces a single, generic query part.
  603.      *
  604.      * The available parts are: 'select', 'from', 'join', 'set', 'where',
  605.      * 'groupBy', 'having' and 'orderBy'.
  606.      *
  607.      * @param string $dqlPartName The DQL part name.
  608.      * @param bool   $append      Whether to append (true) or replace (false).
  609.      *
  610.      * @return self
  611.      *
  612.      * @psalm-param string|object|list<string>|array{join: array<int|string, object>} $dqlPart     An Expr object.
  613.      */
  614.     public function add($dqlPartName$dqlPart$append false)
  615.     {
  616.         if ($append && ($dqlPartName === 'where' || $dqlPartName === 'having')) {
  617.             throw new InvalidArgumentException(
  618.                 "Using \$append = true does not have an effect with 'where' or 'having' " .
  619.                 'parts. See QueryBuilder#andWhere() for an example for correct usage.'
  620.             );
  621.         }
  622.         $isMultiple is_array($this->_dqlParts[$dqlPartName])
  623.             && ! ($dqlPartName === 'join' && ! $append);
  624.         // Allow adding any part retrieved from self::getDQLParts().
  625.         if (is_array($dqlPart) && $dqlPartName !== 'join') {
  626.             $dqlPart reset($dqlPart);
  627.         }
  628.         // This is introduced for backwards compatibility reasons.
  629.         // TODO: Remove for 3.0
  630.         if ($dqlPartName === 'join') {
  631.             $newDqlPart = [];
  632.             foreach ($dqlPart as $k => $v) {
  633.                 $k is_numeric($k) ? $this->getRootAlias() : $k;
  634.                 $newDqlPart[$k] = $v;
  635.             }
  636.             $dqlPart $newDqlPart;
  637.         }
  638.         if ($append && $isMultiple) {
  639.             if (is_array($dqlPart)) {
  640.                 $key key($dqlPart);
  641.                 $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
  642.             } else {
  643.                 $this->_dqlParts[$dqlPartName][] = $dqlPart;
  644.             }
  645.         } else {
  646.             $this->_dqlParts[$dqlPartName] = $isMultiple ? [$dqlPart] : $dqlPart;
  647.         }
  648.         $this->_state self::STATE_DIRTY;
  649.         return $this;
  650.     }
  651.     /**
  652.      * Specifies an item that is to be returned in the query result.
  653.      * Replaces any previously specified selections, if any.
  654.      *
  655.      * <code>
  656.      *     $qb = $em->createQueryBuilder()
  657.      *         ->select('u', 'p')
  658.      *         ->from('User', 'u')
  659.      *         ->leftJoin('u.Phonenumbers', 'p');
  660.      * </code>
  661.      *
  662.      * @param mixed $select The selection expressions.
  663.      *
  664.      * @return self
  665.      */
  666.     public function select($select null)
  667.     {
  668.         $this->_type self::SELECT;
  669.         if (empty($select)) {
  670.             return $this;
  671.         }
  672.         $selects is_array($select) ? $select func_get_args();
  673.         return $this->add('select', new Expr\Select($selects), false);
  674.     }
  675.     /**
  676.      * Adds a DISTINCT flag to this query.
  677.      *
  678.      * <code>
  679.      *     $qb = $em->createQueryBuilder()
  680.      *         ->select('u')
  681.      *         ->distinct()
  682.      *         ->from('User', 'u');
  683.      * </code>
  684.      *
  685.      * @param bool $flag
  686.      *
  687.      * @return self
  688.      */
  689.     public function distinct($flag true)
  690.     {
  691.         $this->_dqlParts['distinct'] = (bool) $flag;
  692.         return $this;
  693.     }
  694.     /**
  695.      * Adds an item that is to be returned in the query result.
  696.      *
  697.      * <code>
  698.      *     $qb = $em->createQueryBuilder()
  699.      *         ->select('u')
  700.      *         ->addSelect('p')
  701.      *         ->from('User', 'u')
  702.      *         ->leftJoin('u.Phonenumbers', 'p');
  703.      * </code>
  704.      *
  705.      * @param mixed $select The selection expression.
  706.      *
  707.      * @return self
  708.      */
  709.     public function addSelect($select null)
  710.     {
  711.         $this->_type self::SELECT;
  712.         if (empty($select)) {
  713.             return $this;
  714.         }
  715.         $selects is_array($select) ? $select func_get_args();
  716.         return $this->add('select', new Expr\Select($selects), true);
  717.     }
  718.     /**
  719.      * Turns the query being built into a bulk delete query that ranges over
  720.      * a certain entity type.
  721.      *
  722.      * <code>
  723.      *     $qb = $em->createQueryBuilder()
  724.      *         ->delete('User', 'u')
  725.      *         ->where('u.id = :user_id')
  726.      *         ->setParameter('user_id', 1);
  727.      * </code>
  728.      *
  729.      * @param string $delete The class/type whose instances are subject to the deletion.
  730.      * @param string $alias  The class/type alias used in the constructed query.
  731.      *
  732.      * @return self
  733.      */
  734.     public function delete($delete null$alias null)
  735.     {
  736.         $this->_type self::DELETE;
  737.         if (! $delete) {
  738.             return $this;
  739.         }
  740.         return $this->add('from', new Expr\From($delete$alias));
  741.     }
  742.     /**
  743.      * Turns the query being built into a bulk update query that ranges over
  744.      * a certain entity type.
  745.      *
  746.      * <code>
  747.      *     $qb = $em->createQueryBuilder()
  748.      *         ->update('User', 'u')
  749.      *         ->set('u.password', '?1')
  750.      *         ->where('u.id = ?2');
  751.      * </code>
  752.      *
  753.      * @param string $update The class/type whose instances are subject to the update.
  754.      * @param string $alias  The class/type alias used in the constructed query.
  755.      *
  756.      * @return self
  757.      */
  758.     public function update($update null$alias null)
  759.     {
  760.         $this->_type self::UPDATE;
  761.         if (! $update) {
  762.             return $this;
  763.         }
  764.         return $this->add('from', new Expr\From($update$alias));
  765.     }
  766.     /**
  767.      * Creates and adds a query root corresponding to the entity identified by the given alias,
  768.      * forming a cartesian product with any existing query roots.
  769.      *
  770.      * <code>
  771.      *     $qb = $em->createQueryBuilder()
  772.      *         ->select('u')
  773.      *         ->from('User', 'u');
  774.      * </code>
  775.      *
  776.      * @param string $from    The class name.
  777.      * @param string $alias   The alias of the class.
  778.      * @param string $indexBy The index for the from.
  779.      *
  780.      * @return self
  781.      */
  782.     public function from($from$alias$indexBy null)
  783.     {
  784.         return $this->add('from', new Expr\From($from$alias$indexBy), true);
  785.     }
  786.     /**
  787.      * Updates a query root corresponding to an entity setting its index by. This method is intended to be used with
  788.      * EntityRepository->createQueryBuilder(), which creates the initial FROM clause and do not allow you to update it
  789.      * setting an index by.
  790.      *
  791.      * <code>
  792.      *     $qb = $userRepository->createQueryBuilder('u')
  793.      *         ->indexBy('u', 'u.id');
  794.      *
  795.      *     // Is equivalent to...
  796.      *
  797.      *     $qb = $em->createQueryBuilder()
  798.      *         ->select('u')
  799.      *         ->from('User', 'u', 'u.id');
  800.      * </code>
  801.      *
  802.      * @param string $alias   The root alias of the class.
  803.      * @param string $indexBy The index for the from.
  804.      *
  805.      * @return self
  806.      *
  807.      * @throws Query\QueryException
  808.      */
  809.     public function indexBy($alias$indexBy)
  810.     {
  811.         $rootAliases $this->getRootAliases();
  812.         if (! in_array($alias$rootAliases)) {
  813.             throw new Query\QueryException(
  814.                 sprintf('Specified root alias %s must be set before invoking indexBy().'$alias)
  815.             );
  816.         }
  817.         foreach ($this->_dqlParts['from'] as &$fromClause) {
  818.             assert($fromClause instanceof Expr\From);
  819.             if ($fromClause->getAlias() !== $alias) {
  820.                 continue;
  821.             }
  822.             $fromClause = new Expr\From($fromClause->getFrom(), $fromClause->getAlias(), $indexBy);
  823.         }
  824.         return $this;
  825.     }
  826.     /**
  827.      * Creates and adds a join over an entity association to the query.
  828.      *
  829.      * The entities in the joined association will be fetched as part of the query
  830.      * result if the alias used for the joined association is placed in the select
  831.      * expressions.
  832.      *
  833.      * <code>
  834.      *     $qb = $em->createQueryBuilder()
  835.      *         ->select('u')
  836.      *         ->from('User', 'u')
  837.      *         ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
  838.      * </code>
  839.      *
  840.      * @param string      $join          The relationship to join.
  841.      * @param string      $alias         The alias of the join.
  842.      * @param string|null $conditionType The condition type constant. Either ON or WITH.
  843.      * @param string|null $condition     The condition for the join.
  844.      * @param string|null $indexBy       The index for the join.
  845.      *
  846.      * @return self
  847.      */
  848.     public function join($join$alias$conditionType null$condition null$indexBy null)
  849.     {
  850.         return $this->innerJoin($join$alias$conditionType$condition$indexBy);
  851.     }
  852.     /**
  853.      * Creates and adds a join over an entity association to the query.
  854.      *
  855.      * The entities in the joined association will be fetched as part of the query
  856.      * result if the alias used for the joined association is placed in the select
  857.      * expressions.
  858.      *
  859.      *     [php]
  860.      *     $qb = $em->createQueryBuilder()
  861.      *         ->select('u')
  862.      *         ->from('User', 'u')
  863.      *         ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
  864.      *
  865.      * @param string      $join          The relationship to join.
  866.      * @param string      $alias         The alias of the join.
  867.      * @param string|null $conditionType The condition type constant. Either ON or WITH.
  868.      * @param string|null $condition     The condition for the join.
  869.      * @param string|null $indexBy       The index for the join.
  870.      *
  871.      * @return self
  872.      */
  873.     public function innerJoin($join$alias$conditionType null$condition null$indexBy null)
  874.     {
  875.         $parentAlias substr($join0strpos($join'.'));
  876.         $rootAlias $this->findRootAlias($alias$parentAlias);
  877.         $join = new Expr\Join(
  878.             Expr\Join::INNER_JOIN,
  879.             $join,
  880.             $alias,
  881.             $conditionType,
  882.             $condition,
  883.             $indexBy
  884.         );
  885.         return $this->add('join', [$rootAlias => $join], true);
  886.     }
  887.     /**
  888.      * Creates and adds a left join over an entity association to the query.
  889.      *
  890.      * The entities in the joined association will be fetched as part of the query
  891.      * result if the alias used for the joined association is placed in the select
  892.      * expressions.
  893.      *
  894.      * <code>
  895.      *     $qb = $em->createQueryBuilder()
  896.      *         ->select('u')
  897.      *         ->from('User', 'u')
  898.      *         ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
  899.      * </code>
  900.      *
  901.      * @param string      $join          The relationship to join.
  902.      * @param string      $alias         The alias of the join.
  903.      * @param string|null $conditionType The condition type constant. Either ON or WITH.
  904.      * @param string|null $condition     The condition for the join.
  905.      * @param string|null $indexBy       The index for the join.
  906.      *
  907.      * @return self
  908.      */
  909.     public function leftJoin($join$alias$conditionType null$condition null$indexBy null)
  910.     {
  911.         $parentAlias substr($join0strpos($join'.'));
  912.         $rootAlias $this->findRootAlias($alias$parentAlias);
  913.         $join = new Expr\Join(
  914.             Expr\Join::LEFT_JOIN,
  915.             $join,
  916.             $alias,
  917.             $conditionType,
  918.             $condition,
  919.             $indexBy
  920.         );
  921.         return $this->add('join', [$rootAlias => $join], true);
  922.     }
  923.     /**
  924.      * Sets a new value for a field in a bulk update query.
  925.      *
  926.      * <code>
  927.      *     $qb = $em->createQueryBuilder()
  928.      *         ->update('User', 'u')
  929.      *         ->set('u.password', '?1')
  930.      *         ->where('u.id = ?2');
  931.      * </code>
  932.      *
  933.      * @param string $key   The key/field to set.
  934.      * @param mixed  $value The value, expression, placeholder, etc.
  935.      *
  936.      * @return self
  937.      */
  938.     public function set($key$value)
  939.     {
  940.         return $this->add('set', new Expr\Comparison($keyExpr\Comparison::EQ$value), true);
  941.     }
  942.     /**
  943.      * Specifies one or more restrictions to the query result.
  944.      * Replaces any previously specified restrictions, if any.
  945.      *
  946.      * <code>
  947.      *     $qb = $em->createQueryBuilder()
  948.      *         ->select('u')
  949.      *         ->from('User', 'u')
  950.      *         ->where('u.id = ?');
  951.      *
  952.      *     // You can optionally programmatically build and/or expressions
  953.      *     $qb = $em->createQueryBuilder();
  954.      *
  955.      *     $or = $qb->expr()->orX();
  956.      *     $or->add($qb->expr()->eq('u.id', 1));
  957.      *     $or->add($qb->expr()->eq('u.id', 2));
  958.      *
  959.      *     $qb->update('User', 'u')
  960.      *         ->set('u.password', '?')
  961.      *         ->where($or);
  962.      * </code>
  963.      *
  964.      * @param mixed $predicates The restriction predicates.
  965.      *
  966.      * @return self
  967.      */
  968.     public function where($predicates)
  969.     {
  970.         if (! (func_num_args() === && $predicates instanceof Expr\Composite)) {
  971.             $predicates = new Expr\Andx(func_get_args());
  972.         }
  973.         return $this->add('where'$predicates);
  974.     }
  975.     /**
  976.      * Adds one or more restrictions to the query results, forming a logical
  977.      * conjunction with any previously specified restrictions.
  978.      *
  979.      * <code>
  980.      *     $qb = $em->createQueryBuilder()
  981.      *         ->select('u')
  982.      *         ->from('User', 'u')
  983.      *         ->where('u.username LIKE ?')
  984.      *         ->andWhere('u.is_active = 1');
  985.      * </code>
  986.      *
  987.      * @see where()
  988.      *
  989.      * @param mixed $where The query restrictions.
  990.      *
  991.      * @return self
  992.      */
  993.     public function andWhere()
  994.     {
  995.         $args  func_get_args();
  996.         $where $this->getDQLPart('where');
  997.         if ($where instanceof Expr\Andx) {
  998.             $where->addMultiple($args);
  999.         } else {
  1000.             array_unshift($args$where);
  1001.             $where = new Expr\Andx($args);
  1002.         }
  1003.         return $this->add('where'$where);
  1004.     }
  1005.     /**
  1006.      * Adds one or more restrictions to the query results, forming a logical
  1007.      * disjunction with any previously specified restrictions.
  1008.      *
  1009.      * <code>
  1010.      *     $qb = $em->createQueryBuilder()
  1011.      *         ->select('u')
  1012.      *         ->from('User', 'u')
  1013.      *         ->where('u.id = 1')
  1014.      *         ->orWhere('u.id = 2');
  1015.      * </code>
  1016.      *
  1017.      * @see where()
  1018.      *
  1019.      * @param mixed $where The WHERE statement.
  1020.      *
  1021.      * @return self
  1022.      */
  1023.     public function orWhere()
  1024.     {
  1025.         $args  func_get_args();
  1026.         $where $this->getDQLPart('where');
  1027.         if ($where instanceof Expr\Orx) {
  1028.             $where->addMultiple($args);
  1029.         } else {
  1030.             array_unshift($args$where);
  1031.             $where = new Expr\Orx($args);
  1032.         }
  1033.         return $this->add('where'$where);
  1034.     }
  1035.     /**
  1036.      * Specifies a grouping over the results of the query.
  1037.      * Replaces any previously specified groupings, if any.
  1038.      *
  1039.      * <code>
  1040.      *     $qb = $em->createQueryBuilder()
  1041.      *         ->select('u')
  1042.      *         ->from('User', 'u')
  1043.      *         ->groupBy('u.id');
  1044.      * </code>
  1045.      *
  1046.      * @param string $groupBy The grouping expression.
  1047.      *
  1048.      * @return self
  1049.      */
  1050.     public function groupBy($groupBy)
  1051.     {
  1052.         return $this->add('groupBy', new Expr\GroupBy(func_get_args()));
  1053.     }
  1054.     /**
  1055.      * Adds a grouping expression to the query.
  1056.      *
  1057.      * <code>
  1058.      *     $qb = $em->createQueryBuilder()
  1059.      *         ->select('u')
  1060.      *         ->from('User', 'u')
  1061.      *         ->groupBy('u.lastLogin')
  1062.      *         ->addGroupBy('u.createdAt');
  1063.      * </code>
  1064.      *
  1065.      * @param string $groupBy The grouping expression.
  1066.      *
  1067.      * @return self
  1068.      */
  1069.     public function addGroupBy($groupBy)
  1070.     {
  1071.         return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true);
  1072.     }
  1073.     /**
  1074.      * Specifies a restriction over the groups of the query.
  1075.      * Replaces any previous having restrictions, if any.
  1076.      *
  1077.      * @param mixed $having The restriction over the groups.
  1078.      *
  1079.      * @return self
  1080.      */
  1081.     public function having($having)
  1082.     {
  1083.         if (! (func_num_args() === && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) {
  1084.             $having = new Expr\Andx(func_get_args());
  1085.         }
  1086.         return $this->add('having'$having);
  1087.     }
  1088.     /**
  1089.      * Adds a restriction over the groups of the query, forming a logical
  1090.      * conjunction with any existing having restrictions.
  1091.      *
  1092.      * @param mixed $having The restriction to append.
  1093.      *
  1094.      * @return self
  1095.      */
  1096.     public function andHaving($having)
  1097.     {
  1098.         $args   func_get_args();
  1099.         $having $this->getDQLPart('having');
  1100.         if ($having instanceof Expr\Andx) {
  1101.             $having->addMultiple($args);
  1102.         } else {
  1103.             array_unshift($args$having);
  1104.             $having = new Expr\Andx($args);
  1105.         }
  1106.         return $this->add('having'$having);
  1107.     }
  1108.     /**
  1109.      * Adds a restriction over the groups of the query, forming a logical
  1110.      * disjunction with any existing having restrictions.
  1111.      *
  1112.      * @param mixed $having The restriction to add.
  1113.      *
  1114.      * @return self
  1115.      */
  1116.     public function orHaving($having)
  1117.     {
  1118.         $args   func_get_args();
  1119.         $having $this->getDQLPart('having');
  1120.         if ($having instanceof Expr\Orx) {
  1121.             $having->addMultiple($args);
  1122.         } else {
  1123.             array_unshift($args$having);
  1124.             $having = new Expr\Orx($args);
  1125.         }
  1126.         return $this->add('having'$having);
  1127.     }
  1128.     /**
  1129.      * Specifies an ordering for the query results.
  1130.      * Replaces any previously specified orderings, if any.
  1131.      *
  1132.      * @param string|Expr\OrderBy $sort  The ordering expression.
  1133.      * @param string              $order The ordering direction.
  1134.      *
  1135.      * @return self
  1136.      */
  1137.     public function orderBy($sort$order null)
  1138.     {
  1139.         $orderBy $sort instanceof Expr\OrderBy $sort : new Expr\OrderBy($sort$order);
  1140.         return $this->add('orderBy'$orderBy);
  1141.     }
  1142.     /**
  1143.      * Adds an ordering to the query results.
  1144.      *
  1145.      * @param string|Expr\OrderBy $sort  The ordering expression.
  1146.      * @param string              $order The ordering direction.
  1147.      *
  1148.      * @return self
  1149.      */
  1150.     public function addOrderBy($sort$order null)
  1151.     {
  1152.         $orderBy $sort instanceof Expr\OrderBy $sort : new Expr\OrderBy($sort$order);
  1153.         return $this->add('orderBy'$orderBytrue);
  1154.     }
  1155.     /**
  1156.      * Adds criteria to the query.
  1157.      *
  1158.      * Adds where expressions with AND operator.
  1159.      * Adds orderings.
  1160.      * Overrides firstResult and maxResults if they're set.
  1161.      *
  1162.      * @return self
  1163.      *
  1164.      * @throws Query\QueryException
  1165.      */
  1166.     public function addCriteria(Criteria $criteria)
  1167.     {
  1168.         $allAliases $this->getAllAliases();
  1169.         if (! isset($allAliases[0])) {
  1170.             throw new Query\QueryException('No aliases are set before invoking addCriteria().');
  1171.         }
  1172.         $visitor = new QueryExpressionVisitor($this->getAllAliases());
  1173.         $whereExpression $criteria->getWhereExpression();
  1174.         if ($whereExpression) {
  1175.             $this->andWhere($visitor->dispatch($whereExpression));
  1176.             foreach ($visitor->getParameters() as $parameter) {
  1177.                 $this->parameters->add($parameter);
  1178.             }
  1179.         }
  1180.         if ($criteria->getOrderings()) {
  1181.             foreach ($criteria->getOrderings() as $sort => $order) {
  1182.                 $hasValidAlias false;
  1183.                 foreach ($allAliases as $alias) {
  1184.                     if (strpos($sort '.'$alias '.') === 0) {
  1185.                         $hasValidAlias true;
  1186.                         break;
  1187.                     }
  1188.                 }
  1189.                 if (! $hasValidAlias) {
  1190.                     $sort $allAliases[0] . '.' $sort;
  1191.                 }
  1192.                 $this->addOrderBy($sort$order);
  1193.             }
  1194.         }
  1195.         // Overwrite limits only if they was set in criteria
  1196.         $firstResult $criteria->getFirstResult();
  1197.         if ($firstResult !== null) {
  1198.             $this->setFirstResult($firstResult);
  1199.         }
  1200.         $maxResults $criteria->getMaxResults();
  1201.         if ($maxResults !== null) {
  1202.             $this->setMaxResults($maxResults);
  1203.         }
  1204.         return $this;
  1205.     }
  1206.     /**
  1207.      * Gets a query part by its name.
  1208.      *
  1209.      * @param string $queryPartName
  1210.      *
  1211.      * @return mixed $queryPart
  1212.      */
  1213.     public function getDQLPart($queryPartName)
  1214.     {
  1215.         return $this->_dqlParts[$queryPartName];
  1216.     }
  1217.     /**
  1218.      * Gets all query parts.
  1219.      *
  1220.      * @psalm-return array<string, mixed> $dqlParts
  1221.      */
  1222.     public function getDQLParts()
  1223.     {
  1224.         return $this->_dqlParts;
  1225.     }
  1226.     /**
  1227.      * @return string
  1228.      */
  1229.     private function getDQLForDelete()
  1230.     {
  1231.          return 'DELETE'
  1232.               $this->getReducedDQLQueryPart('from', ['pre' => ' ''separator' => ', '])
  1233.               . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
  1234.               . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ''separator' => ', ']);
  1235.     }
  1236.     /**
  1237.      * @return string
  1238.      */
  1239.     private function getDQLForUpdate()
  1240.     {
  1241.          return 'UPDATE'
  1242.               $this->getReducedDQLQueryPart('from', ['pre' => ' ''separator' => ', '])
  1243.               . $this->getReducedDQLQueryPart('set', ['pre' => ' SET ''separator' => ', '])
  1244.               . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
  1245.               . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ''separator' => ', ']);
  1246.     }
  1247.     /**
  1248.      * @return string
  1249.      */
  1250.     private function getDQLForSelect()
  1251.     {
  1252.         $dql 'SELECT'
  1253.              . ($this->_dqlParts['distinct'] === true ' DISTINCT' '')
  1254.              . $this->getReducedDQLQueryPart('select', ['pre' => ' ''separator' => ', ']);
  1255.         $fromParts   $this->getDQLPart('from');
  1256.         $joinParts   $this->getDQLPart('join');
  1257.         $fromClauses = [];
  1258.         // Loop through all FROM clauses
  1259.         if (! empty($fromParts)) {
  1260.             $dql .= ' FROM ';
  1261.             foreach ($fromParts as $from) {
  1262.                 $fromClause = (string) $from;
  1263.                 if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) {
  1264.                     foreach ($joinParts[$from->getAlias()] as $join) {
  1265.                         $fromClause .= ' ' . ((string) $join);
  1266.                     }
  1267.                 }
  1268.                 $fromClauses[] = $fromClause;
  1269.             }
  1270.         }
  1271.         $dql .= implode(', '$fromClauses)
  1272.               . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
  1273.               . $this->getReducedDQLQueryPart('groupBy', ['pre' => ' GROUP BY ''separator' => ', '])
  1274.               . $this->getReducedDQLQueryPart('having', ['pre' => ' HAVING '])
  1275.               . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ''separator' => ', ']);
  1276.         return $dql;
  1277.     }
  1278.     /**
  1279.      * @psalm-param array<string, mixed> $options
  1280.      */
  1281.     private function getReducedDQLQueryPart(string $queryPartName, array $options = []): string
  1282.     {
  1283.         $queryPart $this->getDQLPart($queryPartName);
  1284.         if (empty($queryPart)) {
  1285.             return $options['empty'] ?? '';
  1286.         }
  1287.         return ($options['pre'] ?? '')
  1288.              . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart)
  1289.              . ($options['post'] ?? '');
  1290.     }
  1291.     /**
  1292.      * Resets DQL parts.
  1293.      *
  1294.      * @return self
  1295.      *
  1296.      * @psalm-param list<string>|null $parts
  1297.      */
  1298.     public function resetDQLParts($parts null)
  1299.     {
  1300.         if ($parts === null) {
  1301.             $parts array_keys($this->_dqlParts);
  1302.         }
  1303.         foreach ($parts as $part) {
  1304.             $this->resetDQLPart($part);
  1305.         }
  1306.         return $this;
  1307.     }
  1308.     /**
  1309.      * Resets single DQL part.
  1310.      *
  1311.      * @param string $part
  1312.      *
  1313.      * @return self
  1314.      */
  1315.     public function resetDQLPart($part)
  1316.     {
  1317.         $this->_dqlParts[$part] = is_array($this->_dqlParts[$part]) ? [] : null;
  1318.         $this->_state           self::STATE_DIRTY;
  1319.         return $this;
  1320.     }
  1321.     /**
  1322.      * Gets a string representation of this QueryBuilder which corresponds to
  1323.      * the final DQL query being constructed.
  1324.      *
  1325.      * @return string The string representation of this QueryBuilder.
  1326.      */
  1327.     public function __toString()
  1328.     {
  1329.         return $this->getDQL();
  1330.     }
  1331.     /**
  1332.      * Deep clones all expression objects in the DQL parts.
  1333.      *
  1334.      * @return void
  1335.      */
  1336.     public function __clone()
  1337.     {
  1338.         foreach ($this->_dqlParts as $part => $elements) {
  1339.             if (is_array($this->_dqlParts[$part])) {
  1340.                 foreach ($this->_dqlParts[$part] as $idx => $element) {
  1341.                     if (is_object($element)) {
  1342.                         $this->_dqlParts[$part][$idx] = clone $element;
  1343.                     }
  1344.                 }
  1345.             } elseif (is_object($elements)) {
  1346.                 $this->_dqlParts[$part] = clone $elements;
  1347.             }
  1348.         }
  1349.         $parameters = [];
  1350.         foreach ($this->parameters as $parameter) {
  1351.             $parameters[] = clone $parameter;
  1352.         }
  1353.         $this->parameters = new ArrayCollection($parameters);
  1354.     }
  1355. }