Skip to content

Commit

Permalink
Merge c292b3c into 65c2f9f
Browse files Browse the repository at this point in the history
  • Loading branch information
othercorey committed Jun 26, 2020
2 parents 65c2f9f + c292b3c commit bbf1e0d
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/Database/Expression/AggregateExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,40 @@
*/
class AggregateExpression extends FunctionExpression implements WindowInterface
{
/**
* @var \Cake\Database\Expression\QueryExpression
*/
protected $filter;

/**
* @var \Cake\Database\Expression\WindowExpression
*/
protected $window;

/**
* Adds conditions to the FILTER clause. The conditions are the same format as
* `Query::where()`.
*
* @param string|array|\Cake\Database\ExpressionInterface|\Closure $conditions The conditions to filter on.
* @param array $types associative array of type names used to bind values to query
* @return $this
* @see \Cake\Database\Query::where()
*/
public function filter($conditions, array $types = [])
{
if ($this->filter === null) {
$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 epression.
*
Expand Down Expand Up @@ -161,6 +190,9 @@ public function excludeTies()
public function sql(ValueBinder $generator): string
{
$sql = parent::sql($generator);
if ($this->filter !== null) {
$sql .= ' FILTER (WHERE ' . $this->filter->sql($generator) . ')';
}
if ($this->window !== null) {
if ($this->window->isNamedOnly()) {
$sql .= ' OVER ' . $this->window->sql($generator);
Expand All @@ -178,7 +210,12 @@ public function sql(ValueBinder $generator): string
public function traverse(Closure $visitor)
{
parent::traverse($visitor);
if ($this->filter !== null) {
$visitor($this->filter);
$this->filter->traverse($visitor);
}
if ($this->window !== null) {
$visitor($this->window);
$this->window->traverse($visitor);
}

Expand Down Expand Up @@ -206,6 +243,9 @@ public function count(): int
public function __clone()
{
parent::__clone();
if ($this->filter !== null) {
$this->filter = clone $this->filter;
}
if ($this->window !== null) {
$this->window = clone $this->window;
}
Expand Down
51 changes: 51 additions & 0 deletions tests/TestCase/Database/Expression/AggregateExpressionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
namespace Cake\Test\TestCase\Database\Expression;

use Cake\Database\Expression\AggregateExpression;
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\Database\Expression\WindowExpression;
use Cake\Database\ValueBinder;

/**
Expand Down Expand Up @@ -45,6 +48,34 @@ public function testEmptyWindow()
);
}

/**
* Tests filter() clauses.
*
* @return void
*/
public function testFilter()
{
$f = (new AggregateExpression('MyFunction'))->filter(['this' => new IdentifierExpression('that')]);
$this->assertEqualsSql(
'MyFunction() FILTER (WHERE this = (that))',
$f->sql(new ValueBinder())
);

$f->filter(function (QueryExpression $q) {
return $q->add(['this2' => new IdentifierExpression('that2')]);
});
$this->assertEqualsSql(
'MyFunction() FILTER (WHERE (this = (that) AND this2 = (that2)))',
$f->sql(new ValueBinder())
);

$f->over();
$this->assertEqualsSql(
'MyFunction() FILTER (WHERE (this = (that) AND this2 = (that2))) OVER ()',
$f->sql(new ValueBinder())
);
}

/**
* Tests WindowInterface calls are passed to the WindowExpression
*
Expand Down Expand Up @@ -137,6 +168,26 @@ public function testWindowInterface()
);
}

/**
* Tests traversing aggregate expressions.
*
* @return void
*/
public function testTraverse()
{
$w = (new AggregateExpression('MyFunction'))
->filter(['this' => true])
->over();

$expressions = [];
$w->traverse(function ($expression) use (&$expressions) {
$expressions[] = $expression;
});

$this->assertEquals(new QueryExpression(['this' => true]), $expressions[0]);
$this->assertEquals(new WindowExpression(), $expressions[2]);
}

/**
* Tests cloning aggregate expressions
*
Expand Down
76 changes: 76 additions & 0 deletions tests/TestCase/Database/QueryTests/AggregatesQueryTests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?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 Open Group Test Suite License
* 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\Test\TestCase\Database\QueryTests;

use Cake\Database\Query;
use Cake\Datasource\ConnectionManager;
use Cake\TestSuite\TestCase;

/**
* Tests AggregateExpression queries class
*/
class AggregatesQueryTests extends TestCase
{
protected $fixtures = [
'core.Comments',
];

public $autoFixtures = false;

/**
* @var \Cake\Database\Connection
*/
protected $connection = null;

/**
* @var bool
*/
protected $skipTests = false;

public function setUp(): void
{
parent::setUp();
$this->connection = ConnectionManager::get('test');
}

public function tearDown(): void
{
parent::tearDown();
}

/**
* Tests filtering aggregate function rows.
*
* @return void
*/
public function testFilters()
{
$skip = !($this->connection->getDriver() instanceof \Cake\Database\Driver\Postgres);
if ($this->connection->getDriver() instanceof \Cake\Database\Driver\Sqlite) {
$skip = version_compare($this->connection->getDriver()->version(), '3.30.0', '<');
}
$this->skipif($skip);
$this->loadFixtures('Comments');

$query = new Query($this->connection);
$result = $query
->select(['num_rows' => $query->func()->count('*')->filter(['article_id' => 2])])
->from('comments')
->execute()
->fetchAll('assoc');
$this->assertEquals(2, $result[0]['num_rows']);
}
}

0 comments on commit bbf1e0d

Please sign in to comment.