Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 58 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# DBAL for ReactPHP

This is a DBAL on top of ReactPHP SQL libraries and Doctrine QueryBuilder
implementation.
[![CircleCI](https://circleci.com/gh/driftphp/reactphp-dbal.svg?style=svg)](https://circleci.com/gh/driftphp/reactphp-dbal)

This is a DBAL on top of ReactPHP SQL clients and Doctrine model implementation.
You will be able to use

- Doctrine QueryBuilder model
- Doctrine Schema model
- Easy-to-use shortcuts for common operations
- and much more support is being added right now

> Attention. Only for proof of concept ATM. Do not use this library on
> production until the first stable version is tagged.
Expand Down Expand Up @@ -183,6 +190,54 @@ $connection
});
```

### Create table

You can easily create a new table with basic information. Needs the table name
and an array of fields and types. Strings are considered with length `255`.
First field position in the array will be considered as primary key. Returns a
Promise with, eventually, the connection.

```php
$connection->createTable('test', [
'id' => 'string',
'name' => 'string',
]);
```

This is a basic table creation method. To create more complex tables, you can
use Doctrine's Schema model. You can execute all Schema SQLs generated by using
this method inside Connection named `executeSchema`. You'll find more
information about this Schema model in
[Doctrine documentation](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.10/reference/schema-representation.html)

```php
$schema = new Schema();
$table = $schema->createTable('test');
$table->addColumn('id', 'string');
// ...

$connection->executeSchema($schema);
```

### Drop table

You can easily drop an existing table. Needs just the table name, and returns,
eventually, the connection.

```php
$connection->dropTable('test');
```

### Truncate table

You can easily truncate an existing table. Needs just the table name, and returns,
eventually, the connection.

```php
$connection->truncateTable('test');
```

## Tests

You can run tests by running `docker-compose up` and by doing `phpunit`.
You can run tests by running `docker-compose up` and by doing
`php vendor/bin/phpunit`.
118 changes: 118 additions & 0 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@

use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Schema\Schema;
use Drift\DBAL\Driver\Driver;
use Drift\DBAL\Mock\MockedDBALConnection;
use Drift\DBAL\Mock\MockedDriver;
use React\Promise\PromiseInterface;
use function React\Promise\map;

/**
* Class Connection.
Expand Down Expand Up @@ -154,6 +158,40 @@ public function queryBySQL(string $sql, array $parameters = []): PromiseInterfac
->query($sql, $parameters);
}

/**
* Execute, sequentially, an array of sqls
*
* @param string[] $sqls
*
* @return PromiseInterface<Connection>
*/
public function executeSQLs(array $sqls): PromiseInterface
{
return
map($sqls, function(string $sql) {
return $this->queryBySQL($sql);
})
->then(function() {
return $this;
});
}

/**
* Execute an schema
*
* @param Schema $schema
*
* @return PromiseInterface<Connection>
*/
public function executeSchema(Schema $schema): PromiseInterface
{
return $this
->executeSQLs($schema->toSql($this->platform))
->then(function() {
return $this;
});
}

/**
* Shortcuts.
*/
Expand Down Expand Up @@ -309,6 +347,86 @@ public function upsert(
});
}

/**
* Table related shortcuts
*/

/**
* Easy shortcut for creating tables. Fields is just a simple key value,
* being the key the name of the field, and the value the type. By default,
* Varchar types have length 255.
*
* First field is considered as primary key.
*
* @param string $name
* @param array $fields
*
* @return PromiseInterface<Connection>
*
* @throws InvalidArgumentException
* @throws TableExistsException
*/
public function createTable(
string $name,
array $fields
) : PromiseInterface
{
if (empty($fields)) {
throw InvalidArgumentException::fromEmptyFieldsArray();
}

$schema = new Schema();
$table = $schema->createTable($name);
foreach ($fields as $field => $type) {
$extra = [];
if ($type = 'string') {
$extra = ['length' => 255];
}

$table->addColumn($field, $type, $extra);
}

$table->setPrimaryKey([array_key_first($fields)]);

return $this->executeSchema($schema);
}

/**
* @param string $name
*
* @return PromiseInterface<Connection>
*
* @throws TableNotFoundException
*/
public function dropTable(string $name) : PromiseInterface
{
return $this
->queryBySQL("DROP TABLE $name")
->then(function() {
return $this;
});
}

/**
* @param string $name
*
* @return PromiseInterface<Connection>
*
* @throws TableNotFoundException
*/
public function truncateTable(string $name) : PromiseInterface
{
$truncateTableQuery = $this
->platform
->getTruncateTableSQL($name);

return $this
->queryBySQL($truncateTableQuery)
->then(function() {
return $this;
});
}

/**
* Get result by where clause.
*
Expand Down
6 changes: 2 additions & 4 deletions src/Driver/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
namespace Drift\DBAL\Driver;

use Drift\DBAL\Credentials;
use Drift\DBAL\Result;
use React\Promise\PromiseInterface;

/**
Expand All @@ -27,9 +28,6 @@ interface Driver
* Attempts to create a connection with the database.
*
* @param Credentials $credentials
* @param array $options
*
* @return PromiseInterface
*/
public function connect(Credentials $credentials);

Expand All @@ -39,7 +37,7 @@ public function connect(Credentials $credentials);
* @param string $sql
* @param array $parameters
*
* @return PromiseInterface
* @return PromiseInterface<Result>
*/
public function query(
string $sql,
Expand Down
36 changes: 28 additions & 8 deletions tests/ConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

namespace Drift\DBAL\Tests;

use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Drift\DBAL\Connection;
Expand Down Expand Up @@ -46,26 +47,45 @@ abstract class ConnectionTest extends TestCase
abstract protected function getConnection(LoopInterface $loop): Connection;

/**
* Create database and table.
*
* @param Connection $connection
*
* @return PromiseInterface
*/
abstract protected function createInfrastructure(Connection $connection): PromiseInterface;
protected function createInfrastructure(Connection $connection): PromiseInterface
{
return $connection
->createTable('test', [
'id' => 'string',
'field1' => 'string',
'field2' => 'string'
])
->otherwise(function(TableExistsException $_) use ($connection) {
// Silent pass

return $connection;
})
->then(function (Connection $connection) {
return $connection->truncateTable('test');
});
}

/**
* Drop infrastructure.
*
* @param Connection $connection
*
* @return PromiseInterface
*/
abstract protected function dropInfrastructure(Connection $connection): PromiseInterface;
protected function dropInfrastructure(Connection $connection): PromiseInterface
{
return $connection
->dropTable('test')
->otherwise(function (TableNotFoundException $_) use ($connection) {
// Silent pass

return $connection;
});
}

/**
* Create database and table.
*
* @param Connection $connection
*
* @return PromiseInterface
Expand Down
40 changes: 0 additions & 40 deletions tests/MysqlConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,44 +45,4 @@ protected function getConnection(LoopInterface $loop): Connection
'test'
), $mysqlPlatform);
}

/**
* Create database and table.
*
* @param Connection $connection
*
* @return PromiseInterface
*/
protected function createInfrastructure(Connection $connection): PromiseInterface
{
return $connection
->queryBySQL('CREATE TABLE IF NOT EXISTS test (id VARCHAR(255) PRIMARY KEY, field1 VARCHAR(255), field2 VARCHAR (255))')
->then(function () use ($connection) {
return $connection
->queryBySQL('TRUNCATE TABLE test')
->then(function () use ($connection) {
return $connection;
});
});
}

/**
* Drop infrastructure.
*
* @param Connection $connection
*
* @return PromiseInterface
*/
protected function dropInfrastructure(Connection $connection): PromiseInterface
{
return $connection
->queryBySQL('DROP TABLE test')
->then(function () use ($connection) {
return $connection;
}, function (TableNotFoundException $exception) use ($connection) {
// Silent pass

return $connection;
});
}
}
Loading