init
This commit is contained in:
@@ -0,0 +1,835 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.5.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Query;
|
||||
|
||||
use ArrayIterator;
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Database\Connection;
|
||||
use Cake\Database\Expression\IdentifierExpression;
|
||||
use Cake\Database\Expression\WindowExpression;
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\StatementInterface;
|
||||
use Cake\Database\TypeMap;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* This class is used to generate SELECT queries for the relational database.
|
||||
*
|
||||
* @template T of mixed
|
||||
* @implements \IteratorAggregate<T>
|
||||
*/
|
||||
class SelectQuery extends Query implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Type of this query.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $_type = self::TYPE_SELECT;
|
||||
|
||||
/**
|
||||
* List of SQL parts that will be used to build this query.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected array $_parts = [
|
||||
'comment' => null,
|
||||
'with' => [],
|
||||
'select' => [],
|
||||
'optimizerHint' => [],
|
||||
'modifier' => [],
|
||||
'distinct' => false,
|
||||
'from' => [],
|
||||
'join' => [],
|
||||
'where' => null,
|
||||
'group' => [],
|
||||
'having' => null,
|
||||
'window' => [],
|
||||
'order' => null,
|
||||
'limit' => null,
|
||||
'offset' => null,
|
||||
'union' => [],
|
||||
'epilog' => null,
|
||||
'intersect' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of callbacks to be called to alter each row from resulting
|
||||
* statement upon retrieval. Each one of the callback function will receive
|
||||
* the row array as first argument.
|
||||
*
|
||||
* @var array<\Closure>
|
||||
*/
|
||||
protected array $_resultDecorators = [];
|
||||
|
||||
/**
|
||||
* Result set from executed SELECT query.
|
||||
*
|
||||
* @var iterable|null
|
||||
*/
|
||||
protected ?iterable $_results = null;
|
||||
|
||||
/**
|
||||
* Boolean for tracking whether buffered results
|
||||
* are enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $bufferedResults = true;
|
||||
|
||||
/**
|
||||
* The Type map for fields in the select clause
|
||||
*
|
||||
* @var \Cake\Database\TypeMap|null
|
||||
*/
|
||||
protected ?TypeMap $_selectTypeMap = null;
|
||||
|
||||
/**
|
||||
* Tracking flag to disable casting
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $typeCastEnabled = true;
|
||||
|
||||
/**
|
||||
* Executes query and returns set of decorated results.
|
||||
*
|
||||
* The results are cached until the query is modified and marked dirty.
|
||||
*
|
||||
* @return iterable
|
||||
* @throws \Cake\Core\Exception\CakeException When query is not a SELECT query.
|
||||
*/
|
||||
public function all(): iterable
|
||||
{
|
||||
if ($this->_results === null || $this->_dirty) {
|
||||
$this->_results = $this->execute()->fetchAll(StatementInterface::FETCH_TYPE_ASSOC);
|
||||
}
|
||||
|
||||
return $this->_results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new fields to be returned by a `SELECT` statement when this query is
|
||||
* executed. Fields can be passed as an array of strings, array of expression
|
||||
* objects, a single expression or a single string.
|
||||
*
|
||||
* If an array is passed, keys will be used to alias fields using the value as the
|
||||
* real field to be aliased. It is possible to alias strings, Expression objects or
|
||||
* even other Query objects.
|
||||
*
|
||||
* If a callback is passed, the returning array of the function will
|
||||
* be used as the list of fields.
|
||||
*
|
||||
* By default this function will append any passed argument to the list of fields
|
||||
* to be selected, unless the second argument is set to true.
|
||||
*
|
||||
* ### Examples:
|
||||
*
|
||||
* ```
|
||||
* $query->select(['id', 'title']); // Produces SELECT id, title
|
||||
* $query->select(['author' => 'author_id']); // Appends author: SELECT id, title, author_id as author
|
||||
* $query->select('id', true); // Resets the list: SELECT id
|
||||
* $query->select(['total' => $countQuery]); // SELECT id, (SELECT ...) AS total
|
||||
* $query->select(function ($query) {
|
||||
* return ['article_id', 'total' => $query->func()->count('*')];
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* By default no fields are selected, if you have an instance of `Cake\ORM\Query` and try to append
|
||||
* fields you should also call `Cake\ORM\Query::enableAutoFields()` to select the default fields
|
||||
* from the table.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string|float|int $fields fields to be added to the list.
|
||||
* @param bool $overwrite whether to reset fields with passed list or not
|
||||
* @return $this
|
||||
*/
|
||||
public function select(ExpressionInterface|Closure|array|string|float|int $fields = [], bool $overwrite = false)
|
||||
{
|
||||
if (!is_string($fields) && $fields instanceof Closure) {
|
||||
$fields = $fields($this);
|
||||
}
|
||||
|
||||
if (!is_array($fields)) {
|
||||
$fields = [$fields];
|
||||
}
|
||||
|
||||
if ($overwrite) {
|
||||
$this->_parts['select'] = $fields;
|
||||
} else {
|
||||
$this->_parts['select'] = array_merge($this->_parts['select'], $fields);
|
||||
}
|
||||
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a `DISTINCT` clause to the query to remove duplicates from the result set.
|
||||
* This clause can only be used for select statements.
|
||||
*
|
||||
* If you wish to filter duplicates based of those rows sharing a particular field
|
||||
* or set of fields, you may pass an array of fields to filter on. Beware that
|
||||
* this option might not be fully supported in all database systems.
|
||||
*
|
||||
* ### Examples:
|
||||
*
|
||||
* ```
|
||||
* // Filters products with the same name and city
|
||||
* $query->select(['name', 'city'])->from('products')->distinct();
|
||||
*
|
||||
* // Filters products in the same city
|
||||
* $query->distinct(['city']);
|
||||
* $query->distinct('city');
|
||||
*
|
||||
* // Filter products with the same name
|
||||
* $query->distinct(['name'], true);
|
||||
* $query->distinct('name', true);
|
||||
* ```
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string|bool $on Enable/disable distinct class
|
||||
* or list of fields to be filtered on
|
||||
* @param bool $overwrite whether to reset fields with passed list or not
|
||||
* @return $this
|
||||
*/
|
||||
public function distinct(ExpressionInterface|array|string|bool $on = [], bool $overwrite = false)
|
||||
{
|
||||
if ($on === []) {
|
||||
$on = true;
|
||||
} elseif (is_string($on)) {
|
||||
$on = [$on];
|
||||
}
|
||||
|
||||
if (is_array($on)) {
|
||||
$merge = [];
|
||||
if (is_array($this->_parts['distinct'])) {
|
||||
$merge = $this->_parts['distinct'];
|
||||
}
|
||||
$on = $overwrite ? array_values($on) : array_merge($merge, array_values($on));
|
||||
}
|
||||
|
||||
$this->_parts['distinct'] = $on;
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single or multiple fields to be used in the GROUP BY clause for this query.
|
||||
* Fields can be passed as an array of strings, array of expression
|
||||
* objects, a single expression or a single string.
|
||||
*
|
||||
* By default this function will append any passed argument to the list of fields
|
||||
* to be grouped, unless the second argument is set to true.
|
||||
*
|
||||
* ### Examples:
|
||||
*
|
||||
* ```
|
||||
* // Produces GROUP BY id, title
|
||||
* $query->groupBy(['id', 'title']);
|
||||
*
|
||||
* // Produces GROUP BY title
|
||||
* $query->groupBy('title');
|
||||
* ```
|
||||
*
|
||||
* Group fields are not suitable for use with user supplied data as they are
|
||||
* not sanitized by the query builder.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $fields fields to be added to the list
|
||||
* @param bool $overwrite whether to reset fields with passed list or not
|
||||
* @return $this
|
||||
* @deprecated 5.0.0 Use groupBy() instead now that CollectionInterface methods are no longer proxied.
|
||||
*/
|
||||
public function group(ExpressionInterface|array|string $fields, bool $overwrite = false)
|
||||
{
|
||||
deprecationWarning('5.0.0', 'SelectQuery::group() is deprecated. Use SelectQuery::groupBy() instead.');
|
||||
|
||||
return $this->groupBy($fields, $overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single or multiple fields to be used in the GROUP BY clause for this query.
|
||||
* Fields can be passed as an array of strings, array of expression
|
||||
* objects, a single expression or a single string.
|
||||
*
|
||||
* By default this function will append any passed argument to the list of fields
|
||||
* to be grouped, unless the second argument is set to true.
|
||||
*
|
||||
* ### Examples:
|
||||
*
|
||||
* ```
|
||||
* // Produces GROUP BY id, title
|
||||
* $query->groupBy(['id', 'title']);
|
||||
*
|
||||
* // Produces GROUP BY title
|
||||
* $query->groupBy('title');
|
||||
* ```
|
||||
*
|
||||
* Group fields are not suitable for use with user supplied data as they are
|
||||
* not sanitized by the query builder.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $fields fields to be added to the list
|
||||
* @param bool $overwrite whether to reset fields with passed list or not
|
||||
* @return $this
|
||||
*/
|
||||
public function groupBy(ExpressionInterface|array|string $fields, bool $overwrite = false)
|
||||
{
|
||||
if ($overwrite) {
|
||||
$this->_parts['group'] = [];
|
||||
}
|
||||
|
||||
if (!is_array($fields)) {
|
||||
$fields = [$fields];
|
||||
}
|
||||
|
||||
$this->_parts['group'] = array_merge($this->_parts['group'], array_values($fields));
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition or set of conditions to be used in the `HAVING` clause for this
|
||||
* query. This method operates in exactly the same way as the method `where()`
|
||||
* does. Please refer to its documentation for an insight on how to using each
|
||||
* parameter.
|
||||
*
|
||||
* Having fields are not suitable for use with user supplied data as they are
|
||||
* not sanitized by the query builder.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string|null $conditions The having conditions.
|
||||
* @param array<string, string> $types Associative array of type names used to bind values to query
|
||||
* @param bool $overwrite whether to reset conditions with passed list or not
|
||||
* @see \Cake\Database\Query::where()
|
||||
* @return $this
|
||||
*/
|
||||
public function having(
|
||||
ExpressionInterface|Closure|array|string|null $conditions = null,
|
||||
array $types = [],
|
||||
bool $overwrite = false,
|
||||
) {
|
||||
if ($overwrite) {
|
||||
$this->_parts['having'] = $this->expr();
|
||||
}
|
||||
$this->_conjugate('having', $conditions, 'AND', $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects any previously defined set of conditions to the provided list
|
||||
* using the AND operator in the HAVING clause. This method operates in exactly
|
||||
* the same way as the method `andWhere()` does. Please refer to its
|
||||
* documentation for an insight on how to using each parameter.
|
||||
*
|
||||
* Having fields are not suitable for use with user supplied data as they are
|
||||
* not sanitized by the query builder.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions The AND conditions for HAVING.
|
||||
* @param array<string, string> $types Associative array of type names used to bind values to query
|
||||
* @see \Cake\Database\Query::andWhere()
|
||||
* @return $this
|
||||
*/
|
||||
public function andHaving(ExpressionInterface|Closure|array|string $conditions, array $types = [])
|
||||
{
|
||||
$this->_conjugate('having', $conditions, 'AND', $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a named window expression.
|
||||
*
|
||||
* You are responsible for adding windows in the order your database requires.
|
||||
*
|
||||
* @param string $name Window name
|
||||
* @param \Cake\Database\Expression\WindowExpression|\Closure $window Window expression
|
||||
* @param bool $overwrite Clear all previous query window expressions
|
||||
* @return $this
|
||||
*/
|
||||
public function window(string $name, WindowExpression|Closure $window, bool $overwrite = false)
|
||||
{
|
||||
if ($overwrite) {
|
||||
$this->_parts['window'] = [];
|
||||
}
|
||||
|
||||
if ($window instanceof Closure) {
|
||||
$window = $window(new WindowExpression(), $this);
|
||||
if (!($window instanceof WindowExpression)) {
|
||||
throw new CakeException('You must return a `WindowExpression` from a Closure passed to `window()`.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->_parts['window'][] = ['name' => new IdentifierExpression($name), 'window' => $window];
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the page of results you want.
|
||||
*
|
||||
* This method provides an easier to use interface to set the limit + offset
|
||||
* in the record set you want as results. If empty the limit will default to
|
||||
* the existing limit clause, and if that too is empty, then `25` will be used.
|
||||
*
|
||||
* Pages must start at 1.
|
||||
*
|
||||
* @param int $num The page number you want.
|
||||
* @param int|null $limit The number of rows you want in the page. If null
|
||||
* the current limit clause will be used.
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException If page number < 1.
|
||||
*/
|
||||
public function page(int $num, ?int $limit = null)
|
||||
{
|
||||
if ($num < 1) {
|
||||
throw new InvalidArgumentException('Pages must start at 1.');
|
||||
}
|
||||
if ($limit !== null) {
|
||||
$this->limit($limit);
|
||||
}
|
||||
$limit = $this->clause('limit');
|
||||
if ($limit === null) {
|
||||
$limit = 25;
|
||||
$this->limit($limit);
|
||||
}
|
||||
$offset = ($num - 1) * $limit;
|
||||
if (PHP_INT_MAX <= $offset) {
|
||||
$offset = PHP_INT_MAX;
|
||||
}
|
||||
$this->offset((int)$offset);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a complete query to be used in conjunction with an UNION operator with
|
||||
* this query. This is used to combine the result set of this query with the one
|
||||
* that will be returned by the passed query. You can add as many queries as you
|
||||
* required by calling multiple times this method with different queries.
|
||||
*
|
||||
* By default, the UNION operator will remove duplicate rows, if you wish to include
|
||||
* every row for all queries, use unionAll().
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* ```
|
||||
* $union = (new SelectQuery($conn))->select(['id', 'title'])->from(['a' => 'articles']);
|
||||
* $query->select(['id', 'name'])->from(['d' => 'things'])->union($union);
|
||||
* ```
|
||||
*
|
||||
* Will produce:
|
||||
*
|
||||
* `SELECT id, name FROM things d UNION SELECT id, title FROM articles a`
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query full SQL query to be used in UNION operator
|
||||
* @param bool $overwrite whether to reset the list of queries to be operated or not
|
||||
* @return $this
|
||||
*/
|
||||
public function union(Query|string $query, bool $overwrite = false)
|
||||
{
|
||||
if ($overwrite) {
|
||||
$this->_parts['union'] = [];
|
||||
}
|
||||
$this->_parts['union'][] = [
|
||||
'all' => false,
|
||||
'query' => $query,
|
||||
];
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a complete query to be used in conjunction with the UNION ALL operator with
|
||||
* this query. This is used to combine the result set of this query with the one
|
||||
* that will be returned by the passed query. You can add as many queries as you
|
||||
* required by calling multiple times this method with different queries.
|
||||
*
|
||||
* Unlike UNION, UNION ALL will not remove duplicate rows.
|
||||
*
|
||||
* ```
|
||||
* $union = (new SelectQuery($conn))->select(['id', 'title'])->from(['a' => 'articles']);
|
||||
* $query->select(['id', 'name'])->from(['d' => 'things'])->unionAll($union);
|
||||
* ```
|
||||
*
|
||||
* Will produce:
|
||||
*
|
||||
* `SELECT id, name FROM things d UNION ALL SELECT id, title FROM articles a`
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query full SQL query to be used in UNION operator
|
||||
* @param bool $overwrite whether to reset the list of queries to be operated or not
|
||||
* @return $this
|
||||
*/
|
||||
public function unionAll(Query|string $query, bool $overwrite = false)
|
||||
{
|
||||
if ($overwrite) {
|
||||
$this->_parts['union'] = [];
|
||||
}
|
||||
$this->_parts['union'][] = [
|
||||
'all' => true,
|
||||
'query' => $query,
|
||||
];
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a complete query to be used in conjunction with an INTERSECT operator with
|
||||
* this query. This is used to combine the result set of this query with the one
|
||||
* that will be returned by the passed query. You can add as many queries as you
|
||||
* required by calling multiple times this method with different queries.
|
||||
*
|
||||
* By default, the INTERSECT operator will remove duplicate rows, if you wish to include
|
||||
* every row for all queries, use intersectAll().
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* ```
|
||||
* $intersect = (new SelectQuery($conn))->select(['id', 'title'])->from(['a' => 'articles']);
|
||||
* $query->select(['id', 'name'])->from(['d' => 'things'])->intersect($intersect);
|
||||
* ```
|
||||
*
|
||||
* Will produce:
|
||||
*
|
||||
* `SELECT id, name FROM things d INTERSECT SELECT id, title FROM articles a`
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query full SQL query to be used in INTERSECT operator
|
||||
* @param bool $overwrite whether to reset the list of queries to be operated or not
|
||||
* @return $this
|
||||
*/
|
||||
public function intersect(Query|string $query, bool $overwrite = false)
|
||||
{
|
||||
if ($overwrite) {
|
||||
$this->_parts['intersect'] = [];
|
||||
}
|
||||
$this->_parts['intersect'][] = [
|
||||
'all' => false,
|
||||
'query' => $query,
|
||||
];
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a complete query to be used in conjunction with the INTERSECT ALL operator with
|
||||
* this query. This is used to combine the result set of this query with the one
|
||||
* that will be returned by the passed query. You can add as many queries as you
|
||||
* required by calling multiple times this method with different queries.
|
||||
*
|
||||
* Unlike INTERSECT, INTERSECT ALL will not remove duplicate rows.
|
||||
*
|
||||
* ```
|
||||
* $intersect = (new SelectQuery($conn))->select(['id', 'title'])->from(['a' => 'articles']);
|
||||
* $query->select(['id', 'name'])->from(['d' => 'things'])->intersectAll($intersect);
|
||||
* ```
|
||||
*
|
||||
* Will produce:
|
||||
*
|
||||
* `SELECT id, name FROM things d INTERSECT ALL SELECT id, title FROM articles a`
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query full SQL query to be used in INTERSECT operator
|
||||
* @param bool $overwrite whether to reset the list of queries to be operated or not
|
||||
* @return $this
|
||||
*/
|
||||
public function intersectAll(Query|string $query, bool $overwrite = false)
|
||||
{
|
||||
if ($overwrite) {
|
||||
$this->_parts['intersect'] = [];
|
||||
}
|
||||
$this->_parts['intersect'][] = [
|
||||
'all' => true,
|
||||
'query' => $query,
|
||||
];
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes this query and returns a results iterator. This function is required
|
||||
* for implementing the IteratorAggregate interface and allows the query to be
|
||||
* iterated without having to call all() manually, thus making it look like
|
||||
* a result set instead of the query itself.
|
||||
*
|
||||
* @return \Traversable
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
if ($this->bufferedResults) {
|
||||
/** @var \Traversable|array $results */
|
||||
$results = $this->all();
|
||||
if (is_array($results)) {
|
||||
return new ArrayIterator($results);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
return $this->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback to be executed for each result that is fetched from the
|
||||
* result set, the callback function will receive as first parameter an array with
|
||||
* the raw data from the database for every row that is fetched and must return the
|
||||
* row with any possible modifications.
|
||||
*
|
||||
* Callbacks will be executed lazily, if only 3 rows are fetched for database it will
|
||||
* be called 3 times, event though there might be more rows to be fetched in the cursor.
|
||||
*
|
||||
* Callbacks are stacked in the order they are registered, if you wish to reset the stack
|
||||
* the call this function with the second parameter set to true.
|
||||
*
|
||||
* If you wish to remove all decorators from the stack, set the first parameter
|
||||
* to null and the second to true.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* $query->decorateResults(function ($row) {
|
||||
* $row['order_total'] = $row['subtotal'] + ($row['subtotal'] * $row['tax']);
|
||||
* return $row;
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param \Closure|null $callback The callback to invoke when results are fetched.
|
||||
* @param bool $overwrite Whether this should append or replace all existing decorators.
|
||||
* @return $this
|
||||
*/
|
||||
public function decorateResults(?Closure $callback, bool $overwrite = false)
|
||||
{
|
||||
$this->_dirty();
|
||||
if ($overwrite) {
|
||||
$this->_resultDecorators = [];
|
||||
}
|
||||
|
||||
if ($callback !== null) {
|
||||
$this->_resultDecorators[] = $callback;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get result decorators.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultDecorators(): array
|
||||
{
|
||||
return $this->_resultDecorators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables buffered results.
|
||||
*
|
||||
* When enabled the results returned by this query will be
|
||||
* buffered. This enables you to iterate a result set multiple times, or
|
||||
* both cache and iterate it.
|
||||
*
|
||||
* When disabled it will consume less memory as fetched results are not
|
||||
* remembered for future iterations.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enableBufferedResults()
|
||||
{
|
||||
$this->_dirty();
|
||||
$this->bufferedResults = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables buffered results.
|
||||
*
|
||||
* Disabling buffering will consume less memory as fetched results are not
|
||||
* remembered for future iterations.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableBufferedResults()
|
||||
{
|
||||
$this->_dirty();
|
||||
$this->bufferedResults = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether buffered results are enabled/disabled.
|
||||
*
|
||||
* When enabled the results returned by this query will be
|
||||
* buffered. This enables you to iterate a result set multiple times, or
|
||||
* both cache and iterate it.
|
||||
*
|
||||
* When disabled it will consume less memory as fetched results are not
|
||||
* remembered for future iterations.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isBufferedResultsEnabled(): bool
|
||||
{
|
||||
return $this->bufferedResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the TypeMap class where the types for each of the fields in the
|
||||
* select clause are stored.
|
||||
*
|
||||
* @param \Cake\Database\TypeMap|array $typeMap Creates a TypeMap if array, otherwise sets the given TypeMap.
|
||||
* @return $this
|
||||
*/
|
||||
public function setSelectTypeMap(TypeMap|array $typeMap)
|
||||
{
|
||||
$this->_selectTypeMap = is_array($typeMap) ? new TypeMap($typeMap) : $typeMap;
|
||||
$this->_dirty();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the TypeMap class where the types for each of the fields in the
|
||||
* select clause are stored.
|
||||
*
|
||||
* @return \Cake\Database\TypeMap
|
||||
*/
|
||||
public function getSelectTypeMap(): TypeMap
|
||||
{
|
||||
return $this->_selectTypeMap ??= new TypeMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables result casting.
|
||||
*
|
||||
* When disabled, the fields will be returned as received from the database
|
||||
* driver (which in most environments means they are being returned as
|
||||
* strings), which can improve performance with larger datasets.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableResultsCasting()
|
||||
{
|
||||
$this->typeCastEnabled = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables result casting.
|
||||
*
|
||||
* When enabled, the fields in the results returned by this Query will be
|
||||
* cast to their corresponding PHP data type.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enableResultsCasting()
|
||||
{
|
||||
$this->typeCastEnabled = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether result casting is enabled/disabled.
|
||||
*
|
||||
* When enabled, the fields in the results returned by this Query will be
|
||||
* casted to their corresponding PHP data type.
|
||||
*
|
||||
* When disabled, the fields will be returned as received from the database
|
||||
* driver (which in most environments means they are being returned as
|
||||
* strings), which can improve performance with larger datasets.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isResultsCastingEnabled(): bool
|
||||
{
|
||||
return $this->typeCastEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clearing iterator and cloning all expressions and value binders.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
parent::__clone();
|
||||
|
||||
$this->_results = null;
|
||||
if ($this->_selectTypeMap !== null) {
|
||||
$this->_selectTypeMap = clone $this->_selectTypeMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array that can be used to describe the internal state of this
|
||||
* object.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
$return = parent::__debugInfo();
|
||||
$return['decorators'] = count($this->_resultDecorators);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection role.
|
||||
*
|
||||
* @param string $role Connection role ('read' or 'write')
|
||||
* @return $this
|
||||
*/
|
||||
public function setConnectionRole(string $role)
|
||||
{
|
||||
assert($role === Connection::ROLE_READ || $role === Connection::ROLE_WRITE);
|
||||
$this->connectionRole = $role;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection role to read.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function useReadRole()
|
||||
{
|
||||
return $this->setConnectionRole(Connection::ROLE_READ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection role to write.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function useWriteRole()
|
||||
{
|
||||
return $this->setConnectionRole(Connection::ROLE_WRITE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user