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

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