Skip to content

Commit

Permalink
Implement having null (#37228)
Browse files Browse the repository at this point in the history
  • Loading branch information
amir9480 authored May 3, 2021
1 parent 7a5672a commit accf9db
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/Illuminate/Database/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,59 @@ public function orHaving($column, $operator = null, $value = null)
return $this->having($column, $operator, $value, 'or');
}

/**
* Add a "having null" clause to the query.
*
* @param string|array $columns
* @param string $boolean
* @param bool $not
* @return $this
*/
public function havingNull($columns, $boolean = 'and', $not = false)
{
$type = $not ? 'NotNull' : 'Null';

foreach (Arr::wrap($columns) as $column) {
$this->havings[] = compact('type', 'column', 'boolean');
}

return $this;
}

/**
* Add an "or having null" clause to the query.
*
* @param string $column
* @return $this
*/
public function orHavingNull($column)
{
return $this->havingNull($column, 'or');
}

/**
* Add a "having not null" clause to the query.
*
* @param string|array $columns
* @param string $boolean
* @return $this
*/
public function havingNotNull($columns, $boolean = 'and')
{
return $this->havingNull($columns, $boolean, true);
}

/**
* Add an "or having not null" clause to the query.
*
* @param string $column
* @return $this
*/
public function orHavingNotNull($column)
{
return $this->havingNotNull($column, 'or');
}

/**
* Add a "having between " clause to the query.
*
Expand Down
30 changes: 30 additions & 0 deletions src/Illuminate/Database/Query/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,10 @@ protected function compileHaving(array $having)
return $having['boolean'].' '.$having['sql'];
} elseif ($having['type'] === 'between') {
return $this->compileHavingBetween($having);
} elseif ($having['type'] === 'Null') {
return $this->compileHavingNull($having);
} elseif ($having['type'] === 'NotNull') {
return $this->compileHavingNotNull($having);
}

return $this->compileBasicHaving($having);
Expand Down Expand Up @@ -708,6 +712,32 @@ protected function compileHavingBetween($having)
return $having['boolean'].' '.$column.' '.$between.' '.$min.' and '.$max;
}

/**
* Compile a having null clause.
*
* @param array $having
* @return string
*/
protected function compileHavingNull($having)
{
$column = $this->wrap($having['column']);

return $having['boolean'].' '.$column.' is null';
}

/**
* Compile a having not null clause.
*
* @param array $having
* @return string
*/
protected function compileHavingNotNull($having)
{
$column = $this->wrap($having['column']);

return $having['boolean'].' '.$column.' is not null';
}

/**
* Compile the "order by" portions of the query.
*
Expand Down
70 changes: 70 additions & 0 deletions tests/Database/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,76 @@ public function testHavingBetweens()
$this->assertEquals([0 => 1, 1 => 2], $builder->getBindings());
}

public function testHavingNull()
{
$builder = $this->getBuilder();
$builder->select('*')->from('users')->havingNull('email');
$this->assertSame('select * from "users" having "email" is null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('*')->from('users')
->havingNull('email')
->havingNull('phone');
$this->assertSame('select * from "users" having "email" is null and "phone" is null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('*')->from('users')
->orHavingNull('email')
->orHavingNull('phone');
$this->assertSame('select * from "users" having "email" is null or "phone" is null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('*')->from('users')->groupBy('email')->havingNull('email');
$this->assertSame('select * from "users" group by "email" having "email" is null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('email as foo_email')->from('users')->havingNull('foo_email');
$this->assertSame('select "email" as "foo_email" from "users" having "foo_email" is null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNull('total');
$this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNull('total');
$this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is null', $builder->toSql());
}

public function testHavingNotNull()
{
$builder = $this->getBuilder();
$builder->select('*')->from('users')->havingNotNull('email');
$this->assertSame('select * from "users" having "email" is not null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('*')->from('users')
->havingNotNull('email')
->havingNotNull('phone');
$this->assertSame('select * from "users" having "email" is not null and "phone" is not null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('*')->from('users')
->orHavingNotNull('email')
->orHavingNotNull('phone');
$this->assertSame('select * from "users" having "email" is not null or "phone" is not null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('*')->from('users')->groupBy('email')->havingNotNull('email');
$this->assertSame('select * from "users" group by "email" having "email" is not null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select('email as foo_email')->from('users')->havingNotNull('foo_email');
$this->assertSame('select "email" as "foo_email" from "users" having "foo_email" is not null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNotNull('total');
$this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is not null', $builder->toSql());

$builder = $this->getBuilder();
$builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNotNull('total');
$this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is not null', $builder->toSql());
}

public function testHavingShortcut()
{
$builder = $this->getBuilder();
Expand Down

0 comments on commit accf9db

Please sign in to comment.