vendor/doctrine/dbal/src/Query/QueryBuilder.php line 271

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