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

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