vendor/shopware/core/Framework/Adapter/Doctrine/Patch/QueryBuilder.php line 249

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