diff --git a/README.md b/README.md index 5571a47..7391705 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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`. diff --git a/src/Connection.php b/src/Connection.php index 228d4ad..fb64774 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -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. @@ -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 + */ + 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 + */ + public function executeSchema(Schema $schema): PromiseInterface + { + return $this + ->executeSQLs($schema->toSql($this->platform)) + ->then(function() { + return $this; + }); + } + /** * Shortcuts. */ @@ -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 + * + * @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 + * + * @throws TableNotFoundException + */ + public function dropTable(string $name) : PromiseInterface + { + return $this + ->queryBySQL("DROP TABLE $name") + ->then(function() { + return $this; + }); + } + + /** + * @param string $name + * + * @return PromiseInterface + * + * @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. * diff --git a/src/Driver/Driver.php b/src/Driver/Driver.php index 174b50b..02e5812 100644 --- a/src/Driver/Driver.php +++ b/src/Driver/Driver.php @@ -16,6 +16,7 @@ namespace Drift\DBAL\Driver; use Drift\DBAL\Credentials; +use Drift\DBAL\Result; use React\Promise\PromiseInterface; /** @@ -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); @@ -39,7 +37,7 @@ public function connect(Credentials $credentials); * @param string $sql * @param array $parameters * - * @return PromiseInterface + * @return PromiseInterface */ public function query( string $sql, diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 0b70480..e5c28d0 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -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; @@ -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 diff --git a/tests/MysqlConnectionTest.php b/tests/MysqlConnectionTest.php index 853a264..5b08cc9 100644 --- a/tests/MysqlConnectionTest.php +++ b/tests/MysqlConnectionTest.php @@ -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; - }); - } } diff --git a/tests/PostgreSQLConnectionTest.php b/tests/PostgreSQLConnectionTest.php index 3d7092a..e8eb2aa 100644 --- a/tests/PostgreSQLConnectionTest.php +++ b/tests/PostgreSQLConnectionTest.php @@ -15,13 +15,11 @@ namespace Drift\DBAL\Tests; -use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Drift\DBAL\Connection; use Drift\DBAL\Credentials; use Drift\DBAL\Driver\PostgreSQL\PostgreSQLDriver; use React\EventLoop\LoopInterface; -use React\Promise\PromiseInterface; /** * Class PostgreSQLConnectionTest. @@ -43,45 +41,4 @@ public 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 PRIMARY KEY, field1 VARCHAR, field2 VARCHAR)') - ->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; - }) - ->otherwise(function (TableNotFoundException $exception) use ($connection) { - // Silent pass - - return $connection; - }); - } } diff --git a/tests/SQLiteConnectionTest.php b/tests/SQLiteConnectionTest.php index 54bd0d1..96200f3 100644 --- a/tests/SQLiteConnectionTest.php +++ b/tests/SQLiteConnectionTest.php @@ -15,13 +15,11 @@ namespace Drift\DBAL\Tests; -use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\Platforms\SqlitePlatform; use Drift\DBAL\Connection; use Drift\DBAL\Credentials; use Drift\DBAL\Driver\SQLite\SQLiteDriver; use React\EventLoop\LoopInterface; -use React\Promise\PromiseInterface; /** * Class SQLiteConnectionTest. @@ -45,40 +43,4 @@ public function getConnection(LoopInterface $loop): Connection ':memory:' ), $mysqlPlatform); } - - /** - * Create database and table. - * - * @param Connection $connection - * - * @return PromiseInterface - */ - protected function createInfrastructure(Connection $connection): PromiseInterface - { - return $connection - ->queryBySQL('CREATE TABLE test (id VARCHAR(255) PRIMARY KEY, field1 VARCHAR(255), field2 VARCHAR (255))') - ->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; - }); - } }