vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php line 208

Open in your IDE?
  1. <?php
  2. namespace Doctrine\DBAL\Query;
  3. use Doctrine\DBAL\Connection;
  4. use Doctrine\DBAL\Exception;
  5. use Doctrine\DBAL\ForwardCompatibility;
  6. use Doctrine\DBAL\ParameterType;
  7. use Doctrine\DBAL\Query\Expression\CompositeExpression;
  8. use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
  9. use Doctrine\DBAL\Types\Type;
  10. use Doctrine\Deprecations\Deprecation;
  11. use function array_filter;
  12. use function array_key_exists;
  13. use function array_keys;
  14. use function array_unshift;
  15. use function count;
  16. use function func_get_args;
  17. use function func_num_args;
  18. use function implode;
  19. use function is_array;
  20. use function is_object;
  21. use function key;
  22. use function strtoupper;
  23. use function substr;
  24. /**
  25.  * QueryBuilder class is responsible to dynamically create SQL queries.
  26.  *
  27.  * Important: Verify that every feature you use will work with your database vendor.
  28.  * SQL Query Builder does not attempt to validate the generated SQL at all.
  29.  *
  30.  * The query builder does no validation whatsoever if certain features even work with the
  31.  * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements
  32.  * even if some vendors such as MySQL support it.
  33.  */
  34. class QueryBuilder
  35. {
  36.     /*
  37.      * The query types.
  38.      */
  39.     public const SELECT 0;
  40.     public const DELETE 1;
  41.     public const UPDATE 2;
  42.     public const INSERT 3;
  43.     /*
  44.      * The builder states.
  45.      */
  46.     public const STATE_DIRTY 0;
  47.     public const STATE_CLEAN 1;
  48.     /**
  49.      * The DBAL Connection.
  50.      *
  51.      * @var Connection
  52.      */
  53.     private $connection;
  54.     /*
  55.      * The default values of SQL parts collection
  56.      */
  57.     private const SQL_PARTS_DEFAULTS = [
  58.         'select'   => [],
  59.         'distinct' => false,
  60.         'from'     => [],
  61.         'join'     => [],
  62.         'set'      => [],
  63.         'where'    => null,
  64.         'groupBy'  => [],
  65.         'having'   => null,
  66.         'orderBy'  => [],
  67.         'values'   => [],
  68.     ];
  69.     /**
  70.      * The array of SQL parts collected.
  71.      *
  72.      * @var mixed[]
  73.      */
  74.     private $sqlParts self::SQL_PARTS_DEFAULTS;
  75.     /**
  76.      * The complete SQL string for this query.
  77.      *
  78.      * @var string|null
  79.      */
  80.     private $sql;
  81.     /**
  82.      * The query parameters.
  83.      *
  84.      * @var array<int, mixed>|array<string, mixed>
  85.      */
  86.     private $params = [];
  87.     /**
  88.      * The parameter type map of this query.
  89.      *
  90.      * @var array<int, int|string|Type|null>|array<string, int|string|Type|null>
  91.      */
  92.     private $paramTypes = [];
  93.     /**
  94.      * The type of query this is. Can be select, update or delete.
  95.      *
  96.      * @var int
  97.      */
  98.     private $type self::SELECT;
  99.     /**
  100.      * The state of the query object. Can be dirty or clean.
  101.      *
  102.      * @var int
  103.      */
  104.     private $state self::STATE_CLEAN;
  105.     /**
  106.      * The index of the first result to retrieve.
  107.      *
  108.      * @var int
  109.      */
  110.     private $firstResult 0;
  111.     /**
  112.      * The maximum number of results to retrieve or NULL to retrieve all results.
  113.      *
  114.      * @var int|null
  115.      */
  116.     private $maxResults;
  117.     /**
  118.      * The counter of bound parameters used with {@see bindValue).
  119.      *
  120.      * @var int
  121.      */
  122.     private $boundCounter 0;
  123.     /**
  124.      * Initializes a new <tt>QueryBuilder</tt>.
  125.      *
  126.      * @param Connection $connection The DBAL Connection.
  127.      */
  128.     public function __construct(Connection $connection)
  129.     {
  130.         $this->connection $connection;
  131.     }
  132.     /**
  133.      * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
  134.      * This producer method is intended for convenient inline usage. Example:
  135.      *
  136.      * <code>
  137.      *     $qb = $conn->createQueryBuilder()
  138.      *         ->select('u')
  139.      *         ->from('users', 'u')
  140.      *         ->where($qb->expr()->eq('u.id', 1));
  141.      * </code>
  142.      *
  143.      * For more complex expression construction, consider storing the expression
  144.      * builder object in a local variable.
  145.      *
  146.      * @return ExpressionBuilder
  147.      */
  148.     public function expr()
  149.     {
  150.         return $this->connection->getExpressionBuilder();
  151.     }
  152.     /**
  153.      * Gets the type of the currently built query.
  154.      *
  155.      * @return int
  156.      */
  157.     public function getType()
  158.     {
  159.         return $this->type;
  160.     }
  161.     /**
  162.      * Gets the associated DBAL Connection for this query builder.
  163.      *
  164.      * @return Connection
  165.      */
  166.     public function getConnection()
  167.     {
  168.         return $this->connection;
  169.     }
  170.     /**
  171.      * Gets the state of this query builder instance.
  172.      *
  173.      * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
  174.      */
  175.     public function getState()
  176.     {
  177.         return $this->state;
  178.     }
  179.     /**
  180.      * Executes this query using the bound parameters and their types.
  181.      *
  182.      * @return ForwardCompatibility\Result|int|string
  183.      *
  184.      * @throws Exception
  185.      */
  186.     public function execute()
  187.     {
  188.         if ($this->type === self::SELECT) {
  189.             return ForwardCompatibility\Result::ensure(
  190.                 $this->connection->executeQuery($this->getSQL(), $this->params$this->paramTypes)
  191.             );
  192.         }
  193.         return $this->connection->executeStatement($this->getSQL(), $this->params$this->paramTypes);
  194.     }
  195.     /**
  196.      * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
  197.      *
  198.      * <code>
  199.      *     $qb = $em->createQueryBuilder()
  200.      *         ->select('u')
  201.      *         ->from('User', 'u')
  202.      *     echo $qb->getSQL(); // SELECT u FROM User u
  203.      * </code>
  204.      *
  205.      * @return string The SQL query string.
  206.      */
  207.     public function getSQL()
  208.     {
  209.         if ($this->sql !== null && $this->state === self::STATE_CLEAN) {
  210.             return $this->sql;
  211.         }
  212.         switch ($this->type) {
  213.             case self::INSERT:
  214.                 $sql $this->getSQLForInsert();
  215.                 break;
  216.             case self::DELETE:
  217.                 $sql $this->getSQLForDelete();
  218.                 break;
  219.             case self::UPDATE:
  220.                 $sql $this->getSQLForUpdate();
  221.                 break;
  222.             case self::SELECT:
  223.             default:
  224.                 $sql $this->getSQLForSelect();
  225.                 break;
  226.         }
  227.         $this->state self::STATE_CLEAN;
  228.         $this->sql   $sql;
  229.         return $sql;
  230.     }
  231.     /**
  232.      * Sets a query parameter for the query being constructed.
  233.      *
  234.      * <code>
  235.      *     $qb = $conn->createQueryBuilder()
  236.      *         ->select('u')
  237.      *         ->from('users', 'u')
  238.      *         ->where('u.id = :user_id')
  239.      *         ->setParameter(':user_id', 1);
  240.      * </code>
  241.      *
  242.      * @param int|string           $key   Parameter position or name
  243.      * @param mixed                $value Parameter value
  244.      * @param int|string|Type|null $type  Parameter type
  245.      *
  246.      * @return $this This QueryBuilder instance.
  247.      */
  248.     public function setParameter($key$value$type null)
  249.     {
  250.         if ($type !== null) {
  251.             $this->paramTypes[$key] = $type;
  252.         }
  253.         $this->params[$key] = $value;
  254.         return $this;
  255.     }
  256.     /**
  257.      * Sets a collection of query parameters for the query being constructed.
  258.      *
  259.      * <code>
  260.      *     $qb = $conn->createQueryBuilder()
  261.      *         ->select('u')
  262.      *         ->from('users', 'u')
  263.      *         ->where('u.id = :user_id1 OR u.id = :user_id2')
  264.      *         ->setParameters(array(
  265.      *             ':user_id1' => 1,
  266.      *             ':user_id2' => 2
  267.      *         ));
  268.      * </code>
  269.      *
  270.      * @param array<int, mixed>|array<string, mixed>                               $params Parameters to set
  271.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  272.      *
  273.      * @return $this This QueryBuilder instance.
  274.      */
  275.     public function setParameters(array $params, array $types = [])
  276.     {
  277.         $this->paramTypes $types;
  278.         $this->params     $params;
  279.         return $this;
  280.     }
  281.     /**
  282.      * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
  283.      *
  284.      * @return array<int, mixed>|array<string, mixed> The currently defined query parameters
  285.      */
  286.     public function getParameters()
  287.     {
  288.         return $this->params;
  289.     }
  290.     /**
  291.      * Gets a (previously set) query parameter of the query being constructed.
  292.      *
  293.      * @param mixed $key The key (index or name) of the bound parameter.
  294.      *
  295.      * @return mixed The value of the bound parameter.
  296.      */
  297.     public function getParameter($key)
  298.     {
  299.         return $this->params[$key] ?? null;
  300.     }
  301.     /**
  302.      * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
  303.      *
  304.      * @return array<int, int|string|Type|null>|array<string, int|string|Type|null> The currently defined
  305.      *                                                                              query parameter types
  306.      */
  307.     public function getParameterTypes()
  308.     {
  309.         return $this->paramTypes;
  310.     }
  311.     /**
  312.      * Gets a (previously set) query parameter type of the query being constructed.
  313.      *
  314.      * @param int|string $key The key of the bound parameter type
  315.      *
  316.      * @return int|string|Type|null The value of the bound parameter type
  317.      */
  318.     public function getParameterType($key)
  319.     {
  320.         return $this->paramTypes[$key] ?? null;
  321.     }
  322.     /**
  323.      * Sets the position of the first result to retrieve (the "offset").
  324.      *
  325.      * @param int $firstResult The first result to return.
  326.      *
  327.      * @return $this This QueryBuilder instance.
  328.      */
  329.     public function setFirstResult($firstResult)
  330.     {
  331.         $this->state       self::STATE_DIRTY;
  332.         $this->firstResult $firstResult;
  333.         return $this;
  334.     }
  335.     /**
  336.      * Gets the position of the first result the query object was set to retrieve (the "offset").
  337.      *
  338.      * @return int The position of the first result.
  339.      */
  340.     public function getFirstResult()
  341.     {
  342.         return $this->firstResult;
  343.     }
  344.     /**
  345.      * Sets the maximum number of results to retrieve (the "limit").
  346.      *
  347.      * @param int|null $maxResults The maximum number of results to retrieve or NULL to retrieve all results.
  348.      *
  349.      * @return $this This QueryBuilder instance.
  350.      */
  351.     public function setMaxResults($maxResults)
  352.     {
  353.         $this->state      self::STATE_DIRTY;
  354.         $this->maxResults $maxResults;
  355.         return $this;
  356.     }
  357.     /**
  358.      * Gets the maximum number of results the query object was set to retrieve (the "limit").
  359.      * Returns NULL if all results will be returned.
  360.      *
  361.      * @return int|null The maximum number of results.
  362.      */
  363.     public function getMaxResults()
  364.     {
  365.         return $this->maxResults;
  366.     }
  367.     /**
  368.      * Either appends to or replaces a single, generic query part.
  369.      *
  370.      * The available parts are: 'select', 'from', 'set', 'where',
  371.      * 'groupBy', 'having' and 'orderBy'.
  372.      *
  373.      * @param string $sqlPartName
  374.      * @param mixed  $sqlPart
  375.      * @param bool   $append
  376.      *
  377.      * @return $this This QueryBuilder instance.
  378.      */
  379.     public function add($sqlPartName$sqlPart$append false)
  380.     {
  381.         $isArray    is_array($sqlPart);
  382.         $isMultiple is_array($this->sqlParts[$sqlPartName]);
  383.         if ($isMultiple && ! $isArray) {
  384.             $sqlPart = [$sqlPart];
  385.         }
  386.         $this->state self::STATE_DIRTY;
  387.         if ($append) {
  388.             if (
  389.                 $sqlPartName === 'orderBy'
  390.                 || $sqlPartName === 'groupBy'
  391.                 || $sqlPartName === 'select'
  392.                 || $sqlPartName === 'set'
  393.             ) {
  394.                 foreach ($sqlPart as $part) {
  395.                     $this->sqlParts[$sqlPartName][] = $part;
  396.                 }
  397.             } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) {
  398.                 $key                                  key($sqlPart);
  399.                 $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key];
  400.             } elseif ($isMultiple) {
  401.                 $this->sqlParts[$sqlPartName][] = $sqlPart;
  402.             } else {
  403.                 $this->sqlParts[$sqlPartName] = $sqlPart;
  404.             }
  405.             return $this;
  406.         }
  407.         $this->sqlParts[$sqlPartName] = $sqlPart;
  408.         return $this;
  409.     }
  410.     /**
  411.      * Specifies an item that is to be returned in the query result.
  412.      * Replaces any previously specified selections, if any.
  413.      *
  414.      * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  415.      *
  416.      * <code>
  417.      *     $qb = $conn->createQueryBuilder()
  418.      *         ->select('u.id', 'p.id')
  419.      *         ->from('users', 'u')
  420.      *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
  421.      * </code>
  422.      *
  423.      * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED.
  424.      *                                     Pass each value as an individual argument.
  425.      *
  426.      * @return $this This QueryBuilder instance.
  427.      */
  428.     public function select($select null/*, string ...$selects*/)
  429.     {
  430.         $this->type self::SELECT;
  431.         if (empty($select)) {
  432.             return $this;
  433.         }
  434.         if (is_array($select)) {
  435.             Deprecation::trigger(
  436.                 'doctrine/dbal',
  437.                 'https://github.com/doctrine/dbal/issues/3837',
  438.                 'Passing an array for the first argument to QueryBuilder::select is deprecated, ' .
  439.                 'pass each value as an individual variadic argument instead.'
  440.             );
  441.         }
  442.         $selects is_array($select) ? $select func_get_args();
  443.         return $this->add('select'$selects);
  444.     }
  445.     /**
  446.      * Adds DISTINCT to the query.
  447.      *
  448.      * <code>
  449.      *     $qb = $conn->createQueryBuilder()
  450.      *         ->select('u.id')
  451.      *         ->distinct()
  452.      *         ->from('users', 'u')
  453.      * </code>
  454.      *
  455.      * @return $this This QueryBuilder instance.
  456.      */
  457.     public function distinct(): self
  458.     {
  459.         $this->sqlParts['distinct'] = true;
  460.         return $this;
  461.     }
  462.     /**
  463.      * Adds an item that is to be returned in the query result.
  464.      *
  465.      * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  466.      *
  467.      * <code>
  468.      *     $qb = $conn->createQueryBuilder()
  469.      *         ->select('u.id')
  470.      *         ->addSelect('p.id')
  471.      *         ->from('users', 'u')
  472.      *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
  473.      * </code>
  474.      *
  475.      * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED.
  476.      *                                     Pass each value as an individual argument.
  477.      *
  478.      * @return $this This QueryBuilder instance.
  479.      */
  480.     public function addSelect($select null/*, string ...$selects*/)
  481.     {
  482.         $this->type self::SELECT;
  483.         if (empty($select)) {
  484.             return $this;
  485.         }
  486.         if (is_array($select)) {
  487.             Deprecation::trigger(
  488.                 'doctrine/dbal',
  489.                 'https://github.com/doctrine/dbal/issues/3837',
  490.                 'Passing an array for the first argument to QueryBuilder::addSelect is deprecated, ' .
  491.                 'pass each value as an individual variadic argument instead.'
  492.             );
  493.         }
  494.         $selects is_array($select) ? $select func_get_args();
  495.         return $this->add('select'$selectstrue);
  496.     }
  497.     /**
  498.      * Turns the query being built into a bulk delete query that ranges over
  499.      * a certain table.
  500.      *
  501.      * <code>
  502.      *     $qb = $conn->createQueryBuilder()
  503.      *         ->delete('users', 'u')
  504.      *         ->where('u.id = :user_id')
  505.      *         ->setParameter(':user_id', 1);
  506.      * </code>
  507.      *
  508.      * @param string $delete The table whose rows are subject to the deletion.
  509.      * @param string $alias  The table alias used in the constructed query.
  510.      *
  511.      * @return $this This QueryBuilder instance.
  512.      */
  513.     public function delete($delete null$alias null)
  514.     {
  515.         $this->type self::DELETE;
  516.         if (! $delete) {
  517.             return $this;
  518.         }
  519.         return $this->add('from', [
  520.             'table' => $delete,
  521.             'alias' => $alias,
  522.         ]);
  523.     }
  524.     /**
  525.      * Turns the query being built into a bulk update query that ranges over
  526.      * a certain table
  527.      *
  528.      * <code>
  529.      *     $qb = $conn->createQueryBuilder()
  530.      *         ->update('counters', 'c')
  531.      *         ->set('c.value', 'c.value + 1')
  532.      *         ->where('c.id = ?');
  533.      * </code>
  534.      *
  535.      * @param string $update The table whose rows are subject to the update.
  536.      * @param string $alias  The table alias used in the constructed query.
  537.      *
  538.      * @return $this This QueryBuilder instance.
  539.      */
  540.     public function update($update null$alias null)
  541.     {
  542.         $this->type self::UPDATE;
  543.         if (! $update) {
  544.             return $this;
  545.         }
  546.         return $this->add('from', [
  547.             'table' => $update,
  548.             'alias' => $alias,
  549.         ]);
  550.     }
  551.     /**
  552.      * Turns the query being built into an insert query that inserts into
  553.      * a certain table
  554.      *
  555.      * <code>
  556.      *     $qb = $conn->createQueryBuilder()
  557.      *         ->insert('users')
  558.      *         ->values(
  559.      *             array(
  560.      *                 'name' => '?',
  561.      *                 'password' => '?'
  562.      *             )
  563.      *         );
  564.      * </code>
  565.      *
  566.      * @param string $insert The table into which the rows should be inserted.
  567.      *
  568.      * @return $this This QueryBuilder instance.
  569.      */
  570.     public function insert($insert null)
  571.     {
  572.         $this->type self::INSERT;
  573.         if (! $insert) {
  574.             return $this;
  575.         }
  576.         return $this->add('from', ['table' => $insert]);
  577.     }
  578.     /**
  579.      * Creates and adds a query root corresponding to the table identified by the
  580.      * given alias, forming a cartesian product with any existing query roots.
  581.      *
  582.      * <code>
  583.      *     $qb = $conn->createQueryBuilder()
  584.      *         ->select('u.id')
  585.      *         ->from('users', 'u')
  586.      * </code>
  587.      *
  588.      * @param string      $from  The table.
  589.      * @param string|null $alias The alias of the table.
  590.      *
  591.      * @return $this This QueryBuilder instance.
  592.      */
  593.     public function from($from$alias null)
  594.     {
  595.         return $this->add('from', [
  596.             'table' => $from,
  597.             'alias' => $alias,
  598.         ], true);
  599.     }
  600.     /**
  601.      * Creates and adds a join to the query.
  602.      *
  603.      * <code>
  604.      *     $qb = $conn->createQueryBuilder()
  605.      *         ->select('u.name')
  606.      *         ->from('users', 'u')
  607.      *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  608.      * </code>
  609.      *
  610.      * @param string $fromAlias The alias that points to a from clause.
  611.      * @param string $join      The table name to join.
  612.      * @param string $alias     The alias of the join table.
  613.      * @param string $condition The condition for the join.
  614.      *
  615.      * @return $this This QueryBuilder instance.
  616.      */
  617.     public function join($fromAlias$join$alias$condition null)
  618.     {
  619.         return $this->innerJoin($fromAlias$join$alias$condition);
  620.     }
  621.     /**
  622.      * Creates and adds a join to the query.
  623.      *
  624.      * <code>
  625.      *     $qb = $conn->createQueryBuilder()
  626.      *         ->select('u.name')
  627.      *         ->from('users', 'u')
  628.      *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  629.      * </code>
  630.      *
  631.      * @param string $fromAlias The alias that points to a from clause.
  632.      * @param string $join      The table name to join.
  633.      * @param string $alias     The alias of the join table.
  634.      * @param string $condition The condition for the join.
  635.      *
  636.      * @return $this This QueryBuilder instance.
  637.      */
  638.     public function innerJoin($fromAlias$join$alias$condition null)
  639.     {
  640.         return $this->add('join', [
  641.             $fromAlias => [
  642.                 'joinType'      => 'inner',
  643.                 'joinTable'     => $join,
  644.                 'joinAlias'     => $alias,
  645.                 'joinCondition' => $condition,
  646.             ],
  647.         ], true);
  648.     }
  649.     /**
  650.      * Creates and adds a left join to the query.
  651.      *
  652.      * <code>
  653.      *     $qb = $conn->createQueryBuilder()
  654.      *         ->select('u.name')
  655.      *         ->from('users', 'u')
  656.      *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  657.      * </code>
  658.      *
  659.      * @param string $fromAlias The alias that points to a from clause.
  660.      * @param string $join      The table name to join.
  661.      * @param string $alias     The alias of the join table.
  662.      * @param string $condition The condition for the join.
  663.      *
  664.      * @return $this This QueryBuilder instance.
  665.      */
  666.     public function leftJoin($fromAlias$join$alias$condition null)
  667.     {
  668.         return $this->add('join', [
  669.             $fromAlias => [
  670.                 'joinType'      => 'left',
  671.                 'joinTable'     => $join,
  672.                 'joinAlias'     => $alias,
  673.                 'joinCondition' => $condition,
  674.             ],
  675.         ], true);
  676.     }
  677.     /**
  678.      * Creates and adds a right join to the query.
  679.      *
  680.      * <code>
  681.      *     $qb = $conn->createQueryBuilder()
  682.      *         ->select('u.name')
  683.      *         ->from('users', 'u')
  684.      *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  685.      * </code>
  686.      *
  687.      * @param string $fromAlias The alias that points to a from clause.
  688.      * @param string $join      The table name to join.
  689.      * @param string $alias     The alias of the join table.
  690.      * @param string $condition The condition for the join.
  691.      *
  692.      * @return $this This QueryBuilder instance.
  693.      */
  694.     public function rightJoin($fromAlias$join$alias$condition null)
  695.     {
  696.         return $this->add('join', [
  697.             $fromAlias => [
  698.                 'joinType'      => 'right',
  699.                 'joinTable'     => $join,
  700.                 'joinAlias'     => $alias,
  701.                 'joinCondition' => $condition,
  702.             ],
  703.         ], true);
  704.     }
  705.     /**
  706.      * Sets a new value for a column in a bulk update query.
  707.      *
  708.      * <code>
  709.      *     $qb = $conn->createQueryBuilder()
  710.      *         ->update('counters', 'c')
  711.      *         ->set('c.value', 'c.value + 1')
  712.      *         ->where('c.id = ?');
  713.      * </code>
  714.      *
  715.      * @param string $key   The column to set.
  716.      * @param string $value The value, expression, placeholder, etc.
  717.      *
  718.      * @return $this This QueryBuilder instance.
  719.      */
  720.     public function set($key$value)
  721.     {
  722.         return $this->add('set'$key ' = ' $valuetrue);
  723.     }
  724.     /**
  725.      * Specifies one or more restrictions to the query result.
  726.      * Replaces any previously specified restrictions, if any.
  727.      *
  728.      * <code>
  729.      *     $qb = $conn->createQueryBuilder()
  730.      *         ->select('c.value')
  731.      *         ->from('counters', 'c')
  732.      *         ->where('c.id = ?');
  733.      *
  734.      *     // You can optionally programatically build and/or expressions
  735.      *     $qb = $conn->createQueryBuilder();
  736.      *
  737.      *     $or = $qb->expr()->orx();
  738.      *     $or->add($qb->expr()->eq('c.id', 1));
  739.      *     $or->add($qb->expr()->eq('c.id', 2));
  740.      *
  741.      *     $qb->update('counters', 'c')
  742.      *         ->set('c.value', 'c.value + 1')
  743.      *         ->where($or);
  744.      * </code>
  745.      *
  746.      * @param mixed $predicates The restriction predicates.
  747.      *
  748.      * @return $this This QueryBuilder instance.
  749.      */
  750.     public function where($predicates)
  751.     {
  752.         if (! (func_num_args() === && $predicates instanceof CompositeExpression)) {
  753.             $predicates CompositeExpression::and(...func_get_args());
  754.         }
  755.         return $this->add('where'$predicates);
  756.     }
  757.     /**
  758.      * Adds one or more restrictions to the query results, forming a logical
  759.      * conjunction with any previously specified restrictions.
  760.      *
  761.      * <code>
  762.      *     $qb = $conn->createQueryBuilder()
  763.      *         ->select('u')
  764.      *         ->from('users', 'u')
  765.      *         ->where('u.username LIKE ?')
  766.      *         ->andWhere('u.is_active = 1');
  767.      * </code>
  768.      *
  769.      * @see where()
  770.      *
  771.      * @param mixed $where The query restrictions.
  772.      *
  773.      * @return $this This QueryBuilder instance.
  774.      */
  775.     public function andWhere($where)
  776.     {
  777.         $args  func_get_args();
  778.         $args  array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  779.         $where $this->getQueryPart('where');
  780.         if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
  781.             if (count($args) > 0) {
  782.                 $where $where->with(...$args);
  783.             }
  784.         } else {
  785.             array_unshift($args$where);
  786.             $where CompositeExpression::and(...$args);
  787.         }
  788.         return $this->add('where'$wheretrue);
  789.     }
  790.     /**
  791.      * Adds one or more restrictions to the query results, forming a logical
  792.      * disjunction with any previously specified restrictions.
  793.      *
  794.      * <code>
  795.      *     $qb = $em->createQueryBuilder()
  796.      *         ->select('u.name')
  797.      *         ->from('users', 'u')
  798.      *         ->where('u.id = 1')
  799.      *         ->orWhere('u.id = 2');
  800.      * </code>
  801.      *
  802.      * @see where()
  803.      *
  804.      * @param mixed $where The WHERE statement.
  805.      *
  806.      * @return $this This QueryBuilder instance.
  807.      */
  808.     public function orWhere($where)
  809.     {
  810.         $args  func_get_args();
  811.         $args  array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  812.         $where $this->getQueryPart('where');
  813.         if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
  814.             if (count($args) > 0) {
  815.                 $where $where->with(...$args);
  816.             }
  817.         } else {
  818.             array_unshift($args$where);
  819.             $where CompositeExpression::or(...$args);
  820.         }
  821.         return $this->add('where'$wheretrue);
  822.     }
  823.     /**
  824.      * Specifies a grouping over the results of the query.
  825.      * Replaces any previously specified groupings, if any.
  826.      *
  827.      * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  828.      *
  829.      * <code>
  830.      *     $qb = $conn->createQueryBuilder()
  831.      *         ->select('u.name')
  832.      *         ->from('users', 'u')
  833.      *         ->groupBy('u.id');
  834.      * </code>
  835.      *
  836.      * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED.
  837.      *                                 Pass each value as an individual argument.
  838.      *
  839.      * @return $this This QueryBuilder instance.
  840.      */
  841.     public function groupBy($groupBy/*, string ...$groupBys*/)
  842.     {
  843.         if (empty($groupBy)) {
  844.             return $this;
  845.         }
  846.         if (is_array($groupBy)) {
  847.             Deprecation::trigger(
  848.                 'doctrine/dbal',
  849.                 'https://github.com/doctrine/dbal/issues/3837',
  850.                 'Passing an array for the first argument to QueryBuilder::groupBy is deprecated, ' .
  851.                 'pass each value as an individual variadic argument instead.'
  852.             );
  853.         }
  854.         $groupBy is_array($groupBy) ? $groupBy func_get_args();
  855.         return $this->add('groupBy'$groupByfalse);
  856.     }
  857.     /**
  858.      * Adds a grouping expression to the query.
  859.      *
  860.      * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  861.      *
  862.      * <code>
  863.      *     $qb = $conn->createQueryBuilder()
  864.      *         ->select('u.name')
  865.      *         ->from('users', 'u')
  866.      *         ->groupBy('u.lastLogin')
  867.      *         ->addGroupBy('u.createdAt');
  868.      * </code>
  869.      *
  870.      * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED.
  871.      *                                 Pass each value as an individual argument.
  872.      *
  873.      * @return $this This QueryBuilder instance.
  874.      */
  875.     public function addGroupBy($groupBy/*, string ...$groupBys*/)
  876.     {
  877.         if (empty($groupBy)) {
  878.             return $this;
  879.         }
  880.         if (is_array($groupBy)) {
  881.             Deprecation::trigger(
  882.                 'doctrine/dbal',
  883.                 'https://github.com/doctrine/dbal/issues/3837',
  884.                 'Passing an array for the first argument to QueryBuilder::addGroupBy is deprecated, ' .
  885.                 'pass each value as an individual variadic argument instead.'
  886.             );
  887.         }
  888.         $groupBy is_array($groupBy) ? $groupBy func_get_args();
  889.         return $this->add('groupBy'$groupBytrue);
  890.     }
  891.     /**
  892.      * Sets a value for a column in an insert query.
  893.      *
  894.      * <code>
  895.      *     $qb = $conn->createQueryBuilder()
  896.      *         ->insert('users')
  897.      *         ->values(
  898.      *             array(
  899.      *                 'name' => '?'
  900.      *             )
  901.      *         )
  902.      *         ->setValue('password', '?');
  903.      * </code>
  904.      *
  905.      * @param string $column The column into which the value should be inserted.
  906.      * @param string $value  The value that should be inserted into the column.
  907.      *
  908.      * @return $this This QueryBuilder instance.
  909.      */
  910.     public function setValue($column$value)
  911.     {
  912.         $this->sqlParts['values'][$column] = $value;
  913.         return $this;
  914.     }
  915.     /**
  916.      * Specifies values for an insert query indexed by column names.
  917.      * Replaces any previous values, if any.
  918.      *
  919.      * <code>
  920.      *     $qb = $conn->createQueryBuilder()
  921.      *         ->insert('users')
  922.      *         ->values(
  923.      *             array(
  924.      *                 'name' => '?',
  925.      *                 'password' => '?'
  926.      *             )
  927.      *         );
  928.      * </code>
  929.      *
  930.      * @param mixed[] $values The values to specify for the insert query indexed by column names.
  931.      *
  932.      * @return $this This QueryBuilder instance.
  933.      */
  934.     public function values(array $values)
  935.     {
  936.         return $this->add('values'$values);
  937.     }
  938.     /**
  939.      * Specifies a restriction over the groups of the query.
  940.      * Replaces any previous having restrictions, if any.
  941.      *
  942.      * @param mixed $having The restriction over the groups.
  943.      *
  944.      * @return $this This QueryBuilder instance.
  945.      */
  946.     public function having($having)
  947.     {
  948.         if (! (func_num_args() === && $having instanceof CompositeExpression)) {
  949.             $having CompositeExpression::and(...func_get_args());
  950.         }
  951.         return $this->add('having'$having);
  952.     }
  953.     /**
  954.      * Adds a restriction over the groups of the query, forming a logical
  955.      * conjunction with any existing having restrictions.
  956.      *
  957.      * @param mixed $having The restriction to append.
  958.      *
  959.      * @return $this This QueryBuilder instance.
  960.      */
  961.     public function andHaving($having)
  962.     {
  963.         $args   func_get_args();
  964.         $args   array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  965.         $having $this->getQueryPart('having');
  966.         if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
  967.             $having $having->with(...$args);
  968.         } else {
  969.             array_unshift($args$having);
  970.             $having CompositeExpression::and(...$args);
  971.         }
  972.         return $this->add('having'$having);
  973.     }
  974.     /**
  975.      * Adds a restriction over the groups of the query, forming a logical
  976.      * disjunction with any existing having restrictions.
  977.      *
  978.      * @param mixed $having The restriction to add.
  979.      *
  980.      * @return $this This QueryBuilder instance.
  981.      */
  982.     public function orHaving($having)
  983.     {
  984.         $args   func_get_args();
  985.         $args   array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  986.         $having $this->getQueryPart('having');
  987.         if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
  988.             $having $having->with(...$args);
  989.         } else {
  990.             array_unshift($args$having);
  991.             $having CompositeExpression::or(...$args);
  992.         }
  993.         return $this->add('having'$having);
  994.     }
  995.     /**
  996.      * Specifies an ordering for the query results.
  997.      * Replaces any previously specified orderings, if any.
  998.      *
  999.      * @param string $sort  The ordering expression.
  1000.      * @param string $order The ordering direction.
  1001.      *
  1002.      * @return $this This QueryBuilder instance.
  1003.      */
  1004.     public function orderBy($sort$order null)
  1005.     {
  1006.         return $this->add('orderBy'$sort ' ' . (! $order 'ASC' $order), false);
  1007.     }
  1008.     /**
  1009.      * Adds an ordering to the query results.
  1010.      *
  1011.      * @param string $sort  The ordering expression.
  1012.      * @param string $order The ordering direction.
  1013.      *
  1014.      * @return $this This QueryBuilder instance.
  1015.      */
  1016.     public function addOrderBy($sort$order null)
  1017.     {
  1018.         return $this->add('orderBy'$sort ' ' . (! $order 'ASC' $order), true);
  1019.     }
  1020.     /**
  1021.      * Gets a query part by its name.
  1022.      *
  1023.      * @param string $queryPartName
  1024.      *
  1025.      * @return mixed
  1026.      */
  1027.     public function getQueryPart($queryPartName)
  1028.     {
  1029.         return $this->sqlParts[$queryPartName];
  1030.     }
  1031.     /**
  1032.      * Gets all query parts.
  1033.      *
  1034.      * @return mixed[]
  1035.      */
  1036.     public function getQueryParts()
  1037.     {
  1038.         return $this->sqlParts;
  1039.     }
  1040.     /**
  1041.      * Resets SQL parts.
  1042.      *
  1043.      * @param string[]|null $queryPartNames
  1044.      *
  1045.      * @return $this This QueryBuilder instance.
  1046.      */
  1047.     public function resetQueryParts($queryPartNames null)
  1048.     {
  1049.         if ($queryPartNames === null) {
  1050.             $queryPartNames array_keys($this->sqlParts);
  1051.         }
  1052.         foreach ($queryPartNames as $queryPartName) {
  1053.             $this->resetQueryPart($queryPartName);
  1054.         }
  1055.         return $this;
  1056.     }
  1057.     /**
  1058.      * Resets a single SQL part.
  1059.      *
  1060.      * @param string $queryPartName
  1061.      *
  1062.      * @return $this This QueryBuilder instance.
  1063.      */
  1064.     public function resetQueryPart($queryPartName)
  1065.     {
  1066.         $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName];
  1067.         $this->state self::STATE_DIRTY;
  1068.         return $this;
  1069.     }
  1070.     /**
  1071.      * @return string
  1072.      *
  1073.      * @throws QueryException
  1074.      */
  1075.     private function getSQLForSelect()
  1076.     {
  1077.         $query 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' '') .
  1078.                   implode(', '$this->sqlParts['select']);
  1079.         $query .= ($this->sqlParts['from'] ? ' FROM ' implode(', '$this->getFromClauses()) : '')
  1080.             . ($this->sqlParts['where'] !== null ' WHERE ' . ((string) $this->sqlParts['where']) : '')
  1081.             . ($this->sqlParts['groupBy'] ? ' GROUP BY ' implode(', '$this->sqlParts['groupBy']) : '')
  1082.             . ($this->sqlParts['having'] !== null ' HAVING ' . ((string) $this->sqlParts['having']) : '')
  1083.             . ($this->sqlParts['orderBy'] ? ' ORDER BY ' implode(', '$this->sqlParts['orderBy']) : '');
  1084.         if ($this->isLimitQuery()) {
  1085.             return $this->connection->getDatabasePlatform()->modifyLimitQuery(
  1086.                 $query,
  1087.                 $this->maxResults,
  1088.                 $this->firstResult
  1089.             );
  1090.         }
  1091.         return $query;
  1092.     }
  1093.     /**
  1094.      * @return string[]
  1095.      */
  1096.     private function getFromClauses()
  1097.     {
  1098.         $fromClauses  = [];
  1099.         $knownAliases = [];
  1100.         // Loop through all FROM clauses
  1101.         foreach ($this->sqlParts['from'] as $from) {
  1102.             if ($from['alias'] === null) {
  1103.                 $tableSql       $from['table'];
  1104.                 $tableReference $from['table'];
  1105.             } else {
  1106.                 $tableSql       $from['table'] . ' ' $from['alias'];
  1107.                 $tableReference $from['alias'];
  1108.             }
  1109.             $knownAliases[$tableReference] = true;
  1110.             $fromClauses[$tableReference] = $tableSql $this->getSQLForJoins($tableReference$knownAliases);
  1111.         }
  1112.         $this->verifyAllAliasesAreKnown($knownAliases);
  1113.         return $fromClauses;
  1114.     }
  1115.     /**
  1116.      * @param array<string,true> $knownAliases
  1117.      *
  1118.      * @throws QueryException
  1119.      */
  1120.     private function verifyAllAliasesAreKnown(array $knownAliases): void
  1121.     {
  1122.         foreach ($this->sqlParts['join'] as $fromAlias => $joins) {
  1123.             if (! isset($knownAliases[$fromAlias])) {
  1124.                 throw QueryException::unknownAlias($fromAliasarray_keys($knownAliases));
  1125.             }
  1126.         }
  1127.     }
  1128.     /**
  1129.      * @return bool
  1130.      */
  1131.     private function isLimitQuery()
  1132.     {
  1133.         return $this->maxResults !== null || $this->firstResult !== 0;
  1134.     }
  1135.     /**
  1136.      * Converts this instance into an INSERT string in SQL.
  1137.      *
  1138.      * @return string
  1139.      */
  1140.     private function getSQLForInsert()
  1141.     {
  1142.         return 'INSERT INTO ' $this->sqlParts['from']['table'] .
  1143.         ' (' implode(', 'array_keys($this->sqlParts['values'])) . ')' .
  1144.         ' VALUES(' implode(', '$this->sqlParts['values']) . ')';
  1145.     }
  1146.     /**
  1147.      * Converts this instance into an UPDATE string in SQL.
  1148.      *
  1149.      * @return string
  1150.      */
  1151.     private function getSQLForUpdate()
  1152.     {
  1153.         $table $this->sqlParts['from']['table']
  1154.             . ($this->sqlParts['from']['alias'] ? ' ' $this->sqlParts['from']['alias'] : '');
  1155.         return 'UPDATE ' $table
  1156.             ' SET ' implode(', '$this->sqlParts['set'])
  1157.             . ($this->sqlParts['where'] !== null ' WHERE ' . ((string) $this->sqlParts['where']) : '');
  1158.     }
  1159.     /**
  1160.      * Converts this instance into a DELETE string in SQL.
  1161.      *
  1162.      * @return string
  1163.      */
  1164.     private function getSQLForDelete()
  1165.     {
  1166.         $table $this->sqlParts['from']['table']
  1167.             . ($this->sqlParts['from']['alias'] ? ' ' $this->sqlParts['from']['alias'] : '');
  1168.         return 'DELETE FROM ' $table
  1169.             . ($this->sqlParts['where'] !== null ' WHERE ' . ((string) $this->sqlParts['where']) : '');
  1170.     }
  1171.     /**
  1172.      * Gets a string representation of this QueryBuilder which corresponds to
  1173.      * the final SQL query being constructed.
  1174.      *
  1175.      * @return string The string representation of this QueryBuilder.
  1176.      */
  1177.     public function __toString()
  1178.     {
  1179.         return $this->getSQL();
  1180.     }
  1181.     /**
  1182.      * Creates a new named parameter and bind the value $value to it.
  1183.      *
  1184.      * This method provides a shortcut for PDOStatement::bindValue
  1185.      * when using prepared statements.
  1186.      *
  1187.      * The parameter $value specifies the value that you want to bind. If
  1188.      * $placeholder is not provided bindValue() will automatically create a
  1189.      * placeholder for you. An automatic placeholder will be of the name
  1190.      * ':dcValue1', ':dcValue2' etc.
  1191.      *
  1192.      * For more information see {@link http://php.net/pdostatement-bindparam}
  1193.      *
  1194.      * Example:
  1195.      * <code>
  1196.      * $value = 2;
  1197.      * $q->eq( 'id', $q->bindValue( $value ) );
  1198.      * $stmt = $q->executeQuery(); // executed with 'id = 2'
  1199.      * </code>
  1200.      *
  1201.      * @link http://www.zetacomponents.org
  1202.      *
  1203.      * @param mixed                $value
  1204.      * @param int|string|Type|null $type
  1205.      * @param string               $placeHolder The name to bind with. The string must start with a colon ':'.
  1206.      *
  1207.      * @return string the placeholder name used.
  1208.      */
  1209.     public function createNamedParameter($value$type ParameterType::STRING$placeHolder null)
  1210.     {
  1211.         if ($placeHolder === null) {
  1212.             $this->boundCounter++;
  1213.             $placeHolder ':dcValue' $this->boundCounter;
  1214.         }
  1215.         $this->setParameter(substr($placeHolder1), $value$type);
  1216.         return $placeHolder;
  1217.     }
  1218.     /**
  1219.      * Creates a new positional parameter and bind the given value to it.
  1220.      *
  1221.      * Attention: If you are using positional parameters with the query builder you have
  1222.      * to be very careful to bind all parameters in the order they appear in the SQL
  1223.      * statement , otherwise they get bound in the wrong order which can lead to serious
  1224.      * bugs in your code.
  1225.      *
  1226.      * Example:
  1227.      * <code>
  1228.      *  $qb = $conn->createQueryBuilder();
  1229.      *  $qb->select('u.*')
  1230.      *     ->from('users', 'u')
  1231.      *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', ParameterType::STRING))
  1232.      *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', ParameterType::STRING))
  1233.      * </code>
  1234.      *
  1235.      * @param mixed                $value
  1236.      * @param int|string|Type|null $type
  1237.      *
  1238.      * @return string
  1239.      */
  1240.     public function createPositionalParameter($value$type ParameterType::STRING)
  1241.     {
  1242.         $this->boundCounter++;
  1243.         $this->setParameter($this->boundCounter$value$type);
  1244.         return '?';
  1245.     }
  1246.     /**
  1247.      * @param string             $fromAlias
  1248.      * @param array<string,true> $knownAliases
  1249.      *
  1250.      * @return string
  1251.      *
  1252.      * @throws QueryException
  1253.      */
  1254.     private function getSQLForJoins($fromAlias, array &$knownAliases)
  1255.     {
  1256.         $sql '';
  1257.         if (isset($this->sqlParts['join'][$fromAlias])) {
  1258.             foreach ($this->sqlParts['join'][$fromAlias] as $join) {
  1259.                 if (array_key_exists($join['joinAlias'], $knownAliases)) {
  1260.                     throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
  1261.                 }
  1262.                 $sql .= ' ' strtoupper($join['joinType'])
  1263.                     . ' JOIN ' $join['joinTable'] . ' ' $join['joinAlias'];
  1264.                 if ($join['joinCondition'] !== null) {
  1265.                     $sql .= ' ON ' $join['joinCondition'];
  1266.                 }
  1267.                 $knownAliases[$join['joinAlias']] = true;
  1268.             }
  1269.             foreach ($this->sqlParts['join'][$fromAlias] as $join) {
  1270.                 $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases);
  1271.             }
  1272.         }
  1273.         return $sql;
  1274.     }
  1275.     /**
  1276.      * Deep clone of all expression objects in the SQL parts.
  1277.      *
  1278.      * @return void
  1279.      */
  1280.     public function __clone()
  1281.     {
  1282.         foreach ($this->sqlParts as $part => $elements) {
  1283.             if (is_array($this->sqlParts[$part])) {
  1284.                 foreach ($this->sqlParts[$part] as $idx => $element) {
  1285.                     if (! is_object($element)) {
  1286.                         continue;
  1287.                     }
  1288.                     $this->sqlParts[$part][$idx] = clone $element;
  1289.                 }
  1290.             } elseif (is_object($elements)) {
  1291.                 $this->sqlParts[$part] = clone $elements;
  1292.             }
  1293.         }
  1294.         foreach ($this->params as $name => $param) {
  1295.             if (! is_object($param)) {
  1296.                 continue;
  1297.             }
  1298.             $this->params[$name] = clone $param;
  1299.         }
  1300.     }
  1301. }