Skip to content

Commit

Permalink
Prepare for v1.0.1 release with join and experimental sqlite support
Browse files Browse the repository at this point in the history
  • Loading branch information
dmarkic committed Apr 21, 2024
1 parent 0f28f97 commit eb4f080
Show file tree
Hide file tree
Showing 19 changed files with 517 additions and 24 deletions.
30 changes: 18 additions & 12 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
# Changelog

## main-dev (2024-03-17)

- QueryBuilder and Condition/ConditionGroup now have `fromArray()` and `toArray()` methods.
## v1.0.1 (2024-04-21)

## main-dev (2024-03-25)

- Offical docs page: https://blrf.net/dbal/
- Full coverage
- Condtion/ConditionBuilder support for more operators (lg, lge, like, isNull, ...)

## main-dev (2024-04-04)

- PHPStan on max
- QueryBuilder::join() added
- sqlite driver added EXPERIMENTAL (rather old version, not production ready)
- Connection::quit() method added

## v1.0.0 (2024-04-10)

Expand All @@ -23,3 +15,17 @@
- Update QueryBuilderInterface
- Fix Result $rows param
- Test QueryBuilder::select() with SelectExpression

## main-dev (2024-04-04)

- PHPStan on max

## main-dev (2024-03-25)

- Offical docs page: https://blrf.net/dbal/
- Full coverage
- Condtion/ConditionBuilder support for more operators (lg, lge, like, isNull, ...)

## main-dev (2024-03-17)

- QueryBuilder and Condition/ConditionGroup now have `fromArray()` and `toArray()` methods.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,4 @@ MIT, see [LICENSE file](LICENSE).

- Write more examples
- Write having
- Write joins
- Schema manager
4 changes: 3 additions & 1 deletion examples/select.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ function (Blrf\Dbal\Connection $db) {
$cb->eq('title')
)
)
->orderBy('publication_date', 'DESC')
->setParameters(['9789998691568', 1, 'Moby Dick'])
->limit(3);
// sql: SELECT * FROM book WHERE ((isbn13 = ? AND language_id = ?) OR title = ?) LIMIT 3
// sql: SELECT * FROM book WHERE ((isbn13 = ? AND language_id = ?) OR title = ?)
// ORDER BY publication_date DESC LIMIT 3
return $qb->execute();
}
)->then(
Expand Down
31 changes: 31 additions & 0 deletions examples/selectJoin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

require __DIR__ . '/../vendor/autoload.php';
$config = new Blrf\Dbal\Config('mysql://user:pass@localhost/bookstore');

$config->create()->then(
function (Blrf\Dbal\Connection $db) {
// start query builder
$qb = $db->query()
->select('*')
->from('customer_address', 'address')
->join('address_status', 'address.status_id = status.status_id', 'status')
->where(
fn(Blrf\Dbal\Query\ConditionBuilder $cb) => $cb->and(
$cb->eq('address.customer_id'),
$cb->like('status.address_status')
)
)
->setParameters([3, 'Inac%'])
->limit(4);
echo "sql: " . $qb->getSql() . "\n";
// sql: SELECT * FROM customer_address AS address
// INNER JOIN address_status AS status ON address.status_id = status.status_id
// WHERE (address.customer_id = ? AND status.address_status LIKE ?) LIMIT 4
return $qb->execute();
}
)->then(
function (Blrf\Dbal\Result $result) {
print_r($result->rows);
}
);
5 changes: 5 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ parameters:
# Condition::fromArray() will return Condition or ConditionGroup based on $data keys
# don't know how to tell that to phpstan
- tests/Query/ConditionTest.php
#
# SQlite driver requires additional extensions and SQLite driver
# is rather experimental. So we'll skip it for now.
#
- src/Driver/Sqlite

paths:
- src/
Expand Down
5 changes: 3 additions & 2 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class Config implements Stringable
* @var array<string,string>
*/
protected static array $driverMap = [
'mysql' => \Blrf\Dbal\Driver\Mysql\Driver::class
'mysql' => \Blrf\Dbal\Driver\Mysql\Driver::class,
'sqlite' => \Blrf\Dbal\Driver\Sqlite\Driver::class
];
/**
* Dbal driver
Expand Down Expand Up @@ -219,7 +220,7 @@ public function createDriver(): Driver
{
$driver = $this->driver;
if (!class_exists($driver)) {
$class = self::$driverMap[$driver];
$class = self::$driverMap[$driver] ?? null;
if ($class === null) {
throw new RuntimeException('No such driver: ' . $driver);
}
Expand Down
7 changes: 7 additions & 0 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ public function execute(string $sql, array $params = []): PromiseInterface;
*/
public function stream(string $sql, array $params = []): ResultStream;

/**
* Quit (soft-close) the connection
*
* @return PromiseInterface<void>
*/
public function quit(): PromiseInterface;

/**
* Get native connection
*/
Expand Down
7 changes: 7 additions & 0 deletions src/Driver/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ abstract public function execute(string $sql, array $params = []): PromiseInterf
*/
abstract public function stream(string $sql, array $params = []): ResultStream;

/**
* Quit (soft-close) the connection
*
* @return PromiseInterface<void>
*/
abstract public function quit(): PromiseInterface;

/**
* Set underlying native connection
*/
Expand Down
6 changes: 6 additions & 0 deletions src/Driver/Mysql/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,10 @@ public function stream(string $sql, array $params = []): ResultStream
// @phpstan-ignore-next-line
return new ResultStream($this->getNativeConnection()->queryStream($sql, $params));
}

public function quit(): PromiseInterface
{
// @phpstan-ignore-next-line
return $this->getNativeConnection()->quit();
}
}
70 changes: 70 additions & 0 deletions src/Driver/Sqlite/Connection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

namespace Blrf\Dbal\Driver\Sqlite;

use Blrf\Dbal\Config;
use Blrf\Dbal\Result;
use Blrf\Dbal\ResultStream;
use Blrf\Dbal\Driver\Connection as DriverConnection;
use Clue\React\SQLite\Factory;
use Clue\React\SQLite\DatabaseInterface;
use Clue\React\SQLite\Result as SqliteResult;
use React\Promise\PromiseInterface;

use function React\Promise\resolve;

/**
* React-Php Sqlite driver
*
* @see https://github.com/clue/reactphp-sqlite
*/
class Connection extends DriverConnection
{
public function connect(): PromiseInterface
{
$factory = new Factory();
return $factory->open('/' . $this->config->getDb(), SQLITE3_OPEN_READWRITE)->then(
function (DatabaseInterface $db) {
return $this->setNativeConnection($db);
}
);
}

public function query(): QueryBuilder
{
return new QueryBuilder($this);
}

/**
* Execute sql query
*
* @param array<int, string> $params
* @return PromiseInterface<Result>
*/
public function execute(string $sql, array $params = []): PromiseInterface
{
// @phpstan-ignore-next-line
return $this->getNativeConnection()->query($sql, $params)->then(
function (SqliteResult $res) {
return new Result(
$res->rows ?? [],
$res->insertId,
$res->changed ?? 0,
0
);
}
);
}

public function stream(string $sql, array $params = []): ResultStream
{
throw new \Exception('Stream not yet supported on sqlite');
}

public function quit(): PromiseInterface
{
return $this->getNativeConnection()->quit();
}
}
26 changes: 26 additions & 0 deletions src/Driver/Sqlite/Driver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Blrf\Dbal\Driver\Sqlite;

use Blrf\Dbal\Config;
use Blrf\Dbal\Driver as DriverInterface;
use Blrf\Dbal\Connection as ConnectionInterface;
use React\Promise\PromiseInterface;
use SensitiveParameter;

class Driver implements DriverInterface
{
/**
* Connect
*
* @return PromiseInterface<ConnectionInterface>
*/
public function connect(
#[SensitiveParameter]
Config $config
): PromiseInterface {
return (new Connection($config))->connect();
}
}
28 changes: 28 additions & 0 deletions src/Driver/Sqlite/QueryBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Blrf\Dbal\Driver\Sqlite;

use Blrf\Dbal\Result;
use Blrf\Dbal\ResultStream;
use Blrf\Dbal\Driver\QueryBuilder as DriverQueryBuilder;
use React\Promise\PromiseInterface;

class QueryBuilder extends DriverQueryBuilder
{
/**
* Execute query
*
* @return PromiseInterface<Result>
*/
public function execute(): PromiseInterface
{
return $this->connection->execute($this->getSql(), $this->getParameters());
}

public function stream(): ResultStream
{
return $this->connection->stream($this->getSql(), $this->getParameters());
}
}
79 changes: 79 additions & 0 deletions src/Query/JoinExpression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace Blrf\Dbal\Query;

use ValueError;

/**
* @phpstan-type JoinFromArray array{
* table: string,
* on: string,
* alias?: string|null,
* type?: string|JoinType
* }
*/
class JoinExpression extends Expression
{
public readonly JoinType $type;

/**
* @param JoinFromArray $data
*/
public static function fromArray(array $data): static
{
$table = $data['table'] ?? '';
$on = $data['on'] ?? '';
$alias = $data['alias'] ?? null;
$type = $data['type'] ?? JoinType::INNER;
return new static($type, $table, $on, $alias);
}

public static function fromString(string $join): static
{
throw new \Exception('Not implemented');
}

final public function __construct(
JoinType|string $type,
public readonly string $table,
public readonly string $on,
public readonly ?string $alias = null
) {
if (is_string($type)) {
$type = empty($type) ? JoinType::INNER : JoinType::from(strtoupper($type));
}
if (strlen($table) == 0) {
throw new ValueError('Join expression table cannot be empty');
}
if (strlen($on) == 0) {
throw new ValueError('Join expression ON cannot be empty');
}
$this->type = $type;
}

public function __toString(): string
{
return $this->type->value . ' JOIN ' . $this->table .
($this->alias === null ? '' : ' AS ' . $this->alias) .
' ON ' . $this->on;
}

/** @return array{
* table: string,
* on: string,
* alias: string|null,
* type: string
* }
*/
public function toArray(): array
{
return [
'type' => $this->type->value,
'table' => $this->table,
'on' => $this->on,
'alias' => $this->alias
];
}
}
11 changes: 11 additions & 0 deletions src/Query/JoinType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Blrf\Dbal\Query;

enum JoinType: string
{
case INNER = 'INNER';
case LEFT = 'LEFT';
case RIGHT = 'RIGHT';
case FULL = 'FULL';
}
2 changes: 1 addition & 1 deletion src/Query/OrderByExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ final public function __construct(
throw new ValueError('Expression cannot be empty');
}
if (is_string($type)) {
$type = empty($type) ? OrderByType::ASC : OrderByType::from($type);
$type = empty($type) ? OrderByType::ASC : OrderByType::from(strtoupper($type));
}
$this->type = $type;
}
Expand Down
Loading

0 comments on commit eb4f080

Please sign in to comment.