Skip to content

Commit

Permalink
Add EXISTS and NOT EXISTS to SELECT queries
Browse files Browse the repository at this point in the history
  • Loading branch information
mecha committed Jul 10, 2024
1 parent d6a619e commit fd4d5a7
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/Expression/CallbackExpr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace RebelCode\Atlas\Expression;

/** Renders using a callback. */
class CallbackExpr extends BaseExpr
{
/** @var callable():string */
private $callback;

/**
* Creates a new callback expression.
* @param callable():string $callback A function that renders the expr SQL.
*/
public function __construct(callable $callback)
{
$this->callback = $callback;
}

/** @inheritdoc */
protected function toBaseString(): string
{
return call_user_func($this->callback);
}
}
16 changes: 16 additions & 0 deletions src/Query/SelectQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
use RebelCode\Atlas\DatabaseAdapter;
use RebelCode\Atlas\DataSource;
use RebelCode\Atlas\Exception\QuerySqlException;
use RebelCode\Atlas\Expression\CallbackExpr;
use RebelCode\Atlas\Expression\ColumnTerm;
use RebelCode\Atlas\Expression\ExprInterface;
use RebelCode\Atlas\Expression\FnExpr;
use RebelCode\Atlas\Expression\Term;
use RebelCode\Atlas\Order;
use RebelCode\Atlas\Query;
Expand Down Expand Up @@ -128,6 +130,20 @@ public function getAlias(): ?string
return $this->alias;
}

public function exists(): ExprInterface
{
return new FnExpr('EXISTS', [
new CallbackExpr(fn () => $this->toSql()),
]);
}

public function notExists(): ExprInterface
{
return new FnExpr('NOT EXISTS', [
new CallbackExpr(fn () => $this->toSql()),
]);
}

/** @inheritDoc */
public function toSql(): string
{
Expand Down
26 changes: 26 additions & 0 deletions tests/Expression/CallbackExprTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace RebelCode\Atlas\Test\Expression;

use PHPUnit\Framework\TestCase;
use RebelCode\Atlas\Expression\CallbackExpr;

class CallbackExprTest extends TestCase
{
public function testRenderClosure()
{
$closure = new CallbackExpr(function () {
return 'foobar';
});

$this->assertEquals('foobar', $closure->toSql());
}

public function testRenderArrowFn()
{
$var = 'foobar';
$closure = new CallbackExpr(fn () => $var);

$this->assertEquals('foobar', $closure->toSql());
}
}
21 changes: 21 additions & 0 deletions tests/Query/SelectQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
use RebelCode\Atlas\Query\SelectQuery;
use RebelCode\Atlas\Test\Helpers\ReflectionHelper;

use function RebelCode\Atlas\col;
use function RebelCode\Atlas\table;

class SelectQueryTest extends TestCase
{
use ReflectionHelper;
Expand Down Expand Up @@ -446,4 +449,22 @@ public function testCompileSelectEverything()

$this->assertEquals($expected, $actual);
}

public function testExists()
{
$query = new SelectQuery();
$query = $query->from(table('test'))->where(col('role')->eq('admin'));
$exists = $query->exists();

$this->assertEquals("EXISTS(SELECT * FROM `test` WHERE (`role` = 'admin'))", $exists->toSql());
}

public function testNotExists()
{
$query = new SelectQuery();
$query = $query->from(table('test'))->where(col('role')->eq('admin'));
$exists = $query->notExists();

$this->assertEquals("NOT EXISTS(SELECT * FROM `test` WHERE (`role` = 'admin'))", $exists->toSql());
}
}

0 comments on commit fd4d5a7

Please sign in to comment.