diff --git a/CHANGELOG.md b/CHANGELOG.md index f0f35c3..013cbf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,4 +12,14 @@ ## main-dev (2024-04-04) -- PHPStan on max \ No newline at end of file +- PHPStan on max + +## v1.0.0 (2024-04-10) + +- QueryBuilder::fromArray() will probably be deprecated +- QueryBuilder::select() accepts SelectExpression +- QueryBuilder::from() accepts FromExpression +- QueryBuilder::createSelectException() correctly calls SelectExpression::fromString() +- Update QueryBuilderInterface +- Fix Result $rows param +- Test QueryBuilder::select() with SelectExpression diff --git a/README.md b/README.md index 8e968f0..f158624 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,6 @@ Async database abstraction layer for [ReactPHP](https://reactphp.org/). -> **Development version**: This project is currently in development. -> This is a proof-of-concept for [ReactPHP ORM](https://github.com/dmarkic/orm) that uses this DBAL. - Full example is available in [Bookstore respository](https://github.com/dmarkic/orm-bookstore-example). Bookstore example uses [blrf/dbal](https://github.com/dmarkic/dbal), [blrf/orm](https://github.com/dmarkic/orm) and [framework-x](https://github.com/reactphp-framework/framework-x) to showcase current DBAL/ORM development. diff --git a/src/Connection.php b/src/Connection.php index 8611b03..e69ca4f 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -6,6 +6,7 @@ use React\Promise\PromiseInterface; use Blrf\Dbal\ResultStream; +use Blrf\Dbal\Driver\QueryBuilder; /** * Connection interface @@ -25,7 +26,7 @@ public function connect(): PromiseInterface; * Start query builder * */ - public function query(): QueryBuilderInterface; + public function query(): QueryBuilder; /** * Execute query on connection diff --git a/src/Query/Condition.php b/src/Query/Condition.php index ffec17c..5aebb6c 100644 --- a/src/Query/Condition.php +++ b/src/Query/Condition.php @@ -17,6 +17,9 @@ class Condition implements Stringable 'is null', 'is not null' ]; + /** + * Value will be null if operator is in noValueOperators + */ public readonly ?string $value; /** * Create condition from array diff --git a/src/Query/FromExpression.php b/src/Query/FromExpression.php index 31ad105..1c8ab3b 100644 --- a/src/Query/FromExpression.php +++ b/src/Query/FromExpression.php @@ -11,7 +11,7 @@ use strlen; /** - * SELECT [expression] + * FROM [expression] */ class FromExpression extends Expression { @@ -46,6 +46,7 @@ public static function fromArray(array $data): static * * @note Currently does not support subquery as QueryBuilder * But could probably be done with `(...) AS x` match. + * Very basic regexp. */ public static function fromString(string $from): static { diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 3306056..4356d51 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -13,6 +13,7 @@ use Blrf\Dbal\Query\OrderByExpression; use Blrf\Dbal\Query\OrderByType; use Blrf\Dbal\Query\Type; +use Blrf\Dbal\Driver\QueryBuilder as DriverQueryBuilder; use TypeError; use array_map; use is_array; @@ -125,8 +126,9 @@ class QueryBuilder implements QueryBuilderInterface * offset?:int * } $data * @param mixed $arguments Arguments to QueryBuilder constructor + * @deprecated Most probably */ - public static function fromArray(array $data, mixed ...$arguments): QueryBuilder|QueryBuilderInterface + public static function fromArray(array $data, mixed ...$arguments): QueryBuilder|DriverQueryBuilder { $class = $data['class'] ?? static::class; $qb = new $class(...$arguments); @@ -250,11 +252,15 @@ public function toArray(): array * * @see self::createSelectExpression */ - public function select(string ...$exprs): static + public function select(string|SelectExpression ...$exprs): static { $this->setType(Type::SELECT); foreach ($exprs as $expr) { - $this->addSelectExpression($this->createSelectExpression($expr)); + if ($expr instanceof SelectExpression) { + $this->addSelectExpression($expr); + } else { + $this->addSelectExpression($this->createSelectExpression($expr)); + } } return $this; } @@ -303,8 +309,11 @@ public function delete(string|QueryBuilderInterface $from, string $as = null): s * * @see self::createFromExpression() */ - public function from(string|QueryBuilderInterface $from, string $as = null): static + public function from(string|QueryBuilderInterface|FromExpression $from, string $as = null): static { + if ($from instanceof FromExpression) { + return $this->addFromExpression($from); + } return $this->addFromExpression($this->createFromExpression($from, $as)); } @@ -633,7 +642,7 @@ protected function reset(): static */ protected function createSelectExpression(string $expr): SelectExpression { - return new SelectExpression($expr); + return SelectExpression::fromString($expr); } /** diff --git a/src/QueryBuilderInterface.php b/src/QueryBuilderInterface.php index 463771a..a38342e 100644 --- a/src/QueryBuilderInterface.php +++ b/src/QueryBuilderInterface.php @@ -6,6 +6,9 @@ use Blrf\Dbal\Query\Condition; use Blrf\Dbal\Query\ConditionGroup; +use Blrf\Dbal\Query\FromExpression; +use Blrf\Dbal\Query\OrderByExpression; +use Blrf\Dbal\Query\SelectExpression; interface QueryBuilderInterface { @@ -16,7 +19,7 @@ interface QueryBuilderInterface */ public static function fromArray(array $data, mixed ...$arguments): self; - public function select(string ...$exprs): static; + public function select(string|SelectExpression ...$exprs): static; public function update(string|self $from): static; @@ -24,7 +27,9 @@ public function insert(string $into): static; public function delete(string|self $from): static; - public function from(string|self $from, string $as = null): static; + public function from(string|FromExpression|self $from, string $as = null): static; + + public function addFromExpression(FromExpression $expr): static; public function value(string $column, mixed $value): static; @@ -43,6 +48,8 @@ public function orWhere(Condition|ConditionGroup|callable $condition): static; public function orderBy(string $orderBy, string $type = 'ASC'): static; + public function addOrderByExpression(OrderByExpression $expr): static; + public function limit(?int $offset = null, ?int $limit = null): static; /** diff --git a/src/Result.php b/src/Result.php index b83dbf9..f3d5c08 100644 --- a/src/Result.php +++ b/src/Result.php @@ -14,7 +14,7 @@ class Result implements Countable /** * Constructor * - * @param array $rows + * @param array> $rows Result rows */ public function __construct( public readonly array $rows = [], diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 8bf4a9b..51ba638 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -4,7 +4,9 @@ use Blrf\Dbal\QueryBuilder; use Blrf\Dbal\Query\Condition; +use Blrf\Dbal\Query\FromExpression; use Blrf\Dbal\Query\OrderByExpression; +use Blrf\Dbal\Query\SelectExpression; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(QueryBuilder::class)] @@ -55,6 +57,15 @@ public function testSelect(): void $this->assertSame(['f'], $nqb->getParameters()); } + public function testSelectWithExpression(): void + { + $qb = new QueryBuilder(); + $qb->select(new SelectExpression('a', 'b')); + $qb->from(new FromExpression('c', 'd')); + $exp = 'SELECT a AS b FROM c AS d'; + $this->assertSame($exp, $qb->getSql()); + } + public function testSelectWithAddWhereWithoutPreviousWhere(): void { $exp = 'SELECT a,b FROM c WHERE d = ? ORDER BY e ASC LIMIT 1 OFFSET 2';