This commit is contained in:
Sebastian Molenda
2026-05-12 21:10:38 +02:00
commit ab96d82fcf
2544 changed files with 721700 additions and 0 deletions
@@ -0,0 +1,265 @@
<?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.1.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Database\Expression;
use Cake\Database\ExpressionInterface;
use Cake\Database\ValueBinder;
use Closure;
use function Cake\Core\deprecationWarning;
/**
* This represents an SQL aggregate function expression in an SQL statement.
* Calls can be constructed by passing the name of the function and a list of params.
* For security reasons, all params passed are quoted by default unless
* explicitly told otherwise.
*/
class AggregateExpression extends FunctionExpression implements WindowInterface
{
/**
* @var \Cake\Database\Expression\QueryExpression|null
*/
protected ?QueryExpression $filter = null;
/**
* @var \Cake\Database\Expression\WindowExpression|null
*/
protected ?WindowExpression $window = null;
/**
* Adds conditions to the FILTER clause. The conditions are the same format as
* `Query::where()`.
*
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions The conditions to filter on.
* @param array<string, string> $types Associative array of type names used to bind values to query
* @return $this
* @see \Cake\Database\Query::where()
*/
public function filter(ExpressionInterface|Closure|array|string $conditions, array $types = [])
{
$this->filter ??= new QueryExpression();
if ($conditions instanceof Closure) {
$conditions = $conditions(new QueryExpression());
}
$this->filter->add($conditions, $types);
return $this;
}
/**
* Adds an empty `OVER()` window expression or a named window expression.
*
* @param string|null $name Window name
* @return $this
*/
public function over(?string $name = null)
{
$window = $this->getWindow();
if ($name) {
// Set name manually in case this was chained from FunctionsBuilder wrapper
$window->name($name);
}
return $this;
}
/**
* @inheritDoc
*/
public function partition(ExpressionInterface|Closure|array|string $partitions)
{
$this->getWindow()->partition($partitions);
return $this;
}
/**
* @inheritDoc
*/
public function order(ExpressionInterface|Closure|array|string $fields)
{
deprecationWarning(
'5.0.0',
'AggregateExpression::order() is deprecated. Use AggregateExpression::orderBy() instead.',
);
return $this->orderBy($fields);
}
/**
* @inheritDoc
*/
public function orderBy(ExpressionInterface|Closure|array|string $fields)
{
$this->getWindow()->orderBy($fields);
return $this;
}
/**
* @inheritDoc
*/
public function range(ExpressionInterface|string|int|null $start, ExpressionInterface|string|int|null $end = 0)
{
$this->getWindow()->range($start, $end);
return $this;
}
/**
* @inheritDoc
*/
public function rows(?int $start, ?int $end = 0)
{
$this->getWindow()->rows($start, $end);
return $this;
}
/**
* @inheritDoc
*/
public function groups(?int $start, ?int $end = 0)
{
$this->getWindow()->groups($start, $end);
return $this;
}
/**
* @inheritDoc
*/
public function frame(
string $type,
ExpressionInterface|string|int|null $startOffset,
string $startDirection,
ExpressionInterface|string|int|null $endOffset,
string $endDirection,
) {
$this->getWindow()->frame($type, $startOffset, $startDirection, $endOffset, $endDirection);
return $this;
}
/**
* @inheritDoc
*/
public function excludeCurrent()
{
$this->getWindow()->excludeCurrent();
return $this;
}
/**
* @inheritDoc
*/
public function excludeGroup()
{
$this->getWindow()->excludeGroup();
return $this;
}
/**
* @inheritDoc
*/
public function excludeTies()
{
$this->getWindow()->excludeTies();
return $this;
}
/**
* Returns or creates WindowExpression for function.
*
* @return \Cake\Database\Expression\WindowExpression
*/
protected function getWindow(): WindowExpression
{
return $this->window ??= new WindowExpression();
}
/**
* @inheritDoc
*/
public function sql(ValueBinder $binder): string
{
$sql = parent::sql($binder);
if ($this->filter !== null) {
$sql .= ' FILTER (WHERE ' . $this->filter->sql($binder) . ')';
}
if ($this->window !== null) {
if ($this->window->isNamedOnly()) {
$sql .= ' OVER ' . $this->window->sql($binder);
} else {
$sql .= ' OVER (' . $this->window->sql($binder) . ')';
}
}
return $sql;
}
/**
* @inheritDoc
*/
public function traverse(Closure $callback)
{
parent::traverse($callback);
if ($this->filter !== null) {
$callback($this->filter);
$this->filter->traverse($callback);
}
if ($this->window !== null) {
$callback($this->window);
$this->window->traverse($callback);
}
return $this;
}
/**
* @inheritDoc
*/
public function count(): int
{
$count = parent::count();
if ($this->window !== null) {
$count += 1;
}
return $count;
}
/**
* Clone this object and its subtree of expressions.
*
* @return void
*/
public function __clone()
{
parent::__clone();
if ($this->filter !== null) {
$this->filter = clone $this->filter;
}
if ($this->window !== null) {
$this->window = clone $this->window;
}
}
}