vendor/doctrine/orm/src/QueryBuilder.php line 41

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