From b33a6721c58813104fd29e8d890a2db55f13d519 Mon Sep 17 00:00:00 2001 From: noelma Date: Sun, 7 Nov 2021 16:33:22 +0100 Subject: [PATCH 01/12] feat: php-cs-fixer update --- .gitattributes | 2 +- .gitignore | 3 +-- .php_cs.dist => .php-cs-fixer.dist.php | 11 +++-------- composer.json | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) rename .php_cs.dist => .php-cs-fixer.dist.php (93%) diff --git a/.gitattributes b/.gitattributes index 4505136..9d344da 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,7 +4,7 @@ .coveralls.yml export-ignore .gitattributes export-ignore .gitignore export-ignore -.php_cs.dist export-ignore +.php-cs-fixer.dist.php export-ignore .travis.yml export-ignore phpdoc.dist.xml export-ignore phpunit.xml export-ignore diff --git a/.gitignore b/.gitignore index 97ce26a..dff0c4b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ /*.lock /*.phar /*.properties -/.php_cs.cache -/.phpunit.result.cache \ No newline at end of file +/*.cache \ No newline at end of file diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 93% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index 1b00355..5fd064a 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -5,8 +5,8 @@ ->exclude('build') ->in(__DIR__); -return PhpCsFixer\Config::create() - ->setRules([ +$config = new PhpCsFixer\Config(); +return $config->setRules([ '@PSR2' => true, /* Force la déclaration des tableaux sous leur forme courte. */ 'array_syntax' => [ 'syntax' => 'short' ], @@ -24,7 +24,6 @@ 'concat_space' => [ 'spacing' => 'one' ], /* Remplace les commentaires simples # par //. */ 'fully_qualified_strict_types' => true, - 'hash_to_slash_comment' => true, /* Retire les parenthèses des include/require. */ 'include' => true, /* Ne pas avoir de code à l'ouverture d'une balise php */ @@ -55,22 +54,18 @@ 'no_mixed_echo_print' => true, /* Supprime les espaces d'une seule ligne avant la fermeture du point-virgule. */ 'no_singleline_whitespace_before_semicolons' => true, - /* Supprimes les espaces autour des accolades des tableaux. */ - 'no_spaces_around_offset' => [ 'outside' ], /* Remplace les éléments elseif superflus par if. */ 'no_superfluous_elseif' => true, /* Supprime les accolades superflus. */ 'no_unneeded_curly_braces' => true, /* Supprime les parenthèses superflus. */ 'no_unneeded_control_parentheses' => true, - /* Une class final ne doit pas avoir de méthodes finales. */ - 'no_unneeded_final_method' => true, /* Supprimez les virgules de fin dans les listes. */ 'no_trailing_comma_in_list_call' => true, /* Supprimez les virgules de fin dans les array. */ 'no_trailing_comma_in_singleline_array' => true, /* Remplace les true, + 'echo_tag_syntax' => true, /* Les annotations PHPUnit doivent être un FQCN, y compris un espace de noms racine. */ 'php_unit_fqcn_annotation' => true, /* Appliquez la camelCase aux méthodes de test PHPUnit, après la configuration. */ diff --git a/composer.json b/composer.json index 20e207a..7e9fcf5 100644 --- a/composer.json +++ b/composer.json @@ -18,12 +18,12 @@ "php": ">=7.2" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.85", "phpstan/phpstan-phpunit": "^0.12.18", "phpunit/phpunit": "^8.5" }, "suggest": { - "friendsofphp/php-cs-fixer": "To normalize the PHP code.", "phpdocumentor/phpdocumentor": "To generate documentation.", "phpmetrics/phpmetrics": "To generate a code status report in HTML format." }, From db21a5625990fa353e371251c8d0262625ec60ab Mon Sep 17 00:00:00 2001 From: noelma Date: Sun, 7 Nov 2021 16:36:09 +0100 Subject: [PATCH 02/12] feat: the binary files must be at the root of the project. --- .gitignore | 1 + composer.json | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index dff0c4b..9b39132 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Repertory /.scannerwork/ +/bin/ /build/ /nbproject/ /vendor/ diff --git a/composer.json b/composer.json index 7e9fcf5..f20368e 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ } }, "config": { + "bin-dir": "bin", "sort-packages": true, "optimize-autoloader": true } From b016f0378d2631a38c54ec3a33146f74af3d7bda Mon Sep 17 00:00:00 2001 From: noelma Date: Thu, 11 Nov 2021 13:17:10 +0100 Subject: [PATCH 03/12] refactor: move execution tests to a separate class --- tests/RequestExecuteTest.php | 234 ++++++++++++++++++++++++++++ tests/RequestTest.php | 283 ++++++++-------------------------- tests/fixtures/role.json | 12 ++ tests/fixtures/schema.json | 43 ++++++ tests/fixtures/user.json | 37 +++++ tests/fixtures/user_role.json | 26 ++++ 6 files changed, 419 insertions(+), 216 deletions(-) create mode 100644 tests/RequestExecuteTest.php create mode 100644 tests/fixtures/role.json create mode 100644 tests/fixtures/schema.json create mode 100644 tests/fixtures/user.json create mode 100644 tests/fixtures/user_role.json diff --git a/tests/RequestExecuteTest.php b/tests/RequestExecuteTest.php new file mode 100644 index 0000000..7121ac1 --- /dev/null +++ b/tests/RequestExecuteTest.php @@ -0,0 +1,234 @@ +isDot() || $fileInfo->getRealPath() === false) { + continue; + } + unlink($fileInfo->getRealPath()); + } + if (file_exists(self::ROOT)) { + rmdir(self::ROOT); + } + } + + protected function setUp(): void + { + $this->bdd = (new Schema) + ->setConfig('data', 'schema', new \Queryflatfile\Driver\Json()) + ->setPathRoot(__DIR__ . '/'); + + $this->request = new Request($this->bdd); + } + + public function testCreateTable(): void + { + $this->bdd->createTable('user', static function (TableBuilder $table) { + $table->increments('id') + ->string('name')->nullable() + ->string('firstname')->nullable(); + }); + $this->bdd->createTable('user_role', static function (TableBuilder $table) { + $table->integer('id_user') + ->integer('id_role'); + }); + $this->bdd->createTable('role', static function (TableBuilder $table) { + $table->increments('id_role') + ->string('labelle'); + }); + + self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); + self::assertFileExists(self::ROOT . 'user_role.' . $this->bdd->getExtension()); + self::assertFileExists(self::ROOT . 'role.' . $this->bdd->getExtension()); + } + + public function testCreateTableException(): void + { + $this->expectException(\Exception::class); + $this->bdd->createTable('user'); + } + + public function testCreateTableIfNotExists(): void + { + $this->bdd->createTableIfNotExists('user'); + + self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); + } + + public function testInsertInto(): void + { + $this->request->insertInto('user', [ 'id', 'name', 'firstname' ]) + ->values([ 0, 'NOEL', 'Mathieu' ]) + ->values([ 1, 'DUPOND', 'Jean' ]) + ->execute(); + + $this->request->insertInto('user', [ 'name', 'firstname' ]) + ->values([ 'MARTIN', 'Manon' ]) + ->values([ null, 'Marie' ]) + ->values([ 'DUPOND', 'Pierre' ]) + ->execute(); + + $this->request->insertInto('user', [ 'id', 'name', 'firstname' ]) + ->values([ 5, 'MEYER', 'Eva' ]) + ->values([ 6, 'ROBERT', null ]) + ->execute(); + + $this->request->insertInto('role', [ 'id_role', 'labelle' ]) + ->values([ 0, 'Admin' ]) + ->values([ 1, 'Author' ]) + ->values([ 2, 'User' ]) + ->execute(); + + $this->request->insertInto('user_role', [ 'id_user', 'id_role' ]) + ->values([ 0, 0 ]) + ->values([ 1, 0 ]) + ->values([ 2, 1 ]) + ->values([ 3, 1 ]) + ->values([ 4, 2 ]) + ->values([ 5, 2 ]) + ->execute(); + + self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); + } + + public function testGetIncrement(): void + { + self::assertEquals(6, $this->bdd->getIncrement('user')); + self::assertEquals(2, $this->bdd->getIncrement('role')); + } + + public function testGetIncrementNoFound(): void + { + $this->expectException(TableNotFoundException::class); + $this->bdd->getIncrement('error'); + } + + public function testGetIncrementNoExist(): void + { + $this->expectException(\Exception::class); + $this->bdd->getIncrement('user_role'); + } + + public function testCreateTableIfNotExistsData(): void + { + $this->bdd->createTableIfNotExists('user'); + + self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); + } + + public function testInsertIntoExceptionTable(): void + { + $this->expectException(TableNotFoundException::class); + $this->request->insertInto('foo', [ 'id', 'name', 'firstname' ])->execute(); + } + + public function testInsertIntoExceptionColumn(): void + { + $this->expectException(ColumnsNotFoundException::class); + $this->request->insertInto('user', [])->values([ 0, 'NOEL' ])->execute(); + } + + public function testInsertIntoExceptionValue(): void + { + $this->expectException(ColumnsNotFoundException::class); + $this->request->insertInto('user', [ 'id', 'name', 'firstname' ]) + ->values([ 0, 'NOEL' ]) + ->execute(); + } + + public function testUpdateData(): void + { + $this->request + ->update('user', [ 'name' => 'PETIT' ]) + ->where('id', '=', 0) + ->execute(); + + $data = $this->request + ->from('user') + ->where('id', '=', 0) + ->fetch(); + + self::assertEquals([ 'id' => 0, 'name' => 'PETIT', 'firstname' => 'Mathieu' ], $data); + } + + public function testUpdateDataFull(): void + { + $this->request + ->update('user', [ 'name' => 'PETIT' ]) + ->execute(); + + $data = $this->request + ->from('user') + ->where('id', '=', 0) + ->fetch(); + + self::assertEquals([ 'id' => 0, 'name' => 'PETIT', 'firstname' => 'Mathieu' ], $data); + } + + public function testDeleteData(): void + { + $this->request->from('user') + ->delete() + ->between('id', 1, 4) + ->execute(); + + $data = $this->request + ->from('user') + ->fetchAll(); + + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'PETIT', 'firstname' => 'Mathieu' ], + [ 'id' => 5, 'name' => 'PETIT', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'PETIT', 'firstname' => null ] + ], + $data + ); + } + + public function testDropTable(): void + { + $this->bdd->dropTable('user_role'); + + self::assertFileNotExists(self::ROOT . 'user_role.json'); + } + + public function testDropSchema(): void + { + $this->bdd->dropSchema(); + + self::assertFileNotExists(self::ROOT . 'schema.json'); + } +} diff --git a/tests/RequestTest.php b/tests/RequestTest.php index efaedde..6f72183 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -2,14 +2,14 @@ namespace Queryflatfile\Test; -use Queryflatfile\Exception\Query\BadFunctionException; +use PHPUnit\Framework\MockObject\MockObject; +use Queryflatfile\DriverInterface; use Queryflatfile\Exception\Query\ColumnsNotFoundException; use Queryflatfile\Exception\Query\OperatorNotFound; use Queryflatfile\Exception\Query\QueryException; use Queryflatfile\Exception\Query\TableNotFoundException; use Queryflatfile\Request; use Queryflatfile\Schema; -use Queryflatfile\TableBuilder; class RequestTest extends \PHPUnit\Framework\TestCase { @@ -28,148 +28,16 @@ class RequestTest extends \PHPUnit\Framework\TestCase /** * @var Request */ - protected $request2; - - public static function tearDownAfterClass(): void - { - if (!file_exists(self::ROOT)) { - return; - } - $dir = new \DirectoryIterator(self::ROOT); - foreach ($dir as $fileInfo) { - if ($fileInfo->isDot() || $fileInfo->getRealPath() === false) { - continue; - } - unlink($fileInfo->getRealPath()); - } - if (file_exists(self::ROOT)) { - rmdir(self::ROOT); - } - } + protected $secondRequest; protected function setUp(): void { - $this->bdd = (new Schema) - ->setConfig('data', 'schema', new \Queryflatfile\Driver\Json()) + $this->bdd = (new Schema) + ->setConfig('data', 'schema', $this->getDriverMock()) ->setPathRoot(__DIR__ . '/'); $this->request = new Request($this->bdd); - $this->request2 = new Request($this->bdd); - } - - public function testCreateTable(): void - { - $this->bdd->createTable('user', static function (TableBuilder $table) { - $table->increments('id') - ->string('name')->nullable() - ->string('firstname')->nullable(); - }); - $this->bdd->createTable('user_role', static function (TableBuilder $table) { - $table->integer('id_user') - ->integer('id_role'); - }); - $this->bdd->createTable('role', static function (TableBuilder $table) { - $table->increments('id_role') - ->string('labelle'); - }); - - self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); - self::assertFileExists(self::ROOT . 'user_role.' . $this->bdd->getExtension()); - self::assertFileExists(self::ROOT . 'role.' . $this->bdd->getExtension()); - } - - public function testCreateTableException(): void - { - $this->expectException(\Exception::class); - $this->bdd->createTable('user'); - } - - public function testCreateTableIfNotExists(): void - { - $this->bdd->createTableIfNotExists('user'); - - self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); - } - - public function testInsertInto(): void - { - $this->request->insertInto('user', [ 'id', 'name', 'firstname' ]) - ->values([ 0, 'NOEL', 'Mathieu' ]) - ->values([ 1, 'DUPOND', 'Jean' ]) - ->execute(); - - $this->request->insertInto('user', [ 'name', 'firstname' ]) - ->values([ 'MARTIN', 'Manon' ]) - ->values([ null, 'Marie' ]) - ->values([ 'DUPOND', 'Pierre' ]) - ->execute(); - - $this->request->insertInto('user', [ 'id', 'name', 'firstname' ]) - ->values([ 5, 'MEYER', 'Eva' ]) - ->values([ 6, 'ROBERT', null ]) - ->execute(); - - $this->request->insertInto('role', [ 'id_role', 'labelle' ]) - ->values([ 0, 'Admin' ]) - ->values([ 1, 'Author' ]) - ->values([ 2, 'User' ]) - ->execute(); - - $this->request->insertInto('user_role', [ 'id_user', 'id_role' ]) - ->values([ 0, 0 ]) - ->values([ 1, 0 ]) - ->values([ 2, 1 ]) - ->values([ 3, 1 ]) - ->values([ 4, 2 ]) - ->values([ 5, 2 ]) - ->execute(); - - self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); - } - - public function testGetIncrement(): void - { - self::assertEquals($this->bdd->getIncrement('user'), 6); - self::assertEquals($this->bdd->getIncrement('role'), 2); - } - - public function testGetIncrementNoFound(): void - { - $this->expectException(TableNotFoundException::class); - self::assertEquals($this->bdd->getIncrement('error'), 1); - } - - public function testGetIncrementNoExist(): void - { - $this->expectException(\Exception::class); - self::assertEquals($this->bdd->getIncrement('user_role'), 1); - } - - public function testCreateTableIfNotExistsData(): void - { - $this->bdd->createTableIfNotExists('user'); - - self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); - } - - public function testInsertIntoExceptionTable(): void - { - $this->expectException(TableNotFoundException::class); - $this->request->insertInto('foo', [ 'id', 'name', 'firstname' ])->execute(); - } - - public function testInsertIntoExceptionColumn(): void - { - $this->expectException(ColumnsNotFoundException::class); - $this->request->insertInto('user', [])->values([ 0, 'NOEL' ])->execute(); - } - - public function testInsertIntoExceptionValue(): void - { - $this->expectException(ColumnsNotFoundException::class); - $this->request->insertInto('user', [ 'id', 'name', 'firstname' ]) - ->values([ 0, 'NOEL' ]) - ->execute(); + $this->secondRequest = new Request($this->bdd); } public function testSelect(): void @@ -1145,7 +1013,7 @@ public function testUnion(): void ->from('user') ->between('id', 1, 5); - $data = $this->request2 + $data = $this->secondRequest ->select('name') ->from('user') ->union($union) @@ -1168,7 +1036,7 @@ public function testUnionMultiple(): void ->from('user') ->between('id', 1, 5); - $data = $this->request2 + $data = $this->secondRequest ->select('name', 'firstname') ->from('user') ->union($union) @@ -1193,7 +1061,7 @@ public function testUnionMultipleException(): void ->from('user') ->between('id', 1, 5); - $this->request2 + $this->secondRequest ->select('name', 'firstname') ->from('user') ->union($union) @@ -1207,7 +1075,7 @@ public function testUnionAll(): void ->from('user') ->between('id', 1, 5); - $data = $this->request2 + $data = $this->secondRequest ->select('name') ->from('user') ->unionAll($union) @@ -1236,7 +1104,7 @@ public function testUnionAllMultiple(): void ->from('user') ->between('id', 1, 5); - $data = $this->request2 + $data = $this->secondRequest ->select('name', 'firstname') ->from('user') ->unionAll($union) @@ -1266,7 +1134,7 @@ public function testUnionAllMultipleException(): void ->from('user') ->between('id', 1, 5); - $this->request2 + $this->secondRequest ->select('name', 'firstname') ->from('user') ->unionAll($union) @@ -1280,7 +1148,7 @@ public function testUnionList(): void ->from('user') ->between('id', 1, 5); - $data = $this->request2 + $data = $this->secondRequest ->select('name') ->from('user') ->union($union) @@ -1303,7 +1171,7 @@ public function testUnionListOrder(): void ->from('user') ->between('id', 1, 5); - $data = $this->request2 + $data = $this->secondRequest ->select('name') ->from('user') ->union($union) @@ -1320,79 +1188,62 @@ public function testUnionListOrder(): void ]); } - public function testExecuteException(): void - { - $this->expectException(BadFunctionException::class); - $this->request - ->from('user') - ->where('id', '=', 0) - ->execute(); - } - - public function testUpdateData(): void - { - $this->request - ->update('user', [ 'name' => 'PETIT' ]) - ->where('id', '=', 0) - ->execute(); - - $data = $this->request - ->from('user') - ->where('id', '=', 0) - ->fetch(); - - self::assertEquals($data, [ 'id' => 0, 'name' => 'PETIT', 'firstname' => 'Mathieu' ]); - } - - public function testUpdateDataFull(): void - { - $this->request - ->update('user', [ 'name' => 'PETIT' ]) - ->execute(); - - $data = $this->request - ->from('user') - ->where('id', '=', 0) - ->fetch(); - - self::assertEquals($data, [ 'id' => 0, 'name' => 'PETIT', 'firstname' => 'Mathieu' ]); - } - - public function testDeleteData(): void - { - $this->request->from('user') - ->delete() - ->between('id', 1, 4) - ->execute(); - - $data = $this->request - ->from('user') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'PETIT', 'firstname' => 'Mathieu' ], - [ 'id' => 5, 'name' => 'PETIT', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'PETIT', 'firstname' => null ] - ]); - } - - public function testDropTable(): void - { - $hasTableUserRole = $this->bdd->dropTable('user_role'); - - self::assertFileNotExists(self::ROOT . 'user_role.json'); - } - - public function testDropSchema(): void - { - $hasSchema = $this->bdd->dropSchema(); - - self::assertFileNotExists(self::ROOT . 'schema.json'); - } - public function testPredicate(): void { $this->expectException(\Exception::class); \Queryflatfile\Where::predicate('', 'error', ''); } + + /** + * @return DriverInterface&MockObject + */ + private function getDriverMock() + { + $mock = $this->createMock(DriverInterface::class); + $mock->expects(self::any()) + ->method('create') + ->willReturnCallback( + function (string $path, string $filename): bool { + return in_array($filename, [ 'schema', 'user', 'user_role', 'role' ]); + } + ); + + $mock->expects(self::any()) + ->method('has') + ->willReturnCallback( + function (string $path, string $filename): bool { + return in_array($filename, [ 'schema', 'user', 'user_role', 'role' ]); + } + ); + + $mock->expects(self::any()) + ->method('read') + ->willReturnCallback( + function (string $path, string $filename): array { + if ($filename === 'schema') { + return $this->loadFixtures('schema'); + } + if ($filename === 'user') { + return $this->loadFixtures('user'); + } + if ($filename === 'user_role') { + return $this->loadFixtures('user_role'); + } + if ($filename === 'role') { + return $this->loadFixtures('role'); + } + + throw new \Exception("Table $filename not found"); + } + ); + + return $mock; + } + + private function loadFixtures(string $filename): array + { + $json = (string) file_get_contents(__DIR__ . "/fixtures/$filename.json"); + + return json_decode($json, true); + } } diff --git a/tests/fixtures/role.json b/tests/fixtures/role.json new file mode 100644 index 0000000..90c8170 --- /dev/null +++ b/tests/fixtures/role.json @@ -0,0 +1,12 @@ +[ + { + "id_role": 0, + "labelle": "Admin" + }, { + "id_role": 1, + "labelle": "Author" + }, { + "id_role": 2, + "labelle": "User" + } +] \ No newline at end of file diff --git a/tests/fixtures/schema.json b/tests/fixtures/schema.json new file mode 100644 index 0000000..4a2d3ec --- /dev/null +++ b/tests/fixtures/schema.json @@ -0,0 +1,43 @@ +{ + "user": { + "fields": { + "id": { + "type": "increments" + }, + "name": { + "type": "string", + "length": 255, + "nullable": true + }, + "firstname": { + "type": "string", + "length": 255, + "nullable": true + } + }, + "increments": 6 + }, + "user_role": { + "fields": { + "id_user": { + "type": "integer" + }, + "id_role": { + "type": "integer" + } + }, + "increments": null + }, + "role": { + "fields": { + "id_role": { + "type": "increments" + }, + "labelle": { + "type": "string", + "length": 255 + } + }, + "increments": 2 + } +} \ No newline at end of file diff --git a/tests/fixtures/user.json b/tests/fixtures/user.json new file mode 100644 index 0000000..00c2660 --- /dev/null +++ b/tests/fixtures/user.json @@ -0,0 +1,37 @@ +[ + { + "id": 0, + "name": "NOEL", + "firstname": "Mathieu" + }, + { + "id": 1, + "name": "DUPOND", + "firstname": "Jean" + }, + { + "id": 2, + "name": "MARTIN", + "firstname": "Manon" + }, + { + "id": 3, + "name": null, + "firstname": "Marie" + }, + { + "id": 4, + "name": "DUPOND", + "firstname": "Pierre" + }, + { + "id": 5, + "name": "MEYER", + "firstname": "Eva" + }, + { + "id": 6, + "name": "ROBERT", + "firstname": null + } +] \ No newline at end of file diff --git a/tests/fixtures/user_role.json b/tests/fixtures/user_role.json new file mode 100644 index 0000000..cb6c24d --- /dev/null +++ b/tests/fixtures/user_role.json @@ -0,0 +1,26 @@ +[ + { + "id_user": 0, + "id_role": 0 + }, + { + "id_user": 1, + "id_role": 0 + }, + { + "id_user": 2, + "id_role": 1 + }, + { + "id_user": 3, + "id_role": 1 + }, + { + "id_user": 4, + "id_role": 2 + }, + { + "id_user": 5, + "id_role": 2 + } +] \ No newline at end of file From 21d2a56eadef7ee2e8344b79b510d3c5c86afb58 Mon Sep 17 00:00:00 2001 From: noelma Date: Thu, 11 Nov 2021 14:43:39 +0100 Subject: [PATCH 04/12] refactor: query string and tests --- src/Request.php | 33 +- src/Where.php | 38 +- tests/RequestTest.php | 1467 ++++++++++++++++++++++++++--------------- 3 files changed, 985 insertions(+), 553 deletions(-) diff --git a/src/Request.php b/src/Request.php index 9f00f09..0920458 100644 --- a/src/Request.php +++ b/src/Request.php @@ -102,41 +102,40 @@ public function __construct(Schema $schema) */ public function __toString(): string { - $output = 'SELECT * '; - if ($this->columns) { - $output = 'SELECT ' . implode(', ', $this->columns) . ' '; + $output = ''; + if ($this->execute) { + $output .= strtoupper($this->execute) . ' '; + } else { + $output .= sprintf('SELECT %s ', $this->columns ? implode(', ', $this->columns) : '*'); } if ($this->from) { - $output .= "FROM $this->from "; + $output .= sprintf('FROM %s ', $this->from); } foreach ($this->joins as $value) { - $output .= strtoupper($value[ 'type' ]) . " JOIN {$value[ 'table' ]} ON {$value[ 'where' ]}"; + $output .= sprintf('%s JOIN %s ON %s ', strtoupper($value[ 'type' ]), $value[ 'table' ], (string) $value[ 'where' ]); } if ($this->where) { - $output .= 'WHERE ' . (string) $this->where; + $output .= sprintf('WHERE %s ', (string) $this->where); } foreach ($this->union as $union) { - $output .= $union[ 'type' ] === self::UNION_SIMPLE - ? 'UNION' - : 'UNION ALL'; - $output .= " ({$union[ 'request' ]}) "; + $type = $union[ 'type' ] === self::UNION_SIMPLE ? 'UNION' : 'UNION ALL'; + $subRequest = trim((string) $union[ 'request' ], ';'); + $output .= sprintf('%s %s ', $type, $subRequest); } if ($this->orderBy) { $output .= 'ORDER BY '; foreach ($this->orderBy as $field => $order) { - $output .= $field . ' ' . ($order === SORT_ASC - ? 'ASC,' - : 'DESC,'); + $output .= sprintf('%s %s, ', $field, $order === SORT_ASC ? 'ASC' : 'DESC'); } - $output = substr($output, 0, -1) . ' '; + $output = trim($output, ', ') . ' '; } if ($this->limit) { - $output .= "LIMIT $this->limit "; + $output .= sprintf('LIMIT %s ', (string) $this->limit); } if ($this->offset) { - $output .= "OFFSET $this->offset "; + $output .= sprintf('OFFSET %s ', (string) $this->offset); } - $output = substr($output, 0, -1) . ';'; + $output = trim($output) . ';'; return htmlspecialchars($output); } diff --git a/src/Where.php b/src/Where.php index 8a84609..452a82f 100644 --- a/src/Where.php +++ b/src/Where.php @@ -28,57 +28,53 @@ public function __toString(): string { $output = ''; foreach ($this->where as $where) { + $output .= $where[ 'bool' ] === self::EXP_AND + ? 'AND ' + : 'OR '; + $not = $where[ 'not' ] - ? 'NOT' + ? 'NOT ' : ''; switch ($where[ 'type' ]) { case 'where': $value = \is_int($where[ 'value' ]) ? $where[ 'value' ] : "'{$where[ 'value' ]}'"; - $output .= "{$where[ 'column' ]} $not " . strtoupper($where[ 'condition' ]) . " $value "; + $output .= sprintf('%s%s %s %s ', $not, $where[ 'column' ], $where[ 'condition' ], $value); break; case 'like': - $output .= "{$where[ 'column' ]} $not LIKE '{$where[ 'value' ]}' "; + $output .= sprintf('%s %sLIKE %s ', $where[ 'column' ], $not, $where[ 'value' ]); break; case 'isNull': - $output .= "{$where[ 'column' ]} IS $not NULL "; + $output .= sprintf('%s IS %sNULL ', $where[ 'column' ], $not); break; case 'in': - $output .= "{$where[ 'column' ]} $not IN " . implode(', ', $where[ 'value' ]) . ' '; + $output .= sprintf('%s %sIN %s ', $where[ 'column' ], $not, implode(', ', $where[ 'value' ])); break; case 'whereCallback': - $output .= "$not ({$where[ 'value' ]}) "; + $output .= sprintf('%s(%s) ', $not, $where[ 'value' ]); break; case 'between': - $output .= "{$where[ 'column' ]} $not BETWEEN {$where[ 'value' ][ 'min' ]} AND {$where[ 'value' ][ 'max' ]} "; + $output .= sprintf('%s %sBETWEEN %s AND %s ', $where[ 'column' ], $not, $where[ 'value' ][ 'min' ], $where[ 'value' ][ 'max' ]); break; case 'regex': - $output .= "{$where[ 'column' ]} $not REGEX {$where[ 'value' ]} "; + $output .= sprintf('%s %sREGEX %s ', $where[ 'column' ], $not, $where[ 'value' ]); break; } - - $output .= $where[ 'bool' ] === self::EXP_AND - ? ' AND ' - : ' OR '; - } - /* Cherche si la dernière occurence est AND ou OR. */ - $nb = 0; - if (strrchr($output, 'AND') !== false) { - $nb = substr(strrchr($output, 'AND'), 0) === 'AND ' - ? -4 - : -3; } + $output = trim($output, ' '); + $str = strpos($output, 'AND ') === 0 + ? 'AND ' + : 'OR '; - /* Supprime la dernière occurence et renvoie la chaine. */ - return htmlspecialchars(substr($output, 0, $nb)); + return substr_replace($output, '', 0, strlen($str)); } /** diff --git a/tests/RequestTest.php b/tests/RequestTest.php index 6f72183..d3a80d0 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -42,64 +42,110 @@ protected function setUp(): void public function testSelect(): void { - $data1 = $this->request->select([ 'firstname' ])->from('user')->fetch(); - $data2 = $this->request->select('firstname')->from('user')->fetch(); - $data3 = $this->request->select()->from('user')->fetch(); - $data4 = $this->request->from('user')->fetch(); + $data1 = $this->request->select([ 'firstname' ])->from('user'); - self::assertEquals($data1, [ 'firstname' => 'Mathieu' ]); - self::assertEquals($data2, [ 'firstname' => 'Mathieu' ]); - self::assertEquals($data3, [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ]); - self::assertEquals($data4, [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ]); + self::assertEquals( + 'SELECT firstname FROM user;', + (string) $data1 + ); + self::assertEquals( + [ 'firstname' => 'Mathieu' ], + $data1->fetch() + ); + + $data2 = $this->request->select('firstname')->from('user'); + + self::assertEquals( + 'SELECT firstname FROM user;', + (string) $data2 + ); + self::assertEquals( + [ 'firstname' => 'Mathieu' ], + $data2->fetch() + ); + + $data3 = $this->request->select()->from('user'); + + self::assertEquals( + 'SELECT * FROM user;', + (string) $data3 + ); + self::assertEquals( + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + $data3->fetch() + ); + + $data4 = $this->request->from('user'); + + self::assertEquals( + 'SELECT * FROM user;', + (string) $data4 + ); + self::assertEquals( + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + $data4->fetch() + ); } public function testLists(): void { - $data = $this->request->from('user')->lists('firstname'); + $data = $this->request->from('user'); - $assert = [ 'Mathieu', 'Jean', 'Manon', 'Marie', 'Pierre', 'Eva', null ]; - - self::assertEquals($data, $assert); + self::assertEquals( + 'SELECT * FROM user;', + (string) $data + ); + self::assertEquals( + [ 'Mathieu', 'Jean', 'Manon', 'Marie', 'Pierre', 'Eva', null ], + $data->lists('firstname') + ); } public function testListsKey(): void { - $data = $this->request->from('user')->lists('firstname', 'id'); - - self::assertEquals($data, [ - 0 => 'Mathieu', - 1 => 'Jean', - 2 => 'Manon', - 3 => 'Marie', - 4 => 'Pierre', - 5 => 'Eva', - 6 => null - ]); + $data = $this->request->from('user'); + + self::assertEquals( + 'SELECT * FROM user;', + (string) $data + ); + self::assertEquals( + [ + 0 => 'Mathieu', + 1 => 'Jean', + 2 => 'Manon', + 3 => 'Marie', + 4 => 'Pierre', + 5 => 'Eva', + 6 => null + ], + $data->lists('firstname', 'id') + ); } public function testListsVoid(): void { $data = $this->request->from('user')->lists('error'); - self::assertEquals($data, []); + self::assertEquals([], $data); } public function testSelectExceptionValue(): void { $this->expectException(ColumnsNotFoundException::class); - $this->request->select([ 'foo' ])->from('user')->fetch(); + $this->request->select('foo')->from('user')->fetch(); } public function testSelectExceptionFrom(): void { $this->expectException(TableNotFoundException::class); - $this->request->select([ 'firstname' ])->from('foo')->fetch(); + $this->request->select('firstname')->from('foo')->fetch(); } public function testFromException(): void { $this->expectException(TableNotFoundException::class); - $this->request->select([ 'firstname' ])->fetch(); + $this->request->select('firstname')->fetch(); } public function testSelectAlternativeExceptionFrom(): void @@ -113,29 +159,55 @@ public function testSelectAlternativeExceptionFrom(): void * * @dataProvider whereEqualsProvider */ - public function testWhereEquals(string $operator, $value, array $arraySubject): void - { - $data = $this->request->select('name') + public function testWhereEquals( + string $operator, + $value, + string $expectedQueryStr, + array $expectedData + ): void { + $data = $this->request + ->select('name') ->from('user') - ->where('id', $operator, $value) - ->fetch(); + ->where('id', $operator, $value); - self::assertEquals($data, $arraySubject); + self::assertEquals($expectedQueryStr, (string) $data); + self::assertEquals($expectedData, $data->fetch()); } public function whereEqualsProvider(): \Generator { - yield [ '=', '1', [] ]; - yield [ '===', '1', [] ]; - yield [ '=', 1, [ 'name' => 'DUPOND' ] ]; - yield [ '===', '1', [] ]; - yield [ '==', '1', [ 'name' => 'DUPOND' ] ]; + yield [ + '=', '1', + 'SELECT name FROM user WHERE id = \'1\';', + [] + ]; + yield [ + '===', '1', + 'SELECT name FROM user WHERE id === \'1\';', + [] + ]; + yield [ + '=', 1, + 'SELECT name FROM user WHERE id = 1;', + [ 'name' => 'DUPOND' ] + ]; + yield [ + '===', '1', + 'SELECT name FROM user WHERE id === \'1\';', + [] + ]; + yield [ + '==', '1', + 'SELECT name FROM user WHERE id == \'1\';', + [ 'name' => 'DUPOND' ] + ]; } public function testWhereOperatorException(): void { $this->expectException(OperatorNotFound::class); - $this->request->select('name') + $this->request + ->select('name') ->from('user') ->where('id', 'error', '1') ->fetch(); @@ -146,112 +218,143 @@ public function testWhereOperatorException(): void * * @dataProvider whereNotEqualsProvider */ - public function testWhereNotEqualsNoType(string $operator, $value): void - { - $data = $this->request->select('id') + public function testWhereNotEqualsNoType( + string $operator, + $value, + string $expectedQueryStr + ): void { + $data = $this->request + ->select('id') ->from('user') - ->where('id', $operator, $value) - ->fetchAll(); - - $arraySubject = [ - [ 'id' => 0 ], - [ 'id' => 2 ], - [ 'id' => 3 ], - [ 'id' => 4 ], - [ 'id' => 5 ], - [ 'id' => 6 ] - ]; + ->where('id', $operator, $value); + self::assertEquals($expectedQueryStr, (string) $data); /* whereNotEquals sans prendre en compte le type */ - self::assertEquals($data, $arraySubject); + self::assertEquals( + [ + [ 'id' => 0 ], + [ 'id' => 2 ], + [ 'id' => 3 ], + [ 'id' => 4 ], + [ 'id' => 5 ], + [ 'id' => 6 ] + ], + $data->fetchAll() + ); } public function whereNotEqualsProvider(): \Generator { - yield [ '<>', '1' ]; - yield [ '<>', 1 ]; - yield [ '!=', '1' ]; - yield [ '!=', 1 ]; + yield [ + '<>', '1', + 'SELECT id FROM user WHERE id <> \'1\';' + ]; + yield [ + '<>', 1, + 'SELECT id FROM user WHERE id <> 1;' + ]; + yield [ + '!=', '1', + 'SELECT id FROM user WHERE id != \'1\';' + ]; + yield [ + '!=', 1, + 'SELECT id FROM user WHERE id != 1;' + ]; } public function testWhereNotEqualsType(): void { /* L'identifiant stocké est un integer, pas un string */ - $dataType1 = $this->request->select('firstname') - ->from('user') - ->where('id', '!==', '1') - ->fetchAll(); - - $dataType2 = $this->request->select('firstname') - ->from('user') - ->where('id', '!==', 1) - ->fetchAll(); - - self::assertEquals($dataType1, [ - [ 'firstname' => 'Mathieu' ], - [ 'firstname' => 'Jean' ], - [ 'firstname' => 'Manon' ], - [ 'firstname' => 'Marie' ], - [ 'firstname' => 'Pierre' ], - [ 'firstname' => 'Eva' ], - [ 'firstname' => null ] - ]); - self::assertEquals($dataType2, [ - [ 'firstname' => 'Mathieu' ], - [ 'firstname' => 'Manon' ], - [ 'firstname' => 'Marie' ], - [ 'firstname' => 'Pierre' ], - [ 'firstname' => 'Eva' ], - [ 'firstname' => null ] - ]); + $data1 = $this->request + ->select('firstname') + ->from('user') + ->where('id', '!==', '1'); + + self::assertEquals( + 'SELECT firstname FROM user WHERE id !== \'1\';', + (string) $data1 + ); + self::assertEquals( + [ + [ 'firstname' => 'Mathieu' ], + [ 'firstname' => 'Jean' ], + [ 'firstname' => 'Manon' ], + [ 'firstname' => 'Marie' ], + [ 'firstname' => 'Pierre' ], + [ 'firstname' => 'Eva' ], + [ 'firstname' => null ] + ], + $data1->fetchAll() + ); + + $data2 = $this->request + ->select('firstname') + ->from('user') + ->where('id', '!==', 1); + + self::assertEquals( + 'SELECT firstname FROM user WHERE id !== 1;', + (string) $data2 + ); + self::assertEquals( + [ + [ 'firstname' => 'Mathieu' ], + [ 'firstname' => 'Manon' ], + [ 'firstname' => 'Marie' ], + [ 'firstname' => 'Pierre' ], + [ 'firstname' => 'Eva' ], + [ 'firstname' => null ] + ], + $data2->fetchAll() + ); } /** - * @dataProvider whereLessProvider + * @dataProvider whereOperatorProvider */ - public function testWhereLess(string $operator, array $arraySubject): void - { + public function testWhereOperator( + string $operator, + int $value, + string $expectedQueryStr, + array $expectedData + ): void { $data = $this->request ->from('user') - ->where('id', $operator, 1) - ->fetchAll(); + ->where('id', $operator, $value); - self::assertEquals($data, $arraySubject); + self::assertEquals($expectedQueryStr, (string) $data); + self::assertEquals($expectedData, $data->fetchAll()); } - public function whereLessProvider(): \Generator + public function whereOperatorProvider(): \Generator { - yield [ '<', [ + yield [ + '<', 1, + 'SELECT * FROM user WHERE id < 1;', + [ [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ] ] ]; - yield [ '<=', [ + yield [ + '<=', 1, + 'SELECT * FROM user WHERE id <= 1;', + [ [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ] ] ]; - } - - /** - * @dataProvider whereGreaterProvider - */ - public function testWhereGreater(string $operator, array $arraySubject): void - { - $data = $this->request - ->from('user') - ->where('id', $operator, 5) - ->fetchAll(); - - self::assertEquals($data, $arraySubject); - } - - public function whereGreaterProvider(): \Generator - { - yield [ '>', [ + yield [ + '>', 5, + 'SELECT * FROM user WHERE id > 5;', + [ [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] ] ]; - yield [ '>=', [ + yield [ + '>=', 5, + 'SELECT * FROM user WHERE id >= 5;', + [ [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] ] @@ -261,7 +364,8 @@ public function whereGreaterProvider(): \Generator public function testWhereEqualsExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); - $this->request->select([ 'name' ]) + $this->request + ->select('name') ->from('user') ->where('foo', '=', 'Jean') ->fetch(); @@ -271,26 +375,38 @@ public function testWhereBetween(): void { $data = $this->request ->from('user') - ->between('id', 1, 2) - ->fetch(); + ->between('id', 1, 2); - self::assertEquals($data, [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ]); + self::assertEquals( + 'SELECT * FROM user WHERE id BETWEEN 1 AND 2;', + (string) $data + ); + self::assertEquals( + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + $data->fetch() + ); } public function testWhereNotBetween(): void { $data = $this->request ->from('user') - ->notBetween('id', 1, 2) - ->fetchAll(); + ->notBetween('id', 1, 2); - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + self::assertEquals( + 'SELECT * FROM user WHERE id NOT BETWEEN 1 AND 2;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereOrBetween(): void @@ -298,15 +414,21 @@ public function testWhereOrBetween(): void $data = $this->request ->from('user') ->between('id', 1, 2) - ->orBetween('id', 5, 6) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + ->orBetween('id', 5, 6); + + self::assertEquals( + 'SELECT * FROM user WHERE id BETWEEN 1 AND 2 OR id BETWEEN 5 AND 6;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereOrNotBetween(): void @@ -314,16 +436,22 @@ public function testWhereOrNotBetween(): void $data = $this->request ->from('user') ->between('id', 1, 2) - ->orNotBetween('id', 5, 6) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] - ]); + ->orNotBetween('id', 5, 6); + + self::assertEquals( + 'SELECT * FROM user WHERE id BETWEEN 1 AND 2 OR id NOT BETWEEN 5 AND 6;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] + ], + $data->fetchAll() + ); } public function testWhereBetweenExceptionColumn(): void @@ -339,29 +467,41 @@ public function testWhereIn(): void { $data = $this->request ->from('user') - ->in('id', [ 0, 1 ]) - ->fetchAll(); + ->in('id', [ 0, 1 ]); - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ] - ]); + self::assertEquals( + 'SELECT * FROM user WHERE id IN 0, 1;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ] + ], + $data->fetchAll() + ); } public function testWhereNotIn(): void { $data = $this->request ->from('user') - ->notIn('id', [ 0, 1 ]) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + ->notIn('id', [ 0, 1 ]); + + self::assertEquals( + 'SELECT * FROM user WHERE id NOT IN 0, 1;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereOrIn(): void @@ -369,15 +509,21 @@ public function testWhereOrIn(): void $data = $this->request ->from('user') ->in('id', [ 0, 1 ]) - ->orIn('id', [ 5, 6 ]) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + ->orIn('id', [ 5, 6 ]); + + self::assertEquals( + 'SELECT * FROM user WHERE id IN 0, 1 OR id IN 5, 6;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereOrNotIn(): void @@ -385,16 +531,22 @@ public function testWhereOrNotIn(): void $data = $this->request ->from('user') ->in('id', [ 0, 1 ]) - ->orNotIn('id', [ 5, 6 ]) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - ]); + ->orNotIn('id', [ 5, 6 ]); + + self::assertEquals( + 'SELECT * FROM user WHERE id IN 0, 1 OR id NOT IN 5, 6;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + ], + $data->fetchAll() + ); } public function testWhereInExceptionColumn(): void @@ -410,27 +562,39 @@ public function testWhereIsNull(): void { $data = $this->request ->from('user') - ->isNull('firstname') - ->fetch(); + ->isNull('firstname'); - self::assertEquals($data, [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ]); + self::assertEquals( + 'SELECT * FROM user WHERE firstname IS NULL;', + (string) $data + ); + self::assertEquals( + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ], + $data->fetch() + ); } public function testWhereIsNotNull(): void { $data = $this->request ->from('user') - ->isNotNull('firstname') - ->fetchAll(); + ->isNotNull('firstname'); - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - ]); + self::assertEquals( + 'SELECT * FROM user WHERE firstname IS NOT NULL;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + ], + $data->fetchAll() + ); } public function testWhereOrIsNull(): void @@ -438,13 +602,19 @@ public function testWhereOrIsNull(): void $data = $this->request ->from('user') ->isNull('firstname') - ->orIsNull('name') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + ->orIsNull('name'); + + self::assertEquals( + 'SELECT * FROM user WHERE firstname IS NULL OR name IS NULL;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereOrIsNotNull(): void @@ -452,17 +622,23 @@ public function testWhereOrIsNotNull(): void $data = $this->request ->from('user') ->isNull('firstname') - ->orIsNotNull('name') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + ->orIsNotNull('name'); + + self::assertEquals( + 'SELECT * FROM user WHERE firstname IS NULL OR name IS NOT NULL;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereIsNullExceptionColumn(): void @@ -479,15 +655,19 @@ public function testWhereIsNullExceptionColumn(): void * * @dataProvider whereLikeProvider */ - public function testWhereLike(string $operator, $value, array $arraySubject): void - { + public function testWhereLike( + string $operator, + $value, + string $expectedQueryStr, + array $expectedData + ): void { $data = $this->request ->select('id', 'name') ->from('user') - ->where('name', $operator, $value) - ->fetchAll(); + ->where('name', $operator, $value); - self::assertEquals($data, $arraySubject); + self::assertEquals($expectedQueryStr, (string) $data); + self::assertEquals($expectedData, $data->fetchAll()); } public function testWhereLikeId(): void @@ -504,60 +684,98 @@ public function testWhereLikeId(): void public function whereLikeProvider(): \Generator { // LIKE - yield [ 'like', 'DUP%', [ + yield [ + 'like', 'DUP%', + 'SELECT id, name FROM user WHERE name LIKE /^DUP.*$/;', + [ [ 'id' => 1, 'name' => 'DUPOND' ], [ 'id' => 4, 'name' => 'DUPOND' ] ] ]; - yield [ 'like', '%TI%', [ + yield [ + 'like', '%TI%', + 'SELECT id, name FROM user WHERE name LIKE /^.*TI.*$/;', + [ [ 'id' => 2, 'name' => 'MARTIN' ] ] ]; - yield [ 'like', 'OND', [] ]; - yield [ 'like', 'OND%', [] ]; - yield [ 'like', '%OND', [ - [ 'id' => 1, 'name' => 'DUPOND'], - [ 'id' => 4, 'name' => 'DUPOND'] + yield [ + 'like', 'OND', + 'SELECT id, name FROM user WHERE name LIKE /^OND$/;', + [] + ]; + yield [ + 'like', 'OND%', + 'SELECT id, name FROM user WHERE name LIKE /^OND.*$/;', + [] + ]; + yield [ + 'like', '%OND', + 'SELECT id, name FROM user WHERE name LIKE /^.*OND$/;', + [ + [ 'id' => 1, 'name' => 'DUPOND' ], + [ 'id' => 4, 'name' => 'DUPOND' ] ] ]; - yield [ 'like', '%OND%', [ - [ 'id' => 1, 'name' => 'DUPOND'], - [ 'id' => 4, 'name' => 'DUPOND'] + yield [ + 'like', '%OND%', + 'SELECT id, name FROM user WHERE name LIKE /^.*OND.*$/;', + [ + [ 'id' => 1, 'name' => 'DUPOND' ], + [ 'id' => 4, 'name' => 'DUPOND' ] ] ]; // ILIKE - yield [ 'ilike', 'Dup%', [ - [ 'id' => 1, 'name' => 'DUPOND'], - [ 'id' => 4, 'name' => 'DUPOND'] + yield [ + 'ilike', 'Dup%', + 'SELECT id, name FROM user WHERE name LIKE /^Dup.*$/i;', + [ + [ 'id' => 1, 'name' => 'DUPOND' ], + [ 'id' => 4, 'name' => 'DUPOND' ] ] ]; - yield [ 'ilike', '%OnD', [ + yield [ + 'ilike', '%OnD', + 'SELECT id, name FROM user WHERE name LIKE /^.*OnD$/i;', + [ [ 'id' => 1, 'name' => 'DUPOND' ], [ 'id' => 4, 'name' => 'DUPOND' ] ] ]; - yield [ 'ilike', '%ti%', [ + yield [ + 'ilike', '%ti%', + 'SELECT id, name FROM user WHERE name LIKE /^.*ti.*$/i;', + [ [ 'id' => 2, 'name' => 'MARTIN' ] ] ]; // NOT LIKE - yield [ 'not like', 'DUP%', [ + yield [ + 'not like', 'DUP%', + 'SELECT id, name FROM user WHERE name NOT LIKE /^DUP.*$/;', + [ [ 'id' => 0, 'name' => 'NOEL' ], [ 'id' => 2, 'name' => 'MARTIN' ], [ 'id' => 5, 'name' => 'MEYER' ], [ 'id' => 6, 'name' => 'ROBERT' ] ] ]; - yield [ 'not like', '%OND', [ + yield [ + 'not like', '%OND', + 'SELECT id, name FROM user WHERE name NOT LIKE /^.*OND$/;', + [ [ 'id' => 0, 'name' => 'NOEL' ], [ 'id' => 2, 'name' => 'MARTIN' ], [ 'id' => 5, 'name' => 'MEYER' ], [ 'id' => 6, 'name' => 'ROBERT' ] ] ]; - yield [ 'not like', '%E%', [ + yield [ + 'not like', '%E%', + 'SELECT id, name FROM user WHERE name NOT LIKE /^.*E.*$/;', + [ [ 'id' => 1, 'name' => 'DUPOND' ], [ 'id' => 2, 'name' => 'MARTIN' ], [ 'id' => 4, 'name' => 'DUPOND' ] @@ -565,21 +783,30 @@ public function whereLikeProvider(): \Generator ]; // NOT ILIKE - yield [ 'not ilike', 'DuP%', [ + yield [ + 'not ilike', 'DuP%', + 'SELECT id, name FROM user WHERE name NOT LIKE /^DuP.*$/i;', + [ [ 'id' => 0, 'name' => 'NOEL' ], [ 'id' => 2, 'name' => 'MARTIN' ], [ 'id' => 5, 'name' => 'MEYER' ], [ 'id' => 6, 'name' => 'ROBERT' ] ] ]; - yield [ 'not ilike', '%D', [ + yield [ + 'not ilike', '%D', + 'SELECT id, name FROM user WHERE name NOT LIKE /^.*D$/i;', + [ [ 'id' => 0, 'name' => 'NOEL' ], [ 'id' => 2, 'name' => 'MARTIN' ], [ 'id' => 5, 'name' => 'MEYER' ], [ 'id' => 6, 'name' => 'ROBERT' ] ] ]; - yield [ 'not ilike', '%E%', [ + yield [ + 'not ilike', '%E%', + 'SELECT id, name FROM user WHERE name NOT LIKE /^.*E.*$/i;', + [ [ 'id' => 1, 'name' => 'DUPOND' ], [ 'id' => 2, 'name' => 'MARTIN' ], [ 'id' => 4, 'name' => 'DUPOND' ] @@ -591,28 +818,34 @@ public function testWhereRegex(): void { $data = $this->request ->from('user') - ->regex('name', '/^D/') - ->fetchAll(); + ->regex('name', '/^D/'); - self::assertEquals($data, [ - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] - ]); + self::assertEquals('SELECT * FROM user WHERE name REGEX /^D/;', (string) $data); + self::assertEquals( + [ + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] + ], + $data->fetchAll() + ); } public function testWhereNotRegex(): void { $data = $this->request ->from('user') - ->notRegex('name', '/^D/') - ->fetchAll(); + ->notRegex('name', '/^D/'); - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + self::assertEquals('SELECT * FROM user WHERE name NOT REGEX /^D/;', (string) $data); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereOrNotRegex(): void @@ -620,14 +853,20 @@ public function testWhereOrNotRegex(): void $data = $this->request ->from('user') ->regex('name', '/^D/') - ->orNotRegex('firstname', '/^M/') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ] - ]); + ->orNotRegex('firstname', '/^M/'); + + self::assertEquals( + 'SELECT * FROM user WHERE name REGEX /^D/ OR firstname NOT REGEX /^M/;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ] + ], + $data->fetchAll() + ); } public function testWhereOrRegex(): void @@ -635,13 +874,20 @@ public function testWhereOrRegex(): void $data = $this->request ->from('user') ->regex('name', '/^D/') - ->orRegex('name', '/^N/') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] ]); + ->orRegex('name', '/^N/'); + + self::assertEquals( + 'SELECT * FROM user WHERE name REGEX /^D/ OR name REGEX /^N/;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] + ], + $data->fetchAll() + ); } public function testWhereRegexExceptionColumns(): void @@ -658,10 +904,16 @@ public function testAndWhere(): void $data = $this->request ->from('user') ->where('name', '=', 'DUPOND') - ->where('firstname', '=', 'Pierre') - ->fetch(); + ->where('firstname', '=', 'Pierre'); - self::assertEquals($data, [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ]); + self::assertEquals( + 'SELECT * FROM user WHERE name = \'DUPOND\' AND firstname = \'Pierre\';', + (string) $data + ); + self::assertEquals( + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + $data->fetch() + ); } public function testOrWhere(): void @@ -684,17 +936,23 @@ public function testOrNotWhere(): void $data = $this->request ->from('user') ->where('name', '=', 'DUPOND') - ->orNotWhere('firstname', '=', 'Mathieu') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] - ]); + ->orNotWhere('firstname', '=', 'Mathieu'); + + self::assertEquals( + 'SELECT * FROM user WHERE name = \'DUPOND\' OR NOT firstname = \'Mathieu\';', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testWhereAndGroup(): void @@ -705,13 +963,19 @@ public function testWhereAndGroup(): void ->where(static function ($query) { $query->where('name', '=', 'DUPOND') ->orWhere('firstname', '=', 'Eva'); - }) - ->fetchAll(); + }); - self::assertEquals($data, [ - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ] - ]); + self::assertEquals( + 'SELECT * FROM user WHERE id >= 2 AND (name = \'DUPOND\' OR firstname = \'Eva\');', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ] + ], + $data->fetchAll() + ); } public function testWhereOrGroup(): void @@ -722,25 +986,36 @@ public function testWhereOrGroup(): void ->orWhere(static function ($query) { $query->where('firstname', '=', 'Eva') ->orWhere('firstname', '=', 'Mathieu'); - }) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ] - ]); + }); + + self::assertEquals( + 'SELECT * FROM user WHERE name = \'DUPOND\' OR (firstname = \'Eva\' OR firstname = \'Mathieu\');', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ] + ], + $data->fetchAll() + ); } public function testLimit(): void { $data = $this->request ->from('user') - ->limit(1) - ->fetchAll(); + ->limit(1); - self::assertEquals($data, [ [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ] ]); + self::assertEquals('SELECT * FROM user LIMIT 1;', (string) $data); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ] + ], + $data->fetchAll() + ); } public function testLimitException(): void @@ -756,10 +1031,15 @@ public function testLimitOffset(): void { $data = $this->request ->from('user') - ->limit(1, 1) - ->fetchAll(); + ->limit(1, 1); - self::assertEquals($data, [ [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ] ]); + self::assertEquals('SELECT * FROM user LIMIT 1 OFFSET 1;', (string) $data); + self::assertEquals( + [ + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ] + ], + $data->fetchAll() + ); } public function testOffsetException(): void @@ -776,187 +1056,276 @@ public function testLimitOffsetWhere(): void $data = $this->request ->from('user') ->where('name', '=', 'DUPOND') - ->limit(1, 1) - ->fetchAll(); + ->limit(1, 1); - self::assertEquals($data, [ [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] ]); + self::assertEquals( + 'SELECT * FROM user WHERE name = \'DUPOND\' LIMIT 1 OFFSET 1;', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre' ] + ], + $data->fetchAll() + ); } public function testOrderByAsc(): void { - $data = $this->request->select([ 'firstname' ]) - ->from('user') - ->orderBy('firstname') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'firstname' => null ], - [ 'firstname' => 'Eva' ], - [ 'firstname' => 'Jean' ], - [ 'firstname' => 'Manon' ], - [ 'firstname' => 'Marie' ], - [ 'firstname' => 'Mathieu' ], - [ 'firstname' => 'Pierre' ] - ]); + $data = $this->request + ->select('firstname') + ->from('user') + ->orderBy('firstname'); + + self::assertEquals( + 'SELECT firstname FROM user ORDER BY firstname ASC;', + (string) $data + ); + self::assertEquals( + [ + [ 'firstname' => null ], + [ 'firstname' => 'Eva' ], + [ 'firstname' => 'Jean' ], + [ 'firstname' => 'Manon' ], + [ 'firstname' => 'Marie' ], + [ 'firstname' => 'Mathieu' ], + [ 'firstname' => 'Pierre' ] + ], + $data->fetchAll() + ); } public function testOrderByDesc(): void { - $data = $this->request->select([ 'firstname' ]) - ->from('user') - ->orderBy('firstname', SORT_DESC) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'firstname' => 'Pierre' ], - [ 'firstname' => 'Mathieu' ], - [ 'firstname' => 'Marie' ], - [ 'firstname' => 'Manon' ], - [ 'firstname' => 'Jean' ], - [ 'firstname' => 'Eva' ], - [ 'firstname' => null ] - ]); + $data = $this->request + ->select('firstname') + ->from('user') + ->orderBy('firstname', SORT_DESC); + + self::assertEquals( + 'SELECT firstname FROM user ORDER BY firstname DESC;', + (string) $data + ); + self::assertEquals( + [ + [ 'firstname' => 'Pierre' ], + [ 'firstname' => 'Mathieu' ], + [ 'firstname' => 'Marie' ], + [ 'firstname' => 'Manon' ], + [ 'firstname' => 'Jean' ], + [ 'firstname' => 'Eva' ], + [ 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testOrderByDescFetch(): void { - $data = $this->request->select([ 'name' ]) + $data = $this->request + ->select('name') ->from('user') ->where('id', '>=', 4) - ->orderBy('name', SORT_DESC) - ->fetch(); + ->orderBy('name', SORT_DESC); - self::assertEquals($data, [ 'name' => 'ROBERT' ]); + self::assertEquals( + 'SELECT name FROM user WHERE id >= 4 ORDER BY name DESC;', + (string) $data + ); + self::assertEquals( + [ 'name' => 'ROBERT' ], + $data->fetch() + ); } public function testOrderByDescLimitOffset(): void { - $data = $this->request->select([ 'name' ]) + $data = $this->request + ->select('name') ->from('user') ->where('id', '>=', 3) ->orderBy('name', SORT_DESC) - ->limit(2, 1) - ->fetchAll(); + ->limit(2, 1); - self::assertEquals($data, [ - [ 'name' => 'MEYER' ], - [ 'name' => 'DUPOND' ] - ]); + self::assertEquals( + 'SELECT name FROM user WHERE id >= 3 ORDER BY name DESC LIMIT 2 OFFSET 1;', + (string) $data + ); + self::assertEquals( + [ + [ 'name' => 'MEYER' ], + [ 'name' => 'DUPOND' ] + ], + $data->fetchAll() + ); } public function testOrderByMultipleAsc(): void { - $data = $this->request->select([ 'name', 'firstname' ]) + $data = $this->request + ->select('name', 'firstname') ->from('user') ->orderBy('name', SORT_DESC) - ->orderBy('firstname') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'name' => 'ROBERT', 'firstname' => null ], - [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'name' => null, 'firstname' => 'Marie' ] - ]); + ->orderBy('firstname'); + + self::assertEquals( + 'SELECT name, firstname FROM user ORDER BY name DESC, firstname ASC;', + (string) $data + ); + self::assertEquals( + [ + [ 'name' => 'ROBERT', 'firstname' => null ], + [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'name' => null, 'firstname' => 'Marie' ] + ], + $data->fetchAll() + ); } public function testOrderByMultipleDesc(): void { - $data = $this->request->select([ 'name', 'firstname' ]) + $data = $this->request + ->select('name', 'firstname') ->from('user') ->orderBy('name', SORT_DESC) - ->orderBy('firstname', SORT_DESC) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'name' => 'ROBERT', 'firstname' => null ], - [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'name' => null, 'firstname' => 'Marie' ] - ]); + ->orderBy('firstname', SORT_DESC); + + self::assertEquals( + 'SELECT name, firstname FROM user ORDER BY name DESC, firstname DESC;', + (string) $data + ); + self::assertEquals( + [ + [ 'name' => 'ROBERT', 'firstname' => null ], + [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'name' => null, 'firstname' => 'Marie' ] + ], + $data->fetchAll() + ); } public function testRightJoin(): void { - $data = $this->request->select('id', 'name', 'firstname', 'labelle') + $data = $this->request + ->select('id', 'name', 'firstname', 'labelle') ->from('role') ->rightJoin('user_role', 'id_role', '=', 'user_role.id_role') - ->rightJoin('user', 'id_user', '=', 'user.id') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu', 'labelle' => 'Admin' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean', 'labelle' => 'Admin' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon', 'labelle' => 'Author' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie', 'labelle' => 'Author' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre', 'labelle' => 'User' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva', 'labelle' => 'User' ], - /* Pas de corespondance pour ROBERT donc le reste des colonnes en null */ - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null, 'labelle' => null ], - ]); + ->rightJoin('user', 'id_user', '=', 'user.id'); + + self::assertEquals( + 'SELECT id, name, firstname, labelle FROM role ' + . 'RIGHT JOIN user_role ON id_role = \'user_role.id_role\' ' + . 'RIGHT JOIN user ON id_user = \'user.id\';', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu', 'labelle' => 'Admin' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean', 'labelle' => 'Admin' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon', 'labelle' => 'Author' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie', 'labelle' => 'Author' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre', 'labelle' => 'User' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva', 'labelle' => 'User' ], + /* Pas de corespondance pour ROBERT donc le reste des colonnes en null */ + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null, 'labelle' => null ], + ], + $data->fetchAll() + ); } public function testLeftJoin(): void { - $data = $this->request->select('id', 'name', 'firstname', 'labelle') + $data = $this->request + ->select('id', 'name', 'firstname', 'labelle') ->from('user') ->leftJoin('user_role', 'id', '=', 'user_role.id_user') - ->leftJoin('role', 'id_role', '=', 'role.id_role') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu', 'labelle' => 'Admin' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean', 'labelle' => 'Admin' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon', 'labelle' => 'Author' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie', 'labelle' => 'Author' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre', 'labelle' => 'User' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva', 'labelle' => 'User' ], - /* Pas de corespondance pour ROBERT donc le reste des colonnes en null */ - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null, 'labelle' => null ], - ]); + ->leftJoin('role', 'id_role', '=', 'role.id_role'); + + self::assertEquals( + 'SELECT id, name, firstname, labelle FROM user ' + . 'LEFT JOIN user_role ON id = \'user_role.id_user\' ' + . 'LEFT JOIN role ON id_role = \'role.id_role\';', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu', 'labelle' => 'Admin' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean', 'labelle' => 'Admin' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon', 'labelle' => 'Author' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie', 'labelle' => 'Author' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre', 'labelle' => 'User' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva', 'labelle' => 'User' ], + /* Pas de corespondance pour ROBERT donc le reste des colonnes en null */ + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null, 'labelle' => null ], + ], + $data->fetchAll() + ); } public function testLeftJoinWhere(): void { - $data = $this->request->select('id', 'name', 'firstname') + $data = $this->request + ->select('id', 'name', 'firstname') ->from('user') ->leftJoin('user_role', 'id', '=', 'user_role.id_user') ->leftJoin('role', 'id_role', '=', 'role.id_role') - ->where('labelle', '=', 'Admin') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - ]); + ->where('labelle', '=', 'Admin'); + + self::assertEquals( + 'SELECT id, name, firstname FROM user ' + . 'LEFT JOIN user_role ON id = \'user_role.id_user\' ' + . 'LEFT JOIN role ON id_role = \'role.id_role\' ' + . 'WHERE labelle = \'Admin\';', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + ], + $data->fetchAll() + ); } public function testLeftJoinGroup(): void { - $data = $this->request->select('id', 'name', 'firstname') + $data = $this->request + ->select('id', 'name', 'firstname') ->from('user') ->leftJoin('user_role', static function ($query) { $query->where('id', '=', 'user_role.id_user'); }) ->leftJoin('role', 'id_role', '=', 'role.id_role') - ->where('labelle', '=', 'Admin') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - ]); + ->where('labelle', '=', 'Admin'); + + self::assertEquals( + 'SELECT id, name, firstname FROM user ' + . 'LEFT JOIN user_role ON id = \'user_role.id_user\' ' + . 'LEFT JOIN role ON id_role = \'role.id_role\' ' + . 'WHERE labelle = \'Admin\';', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + ], + $data->fetchAll() + ); } public function testLeftJoinGroupMultiple(): void { - $data = $this->request->select('id', 'name', 'firstname', 'labelle') + $data = $this->request + ->select('id', 'name', 'firstname', 'labelle') ->from('user') ->leftJoin('user_role', 'id', '=', 'user_role.id_user') ->leftJoin('role', static function ($query) { @@ -964,41 +1333,60 @@ public function testLeftJoinGroupMultiple(): void $query->where('id_role', '=', 'role.id_role'); }); }) - ->where('labelle', '=', 'Admin') - ->fetchAll(); - - self::assertEquals($data, [ - [ 'labelle' => 'Admin', 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'labelle' => 'Admin', 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], - ]); + ->where('labelle', '=', 'Admin'); + + self::assertEquals( + 'SELECT id, name, firstname, labelle FROM user ' + . 'LEFT JOIN user_role ON id = \'user_role.id_user\' ' + . 'LEFT JOIN role ON (id_role = \'role.id_role\') ' + . 'WHERE labelle = \'Admin\';', + (string) $data + ); + self::assertEquals( + [ + [ 'labelle' => 'Admin', 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'labelle' => 'Admin', 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ], + ], + $data->fetchAll() + ); } public function testRightJoinGroupe(): void { - $data = $this->request->select('id', 'name', 'firstname', 'labelle') + $data = $this->request + ->select('id', 'name', 'firstname', 'labelle') ->from('role') ->rightJoin('user_role', 'id_role', '=', 'user_role.id_role') ->rightJoin('user', static function ($query) { $query->where('id_user', '=', 'user.id'); - }) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu', 'labelle' => 'Admin' ], - [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean', 'labelle' => 'Admin' ], - [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon', 'labelle' => 'Author' ], - [ 'id' => 3, 'name' => null, 'firstname' => 'Marie', 'labelle' => 'Author' ], - [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre', 'labelle' => 'User' ], - [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva', 'labelle' => 'User' ], - /* Pas de corespondance pour ROBERT donc le reste des colonnes en null */ - [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null, 'labelle' => null ], - ]); + }); + + self::assertEquals( + 'SELECT id, name, firstname, labelle FROM role ' + . 'RIGHT JOIN user_role ON id_role = \'user_role.id_role\' ' + . 'RIGHT JOIN user ON id_user = \'user.id\';', + (string) $data + ); + self::assertEquals( + [ + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu', 'labelle' => 'Admin' ], + [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean', 'labelle' => 'Admin' ], + [ 'id' => 2, 'name' => 'MARTIN', 'firstname' => 'Manon', 'labelle' => 'Author' ], + [ 'id' => 3, 'name' => null, 'firstname' => 'Marie', 'labelle' => 'Author' ], + [ 'id' => 4, 'name' => 'DUPOND', 'firstname' => 'Pierre', 'labelle' => 'User' ], + [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva', 'labelle' => 'User' ], + /* Pas de corespondance pour ROBERT donc le reste des colonnes en null */ + [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null, 'labelle' => null ], + ], + $data->fetchAll() + ); } public function testLeftJoinExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); - $this->request->select([ 'id', 'name', 'firstname' ]) + $this->request + ->select('id', 'name', 'firstname') ->from('user') ->leftJoin('user_role', 'foo', '==', 'user_role.id_user') ->leftJoin('role', 'id_role', '==', 'role.id') @@ -1016,17 +1404,25 @@ public function testUnion(): void $data = $this->secondRequest ->select('name') ->from('user') - ->union($union) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'name' => 'NOEL' ], - [ 'name' => 'DUPOND' ], - [ 'name' => 'MARTIN' ], - [ 'name' => null ], - [ 'name' => 'MEYER' ], - [ 'name' => 'ROBERT' ] - ]); + ->union($union); + + self::assertEquals( + 'SELECT name FROM user ' + . 'UNION ' + . 'SELECT name FROM user WHERE id BETWEEN 1 AND 5;', + (string) $data + ); + self::assertEquals( + [ + [ 'name' => 'NOEL' ], + [ 'name' => 'DUPOND' ], + [ 'name' => 'MARTIN' ], + [ 'name' => null ], + [ 'name' => 'MEYER' ], + [ 'name' => 'ROBERT' ] + ], + $data->fetchAll() + ); } public function testUnionMultiple(): void @@ -1039,18 +1435,26 @@ public function testUnionMultiple(): void $data = $this->secondRequest ->select('name', 'firstname') ->from('user') - ->union($union) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'name' => null, 'firstname' => 'Marie' ], - [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'name' => 'ROBERT', 'firstname' => null ] - ]); + ->union($union); + + self::assertEquals( + 'SELECT name, firstname FROM user ' + . 'UNION ' + . 'SELECT name, firstname FROM user WHERE id BETWEEN 1 AND 5;', + (string) $data + ); + self::assertEquals( + [ + [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'name' => null, 'firstname' => 'Marie' ], + [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'name' => 'ROBERT', 'firstname' => null ] + ], + $data->fetchAll() + ); } public function testUnionMultipleException(): void @@ -1078,23 +1482,31 @@ public function testUnionAll(): void $data = $this->secondRequest ->select('name') ->from('user') - ->unionAll($union) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'name' => 'NOEL' ], - [ 'name' => 'DUPOND' ], - [ 'name' => 'MARTIN' ], - [ 'name' => null ], - [ 'name' => 'DUPOND' ], - [ 'name' => 'MEYER' ], - [ 'name' => 'ROBERT' ], - [ 'name' => 'DUPOND' ], - [ 'name' => 'MARTIN' ], - [ 'name' => null ], - [ 'name' => 'DUPOND' ], - [ 'name' => 'MEYER' ] - ]); + ->unionAll($union); + + self::assertEquals( + 'SELECT name FROM user ' + . 'UNION ALL ' + . 'SELECT name FROM user WHERE id BETWEEN 1 AND 5;', + (string) $data + ); + self::assertEquals( + [ + [ 'name' => 'NOEL' ], + [ 'name' => 'DUPOND' ], + [ 'name' => 'MARTIN' ], + [ 'name' => null ], + [ 'name' => 'DUPOND' ], + [ 'name' => 'MEYER' ], + [ 'name' => 'ROBERT' ], + [ 'name' => 'DUPOND' ], + [ 'name' => 'MARTIN' ], + [ 'name' => null ], + [ 'name' => 'DUPOND' ], + [ 'name' => 'MEYER' ] + ], + $data->fetchAll() + ); } public function testUnionAllMultiple(): void @@ -1107,23 +1519,31 @@ public function testUnionAllMultiple(): void $data = $this->secondRequest ->select('name', 'firstname') ->from('user') - ->unionAll($union) - ->fetchAll(); - - self::assertEquals($data, [ - [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'name' => null, 'firstname' => 'Marie' ], - [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'name' => 'MEYER', 'firstname' => 'Eva' ], - [ 'name' => 'ROBERT', 'firstname' => null ], - [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], - [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], - [ 'name' => null, 'firstname' => 'Marie' ], - [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], - [ 'name' => 'MEYER', 'firstname' => 'Eva' ] - ]); + ->unionAll($union); + + self::assertEquals( + 'SELECT name, firstname FROM user ' + . 'UNION ALL ' + . 'SELECT name, firstname FROM user WHERE id BETWEEN 1 AND 5;', + (string) $data + ); + self::assertEquals( + [ + [ 'name' => 'NOEL', 'firstname' => 'Mathieu' ], + [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'name' => null, 'firstname' => 'Marie' ], + [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'name' => 'MEYER', 'firstname' => 'Eva' ], + [ 'name' => 'ROBERT', 'firstname' => null ], + [ 'name' => 'DUPOND', 'firstname' => 'Jean' ], + [ 'name' => 'MARTIN', 'firstname' => 'Manon' ], + [ 'name' => null, 'firstname' => 'Marie' ], + [ 'name' => 'DUPOND', 'firstname' => 'Pierre' ], + [ 'name' => 'MEYER', 'firstname' => 'Eva' ] + ], + $data->fetchAll() + ); } public function testUnionAllMultipleException(): void @@ -1151,17 +1571,25 @@ public function testUnionList(): void $data = $this->secondRequest ->select('name') ->from('user') - ->union($union) - ->lists('name'); - - self::assertEquals($data, [ - 'NOEL', - 'DUPOND', - 'MARTIN', - null, - 'MEYER', - 'ROBERT' - ]); + ->union($union); + + self::assertEquals( + 'SELECT name FROM user ' + . 'UNION ' + . 'SELECT name FROM user WHERE id BETWEEN 1 AND 5;', + (string) $data + ); + self::assertEquals( + [ + 'NOEL', + 'DUPOND', + 'MARTIN', + null, + 'MEYER', + 'ROBERT' + ], + $data->lists('name') + ); } public function testUnionListOrder(): void @@ -1175,17 +1603,26 @@ public function testUnionListOrder(): void ->select('name') ->from('user') ->union($union) - ->orderBy('name') - ->lists('name'); - - self::assertEquals($data, [ - null, - 'DUPOND', - 'MARTIN', - 'MEYER', - 'NOEL', - 'ROBERT' - ]); + ->orderBy('name'); + + self::assertEquals( + 'SELECT name FROM user ' + . 'UNION ' + . 'SELECT name FROM user WHERE id BETWEEN 1 AND 5 ' + . 'ORDER BY name ASC;', + (string) $data + ); + self::assertEquals( + [ + null, + 'DUPOND', + 'MARTIN', + 'MEYER', + 'NOEL', + 'ROBERT' + ], + $data->lists('name') + ); } public function testPredicate(): void From 106faa5343c25c9ff70e7793c327b8b9325a0f29 Mon Sep 17 00:00:00 2001 From: noelma Date: Thu, 11 Nov 2021 14:48:42 +0100 Subject: [PATCH 05/12] refactor: move the tests to the unit directory --- composer.json | 2 +- tests/{ => unit}/Driver/IgbinaryTest.php | 2 +- tests/{ => unit}/Driver/JsonTest.php | 2 +- tests/{ => unit}/Driver/MsgPackTest.php | 2 +- tests/{ => unit}/Driver/TxtTest.php | 2 +- tests/{ => unit}/RequestExecuteTest.php | 2 +- tests/{ => unit}/RequestTest.php | 4 ++-- tests/{ => unit}/SchemaJsonTest.php | 2 +- tests/{ => unit}/TableAlterTest.php | 2 +- tests/{ => unit}/TableBuilderTest.php | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) rename tests/{ => unit}/Driver/IgbinaryTest.php (98%) rename tests/{ => unit}/Driver/JsonTest.php (98%) rename tests/{ => unit}/Driver/MsgPackTest.php (98%) rename tests/{ => unit}/Driver/TxtTest.php (98%) rename tests/{ => unit}/RequestExecuteTest.php (99%) rename tests/{ => unit}/RequestTest.php (99%) rename tests/{ => unit}/SchemaJsonTest.php (99%) rename tests/{ => unit}/TableAlterTest.php (97%) rename tests/{ => unit}/TableBuilderTest.php (99%) diff --git a/composer.json b/composer.json index f20368e..26e6e48 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ }, "autoload-dev": { "psr-4": { - "Queryflatfile\\Test\\": "tests" + "Queryflatfile\\Tests\\": "tests" } }, "config": { diff --git a/tests/Driver/IgbinaryTest.php b/tests/unit/Driver/IgbinaryTest.php similarity index 98% rename from tests/Driver/IgbinaryTest.php rename to tests/unit/Driver/IgbinaryTest.php index 525ccf5..94e8587 100644 --- a/tests/Driver/IgbinaryTest.php +++ b/tests/unit/Driver/IgbinaryTest.php @@ -1,6 +1,6 @@ Date: Thu, 11 Nov 2021 14:56:10 +0100 Subject: [PATCH 06/12] fix: add a return and a type of parameter to anonymous functions --- tests/unit/RequestExecuteTest.php | 6 +++--- tests/unit/RequestTest.php | 13 ++++++------ tests/unit/SchemaJsonTest.php | 34 +++++++++++++++---------------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/tests/unit/RequestExecuteTest.php b/tests/unit/RequestExecuteTest.php index 4df7388..67f6a29 100644 --- a/tests/unit/RequestExecuteTest.php +++ b/tests/unit/RequestExecuteTest.php @@ -55,16 +55,16 @@ protected function setUp(): void public function testCreateTable(): void { - $this->bdd->createTable('user', static function (TableBuilder $table) { + $this->bdd->createTable('user', static function (TableBuilder $table): void { $table->increments('id') ->string('name')->nullable() ->string('firstname')->nullable(); }); - $this->bdd->createTable('user_role', static function (TableBuilder $table) { + $this->bdd->createTable('user_role', static function (TableBuilder $table): void { $table->integer('id_user') ->integer('id_role'); }); - $this->bdd->createTable('role', static function (TableBuilder $table) { + $this->bdd->createTable('role', static function (TableBuilder $table): void { $table->increments('id_role') ->string('labelle'); }); diff --git a/tests/unit/RequestTest.php b/tests/unit/RequestTest.php index b0a039c..5467349 100644 --- a/tests/unit/RequestTest.php +++ b/tests/unit/RequestTest.php @@ -10,6 +10,7 @@ use Queryflatfile\Exception\Query\TableNotFoundException; use Queryflatfile\Request; use Queryflatfile\Schema; +use Queryflatfile\WhereHandler; class RequestTest extends \PHPUnit\Framework\TestCase { @@ -960,7 +961,7 @@ public function testWhereAndGroup(): void $data = $this->request ->from('user') ->where('id', '>=', 2) - ->where(static function ($query) { + ->where(static function (WhereHandler $query): void { $query->where('name', '=', 'DUPOND') ->orWhere('firstname', '=', 'Eva'); }); @@ -983,7 +984,7 @@ public function testWhereOrGroup(): void $data = $this->request ->from('user') ->where('name', '=', 'DUPOND') - ->orWhere(static function ($query) { + ->orWhere(static function (WhereHandler $query): void { $query->where('firstname', '=', 'Eva') ->orWhere('firstname', '=', 'Mathieu'); }); @@ -1300,7 +1301,7 @@ public function testLeftJoinGroup(): void $data = $this->request ->select('id', 'name', 'firstname') ->from('user') - ->leftJoin('user_role', static function ($query) { + ->leftJoin('user_role', static function (WhereHandler $query): void { $query->where('id', '=', 'user_role.id_user'); }) ->leftJoin('role', 'id_role', '=', 'role.id_role') @@ -1328,8 +1329,8 @@ public function testLeftJoinGroupMultiple(): void ->select('id', 'name', 'firstname', 'labelle') ->from('user') ->leftJoin('user_role', 'id', '=', 'user_role.id_user') - ->leftJoin('role', static function ($query) { - $query->where(static function ($query) { + ->leftJoin('role', static function (WhereHandler $query): void { + $query->where(static function (WhereHandler $query): void { $query->where('id_role', '=', 'role.id_role'); }); }) @@ -1357,7 +1358,7 @@ public function testRightJoinGroupe(): void ->select('id', 'name', 'firstname', 'labelle') ->from('role') ->rightJoin('user_role', 'id_role', '=', 'user_role.id_role') - ->rightJoin('user', static function ($query) { + ->rightJoin('user', static function (WhereHandler $query): void { $query->where('id_user', '=', 'user.id'); }); diff --git a/tests/unit/SchemaJsonTest.php b/tests/unit/SchemaJsonTest.php index a926a2d..73c4bdd 100644 --- a/tests/unit/SchemaJsonTest.php +++ b/tests/unit/SchemaJsonTest.php @@ -29,7 +29,7 @@ protected function setUp(): void $this->request = new Request($this->bdd); - $this->bdd->createTableIfNotExists('test', static function (TableBuilder $table) { + $this->bdd->createTableIfNotExists('test', static function (TableBuilder $table): void { $table->increments('id') ->string('name') ->string('firstname'); @@ -41,7 +41,7 @@ protected function setUp(): void ->values([ 'MARTIN', 'Manon' ]) ->execute(); - $this->bdd->createTable('test_second', static function (TableBuilder $table) { + $this->bdd->createTable('test_second', static function (TableBuilder $table): void { $table->integer('value_i') ->string('value_s'); }); @@ -131,7 +131,7 @@ public function testSetIncrementsException(): void public function testAlterTableAdd(): void { - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table ->string('field_s_default')->valueDefault('foo') ->string('field_s_null')->nullable() @@ -174,7 +174,7 @@ public function testAlterTableAdd(): void public function testAlterTableAddIncrement(): void { - $this->bdd->alterTable('test_second', static function (TableAlter $table) { + $this->bdd->alterTable('test_second', static function (TableAlter $table): void { $table->increments('id'); }); @@ -206,7 +206,7 @@ public function testAlterTableAddIncrement(): void public function testAlterTableRename(): void { - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->renameColumn('name', '_name'); }); @@ -234,7 +234,7 @@ public function testAlterTableRename(): void public function testAlterTableModify(): void { - $this->bdd->alterTable('test_second', static function (TableAlter $table) { + $this->bdd->alterTable('test_second', static function (TableAlter $table): void { $table->float('value_i')->valueDefault(1.0)->modify(); }); @@ -251,7 +251,7 @@ public function testAlterTableModify(): void public function testAlterTableModifyIncrement(): void { - $this->bdd->alterTable('test_second', static function (TableAlter $table) { + $this->bdd->alterTable('test_second', static function (TableAlter $table): void { $table->increments('value_i')->modify(); }); @@ -271,7 +271,7 @@ public function testAlterTableModifyIncrement(): void public function testAlterTableDrop(): void { - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->dropColumn('id') ->dropColumn('firstname'); }); @@ -299,7 +299,7 @@ public function testAlterTableException(): void public function testAlterTableAddException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->string('id'); }); } @@ -307,7 +307,7 @@ public function testAlterTableAddException(): void public function testAlterTableModifyException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->string('error')->modify(); }); } @@ -315,7 +315,7 @@ public function testAlterTableModifyException(): void public function testAlterTableModifyTypeIntegerException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test_second', static function (TableAlter $table) { + $this->bdd->alterTable('test_second', static function (TableAlter $table): void { $table->integer('value_s')->modify(); }); } @@ -323,7 +323,7 @@ public function testAlterTableModifyTypeIntegerException(): void public function testAlterTableModifyTypeStringException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test_second', static function (TableAlter $table) { + $this->bdd->alterTable('test_second', static function (TableAlter $table): void { $table->string('value_i')->modify(); }); } @@ -331,7 +331,7 @@ public function testAlterTableModifyTypeStringException(): void public function testAlterTableModifyColumnsValueException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->increments('name')->modify(); }); } @@ -339,7 +339,7 @@ public function testAlterTableModifyColumnsValueException(): void public function testAlterTableRenameColumnsNotFoundException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->renameColumn('error', 'error'); }); } @@ -347,7 +347,7 @@ public function testAlterTableRenameColumnsNotFoundException(): void public function testAlterTableRenameException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->renameColumn('name', 'id'); }); } @@ -355,7 +355,7 @@ public function testAlterTableRenameException(): void public function testAlterTableDropException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->dropColumn('error'); }); } @@ -363,7 +363,7 @@ public function testAlterTableDropException(): void public function testAlterTableAddIncrementsException(): void { $this->expectException(\Exception::class); - $this->bdd->alterTable('test', static function (TableAlter $table) { + $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->increments('error'); }); } From 72397f410d323918e63482fd73d94c120712359f Mon Sep 17 00:00:00 2001 From: noelma Date: Sat, 13 Nov 2021 18:55:29 +0100 Subject: [PATCH 07/12] test: exception message --- src/Driver.php | 10 ++-- .../Driver/FileNotFoundException.php | 7 +++ src/Exception/Exception.php | 14 ----- .../Query/TableNotFoundException.php | 13 +++++ src/Request.php | 43 ++++++++++---- src/Schema.php | 57 +++++++++++++------ src/TableAlter.php | 4 +- src/TableBuilder.php | 19 +++++-- src/Where.php | 4 +- src/WhereHandler.php | 4 +- tests/unit/Driver/IgbinaryTest.php | 2 + tests/unit/Driver/JsonTest.php | 2 + tests/unit/Driver/MsgPackTest.php | 2 + tests/unit/Driver/TxtTest.php | 2 + tests/unit/RequestExecuteTest.php | 6 ++ tests/unit/RequestTest.php | 32 +++++++++-- tests/unit/SchemaJsonTest.php | 21 ++++++- tests/unit/TableAlterTest.php | 3 + tests/unit/TableBuilderTest.php | 47 +++++++++++++++ 19 files changed, 230 insertions(+), 62 deletions(-) diff --git a/src/Driver.php b/src/Driver.php index ec17bf9..87a8fb2 100644 --- a/src/Driver.php +++ b/src/Driver.php @@ -70,7 +70,7 @@ public function create(string $path, string $fileName, array $data = []): bool $handle = fopen($file, 'w+'); if ($handle === false) { - throw new DriverException("$file file cannot be opened"); + throw new DriverException(sprintf('The %s file cannot be opened', $file)); } fwrite($handle, $this->serializeData($data)); @@ -108,7 +108,7 @@ public function save(string $path, string $fileName, array $data): bool $handle = fopen($file, 'w'); if ($handle === false) { - throw new DriverException("$file file cannot be opened"); + throw new DriverException(sprintf('The %s file cannot be opened', $file)); } fwrite($handle, $this->serializeData($data)); @@ -158,7 +158,7 @@ public function getFile(string $path, string $fileName): string protected function isExist(string $file): void { if (!file_exists($file)) { - throw new FileNotFoundException("The $file file is missing."); + throw new FileNotFoundException(sprintf('The %s file is missing.', $file)); } } @@ -176,7 +176,7 @@ protected function isExist(string $file): void protected function isWrite(string $file): void { if (!\is_writable($file)) { - throw new FileNotWritableException("The $file file is not writable."); + throw new FileNotWritableException(sprintf('The %s file is not writable.', $file)); } } @@ -194,7 +194,7 @@ protected function isWrite(string $file): void protected function isRead(string $file): void { if (!\is_readable($file)) { - throw new FileNotReadableException("The $file file is not readable."); + throw new FileNotReadableException(sprintf('The %s file is not readable.', $file)); } } } diff --git a/src/Exception/Driver/FileNotFoundException.php b/src/Exception/Driver/FileNotFoundException.php index f0752e3..27084cd 100644 --- a/src/Exception/Driver/FileNotFoundException.php +++ b/src/Exception/Driver/FileNotFoundException.php @@ -15,4 +15,11 @@ */ class FileNotFoundException extends DriverException { + public function __construct( + string $message = '', + int $code = 0, + \Throwable $previous = null + ) { + parent::__construct(str_replace('\\', '/', $message), $code, $previous); + } } diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index ae91d83..13ebe55 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -17,18 +17,4 @@ */ class Exception extends \Exception { - /** - * Les balises autorisées. - * - * @var string - */ - protected $balise = ''; - - public function __construct(string $message = '', int $code = 0, \Throwable $previous = null) - { - $msgEntities = htmlentities($message, ENT_QUOTES, 'UTF-8'); - $msgDecode = htmlspecialchars_decode($msgEntities); - $msgTags = strip_tags($msgDecode, $this->balise); - parent::__construct($msgTags, $code, $previous); - } } diff --git a/src/Exception/Query/TableNotFoundException.php b/src/Exception/Query/TableNotFoundException.php index 7e8a459..f316ad6 100644 --- a/src/Exception/Query/TableNotFoundException.php +++ b/src/Exception/Query/TableNotFoundException.php @@ -15,4 +15,17 @@ */ class TableNotFoundException extends QueryException { + public function __construct( + string $tableName = '', + int $code = 0, + \Throwable $previous = null + ) { + parent::__construct( + $tableName === '' + ? 'Table is missing.' + : sprintf('The %s table is missing.', $tableName), + $code, + $previous + ); + } } diff --git a/src/Request.php b/src/Request.php index 0920458..a561ff8 100644 --- a/src/Request.php +++ b/src/Request.php @@ -156,7 +156,7 @@ public function __call(string $name, array $args): self $this->where = $this->where ?? new Where(); if (!method_exists($this->where, $name)) { - throw new BadMethodCallException("The $name method not exist"); + throw new BadMethodCallException(sprintf('The %s method is missing.', $name)); } $this->where->$name(...$args); @@ -488,13 +488,25 @@ protected function executeInsert(): void foreach ($this->values as $values) { /* Pour chaque ligne je vérifie si le nombre de colonne correspond au nombre valeur insérée. */ if ($count !== count($values)) { - throw new ColumnsNotFoundException('keys : ' . implode(',', $this->columns) . ' != ' . implode(',', $values)); + throw new ColumnsNotFoundException( + sprintf( + 'The number of fields in the selections are different: %s != %s', + implode(', ', $this->columns), + implode(', ', $values) + ) + ); } /* Je prépare l'association clé=>valeur pour chaque ligne à insérer. */ $row = array_combine($this->columns, $values); if ($row == false) { - throw new ColumnsNotFoundException('keys : ' . implode(',', $this->columns) . ' != ' . implode(',', $values)); + throw new ColumnsNotFoundException( + sprintf( + 'The number of fields in the selections are different: %s != %s', + implode(', ', $this->columns), + implode(', ', $values) + ) + ); } $data = []; @@ -595,7 +607,7 @@ private function loadAllColumnsSchema(): void private function filterFrom(): void { if (empty($this->from)) { - throw new TableNotFoundException("Table {$this->from} is missing."); + throw new TableNotFoundException(); } } @@ -662,7 +674,9 @@ private function filterOrderBy(): void foreach ($this->orderBy as $field => $order) { if ($order !== SORT_ASC && $order !== SORT_DESC) { - throw new OperatorNotFound("The sort type of the $field field is not valid."); + throw new OperatorNotFound( + sprintf('The sort type of the %s field is not valid.', $field) + ); } } } @@ -677,10 +691,13 @@ private function filterUnion(): void $count = count($this->columns); foreach ($this->union as $request) { if ($count != count($request[ 'request' ]->columns)) { - throw new ColumnsNotFoundException('The number of fields in the selections are different : ' - . implode(',', $this->columns) - . ' != ' - . implode(',', $request[ 'request' ]->columns)); + throw new ColumnsNotFoundException( + sprintf( + 'The number of fields in the selections are different: %s != %s', + implode(', ', $this->columns), + implode(', ', $request[ 'request' ]->columns) + ) + ); } } } @@ -703,7 +720,13 @@ private function diffColumns(array $columns): void if ($diff) { $columnsDiff = array_flip($diff); - throw new ColumnsNotFoundException('Column ' . implode(',', $columnsDiff) . ' is absent : ' . $this); + throw new ColumnsNotFoundException( + sprintf( + 'Column %s is absent: %s', + implode(',', $columnsDiff), + $this + ) + ); } } diff --git a/src/Schema.php b/src/Schema.php index eb2ad8d..e94a2be 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -144,11 +144,13 @@ public function setPathRoot(string $root = ''): self public function setIncrement(string $table, int $increment): bool { if (!$this->hasTable($table)) { - throw new TableNotFoundException("Table $table is not exist."); + throw new TableNotFoundException($table); } if (!isset($this->schema[ $table ][ 'increments' ])) { - throw new Exception("Table $table does not have an incremental value."); + throw new Exception( + sprintf('Table %s does not have an incremental value.', $table) + ); } $this->schema[ $table ][ 'increments' ] = $increment; @@ -169,11 +171,13 @@ public function setIncrement(string $table, int $increment): bool public function getIncrement(string $table): int { if (!$this->hasTable($table)) { - throw new TableNotFoundException("Table $table is not exist."); + throw new TableNotFoundException($table); } if ($this->schema[ $table ][ 'increments' ] === null) { - throw new Exception("Table $table does not have an incremental value."); + throw new Exception( + sprintf('Table %s does not have an incremental value.', $table) + ); } return $this->schema[ $table ][ 'increments' ]; @@ -207,7 +211,7 @@ public function getSchema(): array public function getSchemaTable(string $table): array { if (!$this->hasTable($table)) { - throw new TableNotFoundException("The $table table is missing in the schema."); + throw new TableNotFoundException($table); } return $this->getSchema()[ $table ]; @@ -256,7 +260,7 @@ public function dropSchema(): self public function createTable(string $table, ?callable $callback = null): self { if ($this->hasTable($table)) { - throw new Exception("Table $table exist."); + throw new Exception(sprintf('Table %s exist.', $table)); } $builder = self::tableBuilder($callback); @@ -355,7 +359,9 @@ public static function getValueDefault(string $name, array &$params) return null; } - throw new ColumnsValueException("$name not nullable or not default."); + throw new ColumnsValueException( + sprintf('%s not nullable or not default.', $name) + ); } /** @@ -395,7 +401,7 @@ public function hasColumn(string $table, string $column): bool public function truncateTable(string $table): bool { if (!$this->hasTable($table)) { - throw new TableNotFoundException("Table $table is not exist."); + throw new TableNotFoundException($table); } $deleteSchema = true; @@ -421,7 +427,7 @@ public function truncateTable(string $table): bool public function dropTable(string $table): bool { if (!$this->hasTable($table)) { - throw new TableNotFoundException("Table $table is not exist."); + throw new TableNotFoundException($table); } unset($this->schema[ $table ]); @@ -695,11 +701,16 @@ private static function filterFieldAdd( ): void { /* Si un champ est ajouté il ne doit pas exister dans le schéma. */ if (isset($fields[ $name ])) { - throw new Exception("$name field does not exists in $table table."); + throw new Exception( + sprintf('%s field does not exists in %s table.', $name, $table) + ); } if ($type === TableBuilder::TYPE_INCREMENT && self::isFieldIncrement($fields)) { throw new ColumnsValueException( - "The $table table can not have multiple incremental values." + sprintf( + 'The %s table can not have multiple incremental values.', + $table + ) ); } } @@ -722,12 +733,17 @@ private static function filterFieldModify( ): void { if (!isset($fields[ $name ])) { throw new ColumnsNotFoundException( - "$name field does not exists in $table table." + sprintf( + "%s field does not exists in %s table.", $name, $table + ) ); } if ($type === TableBuilder::TYPE_INCREMENT && self::isFieldIncrement($fields)) { throw new ColumnsValueException( - "The $table table can not have multiple incremental values." + sprintf( + 'The %s table can not have multiple incremental values.', + $table + ) ); } @@ -740,7 +756,14 @@ private static function filterFieldModify( !in_array($fields[ $name ][ 'type' ], [ 'date', 'datetime', 'string', 'text' ]); if ($modifyString || $modifyNumber || $modifyDate) { - throw new Exception("The $name column type {$fields[ $name ][ 'type' ]} can not be changed with the $type type"); + throw new Exception( + sprintf( + 'The %s column type %s can not be changed with the %s type.', + $name, + $fields[ $name ][ 'type' ], + $type + ) + ); } } @@ -761,13 +784,13 @@ private static function filterFieldRename( ): void { if (!isset($fields[ $name ])) { throw new ColumnsNotFoundException( - "$name field does not exists in $table table." + sprintf('%s field does not exists in %s table.', $name, $table) ); } /* Si le champ à renommer existe dans le schema. */ if (isset($fields[ $to ])) { throw new Exception( - "$name field does exists in $table table." + sprintf('%s field does exists in %s table.', $name, $table) ); } } @@ -788,7 +811,7 @@ private static function filterFieldDrop( ): void { if (!isset($fields[ $name ])) { throw new ColumnsNotFoundException( - "$name field does not exists in $table table." + sprintf('%s field does not exists in %s table.', $name, $table) ); } } diff --git a/src/TableAlter.php b/src/TableAlter.php index d531b94..6bdaa84 100644 --- a/src/TableAlter.php +++ b/src/TableAlter.php @@ -84,7 +84,9 @@ protected function checkPreviousBuild(string $opt): array { $current = parent::checkPreviousBuild($opt); if (isset($current[ 'opt' ])) { - throw new ColumnsNotFoundException("No column selected for $opt."); + throw new ColumnsNotFoundException( + sprintf('No column selected for %s.', $opt) + ); } return $current; diff --git a/src/TableBuilder.php b/src/TableBuilder.php index 0f1eae4..c4a894f 100644 --- a/src/TableBuilder.php +++ b/src/TableBuilder.php @@ -251,7 +251,9 @@ public function unsigned(): self { $current = $this->checkPreviousBuild('unsigned'); if ($current[ 'type' ] !== self::TYPE_INT) { - throw new ColumnsValueException("Impossiblie of unsigned type {$current[ 'type' ]} only integer)."); + throw new ColumnsValueException( + sprintf('Impossiblie of unsigned type %s only integer.', $current[ 'type' ]) + ); } $this->builder[ key($this->builder) ][ 'unsigned' ] = true; @@ -300,7 +302,12 @@ public function valueDefault($value): self */ public static function filterValue(string $name, string $type, $value, array $args = []) { - $error = 'The default value (' . $value . ') for column ' . $name . ' does not correspond to type ' . $type . '.'; + $error = sprintf( + 'The default value (%s) for column %s does not correspond to type %s.', + $value, + $name, + $type + ); switch (strtolower($type)) { case self::TYPE_STRING: @@ -357,7 +364,9 @@ public static function filterValue(string $name, string $type, $value, array $ar throw new ColumnsValueException($error); default: - throw new ColumnsValueException("Type $type not supported"); + throw new ColumnsValueException( + sprintf('Type %s not supported', $type) + ); } return $value; @@ -401,7 +410,9 @@ protected function checkPreviousBuild(string $opt): array { $current = end($this->builder); if (!$current) { - throw new ColumnsNotFoundException("No column selected for $opt."); + throw new ColumnsNotFoundException( + sprintf('No column selected for %s.', $opt) + ); } return $current; diff --git a/src/Where.php b/src/Where.php index 452a82f..ac275ee 100644 --- a/src/Where.php +++ b/src/Where.php @@ -216,7 +216,9 @@ public static function predicate($columns, string $operator, $value): bool return $columns >= $value[ 'min' ] && $columns <= $value[ 'max' ]; } - throw new OperatorNotFound("The $operator operator is not supported."); + throw new OperatorNotFound( + sprintf('The %s operator is not supported.', $operator) + ); } /** diff --git a/src/WhereHandler.php b/src/WhereHandler.php index 3d68ccc..2edd346 100644 --- a/src/WhereHandler.php +++ b/src/WhereHandler.php @@ -429,7 +429,9 @@ private function filterOperator(?string $operator): string $condition = strtolower($operator); if (!in_array($condition, self::CONDITION)) { - throw new OperatorNotFound("The condition $operator is not exist."); + throw new OperatorNotFound( + sprintf('The condition %s is not exist.', $operator) + ); } return $condition; diff --git a/tests/unit/Driver/IgbinaryTest.php b/tests/unit/Driver/IgbinaryTest.php index 94e8587..44f2858 100644 --- a/tests/unit/Driver/IgbinaryTest.php +++ b/tests/unit/Driver/IgbinaryTest.php @@ -71,6 +71,7 @@ public function testRead(): void public function testReadException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/igbinary/driver_test_error.ig file is missing.'); $this->driver->read(self::TEST_DIR, 'driver_test_error'); } @@ -90,6 +91,7 @@ public function testSave(): void public function testSaveException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/igbinary/driver_test_error.ig file is missing.'); $this->driver->save(self::TEST_DIR, 'driver_test_error', []); } diff --git a/tests/unit/Driver/JsonTest.php b/tests/unit/Driver/JsonTest.php index 481d16a..f086929 100644 --- a/tests/unit/Driver/JsonTest.php +++ b/tests/unit/Driver/JsonTest.php @@ -71,6 +71,7 @@ public function testRead(): void public function testReadException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/json/driver_test_error.json file is missing.'); $this->driver->read(self::TEST_DIR, 'driver_test_error'); } @@ -90,6 +91,7 @@ public function testSave(): void public function testSaveException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/json/driver_test_error.json file is missing.'); $this->driver->save(self::TEST_DIR, 'driver_test_error', []); } diff --git a/tests/unit/Driver/MsgPackTest.php b/tests/unit/Driver/MsgPackTest.php index 5aab314..087e688 100644 --- a/tests/unit/Driver/MsgPackTest.php +++ b/tests/unit/Driver/MsgPackTest.php @@ -71,6 +71,7 @@ public function testRead(): void public function testReadException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/msgpack/driver_test_error.msg file is missing.'); $this->driver->read(self::TEST_DIR, 'driver_test_error'); } @@ -90,6 +91,7 @@ public function testSave(): void public function testSaveException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/msgpack/driver_test_error.msg file is missing.'); $this->driver->save(self::TEST_DIR, 'driver_test_error', []); } diff --git a/tests/unit/Driver/TxtTest.php b/tests/unit/Driver/TxtTest.php index 6c0bc71..3405b3c 100644 --- a/tests/unit/Driver/TxtTest.php +++ b/tests/unit/Driver/TxtTest.php @@ -66,6 +66,7 @@ public function testRead(): void public function testReadException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/txt/driver_test_error.txt file is missing.'); $this->driver->read(self::TEST_DIR, 'driver_test_error'); } @@ -85,6 +86,7 @@ public function testSave(): void public function testSaveException(): void { $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('tests/txt/driver_test_error.txt file is missing.'); $this->driver->save(self::TEST_DIR, 'driver_test_error', []); } diff --git a/tests/unit/RequestExecuteTest.php b/tests/unit/RequestExecuteTest.php index 67f6a29..fff2cfc 100644 --- a/tests/unit/RequestExecuteTest.php +++ b/tests/unit/RequestExecuteTest.php @@ -77,6 +77,7 @@ public function testCreateTable(): void public function testCreateTableException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('Table user exist.'); $this->bdd->createTable('user'); } @@ -132,12 +133,14 @@ public function testGetIncrement(): void public function testGetIncrementNoFound(): void { $this->expectException(TableNotFoundException::class); + $this->expectExceptionMessage('The error table is missing.'); $this->bdd->getIncrement('error'); } public function testGetIncrementNoExist(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('Table user_role does not have an incremental value.'); $this->bdd->getIncrement('user_role'); } @@ -151,18 +154,21 @@ public function testCreateTableIfNotExistsData(): void public function testInsertIntoExceptionTable(): void { $this->expectException(TableNotFoundException::class); + $this->expectExceptionMessage('The foo table is missing.'); $this->request->insertInto('foo', [ 'id', 'name', 'firstname' ])->execute(); } public function testInsertIntoExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('The number of fields in the selections are different: != 0, NOEL'); $this->request->insertInto('user', [])->values([ 0, 'NOEL' ])->execute(); } public function testInsertIntoExceptionValue(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('The number of fields in the selections are different: id, name, firstname != 0, NOEL'); $this->request->insertInto('user', [ 'id', 'name', 'firstname' ]) ->values([ 0, 'NOEL' ]) ->execute(); diff --git a/tests/unit/RequestTest.php b/tests/unit/RequestTest.php index 5467349..6dc0cd2 100644 --- a/tests/unit/RequestTest.php +++ b/tests/unit/RequestTest.php @@ -134,27 +134,24 @@ public function testListsVoid(): void public function testSelectExceptionValue(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('Column foo is absent: SELECT foo FROM user LIMIT 1;'); $this->request->select('foo')->from('user')->fetch(); } public function testSelectExceptionFrom(): void { $this->expectException(TableNotFoundException::class); + $this->expectExceptionMessage('The foo table is missing.'); $this->request->select('firstname')->from('foo')->fetch(); } public function testFromException(): void { $this->expectException(TableNotFoundException::class); + $this->expectExceptionMessage('Table is missing.'); $this->request->select('firstname')->fetch(); } - public function testSelectAlternativeExceptionFrom(): void - { - $this->expectException(TableNotFoundException::class); - $this->request->from('foo')->fetch(); - } - /** * @param numeric $value * @@ -207,6 +204,7 @@ public function whereEqualsProvider(): \Generator public function testWhereOperatorException(): void { $this->expectException(OperatorNotFound::class); + $this->expectExceptionMessage('The condition error is not exist.'); $this->request ->select('name') ->from('user') @@ -365,6 +363,7 @@ public function whereOperatorProvider(): \Generator public function testWhereEqualsExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('Column foo is absent: SELECT name FROM user WHERE foo = \'Jean\' LIMIT 1;'); $this->request ->select('name') ->from('user') @@ -458,6 +457,7 @@ public function testWhereOrNotBetween(): void public function testWhereBetweenExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('Column foo is absent: SELECT * FROM user WHERE foo BETWEEN 0 AND 2 LIMIT 1;'); $this->request ->from('user') ->between('foo', 0, 2) @@ -553,6 +553,7 @@ public function testWhereOrNotIn(): void public function testWhereInExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('Column foo is absent: SELECT * FROM user WHERE foo IN 0, 1;'); $this->request ->from('user') ->in('foo', [ 0, 1 ]) @@ -645,6 +646,7 @@ public function testWhereOrIsNotNull(): void public function testWhereIsNullExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('Column foo is absent: SELECT * FROM user WHERE foo IS NULL LIMIT 1;'); $this->request ->from('user') ->isNull('foo') @@ -894,6 +896,7 @@ public function testWhereOrRegex(): void public function testWhereRegexExceptionColumns(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage('Column foo is absent: SELECT * FROM user WHERE foo REGEX /^D/ LIMIT 1;'); $this->request ->from('user') ->regex('foo', '/^D/') @@ -1022,6 +1025,7 @@ public function testLimit(): void public function testLimitException(): void { $this->expectException(QueryException::class); + $this->expectExceptionMessage('The limit must be a non-zero positive integer.'); $this->request ->from('user') ->limit(-1) @@ -1046,6 +1050,7 @@ public function testLimitOffset(): void public function testOffsetException(): void { $this->expectException(QueryException::class); + $this->expectExceptionMessage('The offset must be a non-zero positive integer.'); $this->request ->from('user') ->limit(1, -1) @@ -1386,6 +1391,14 @@ public function testRightJoinGroupe(): void public function testLeftJoinExceptionColumn(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage( + 'Column foo is absent: ' + . 'SELECT id, name, firstname FROM user ' + . 'LEFT JOIN user_role ON foo == \'user_role.id_user\' ' + . 'LEFT JOIN role ON id_role == \'role.id\' ' + . 'WHERE labelle = \'Admin\' ' + . 'LIMIT 1;' + ); $this->request ->select('id', 'name', 'firstname') ->from('user') @@ -1461,6 +1474,9 @@ public function testUnionMultiple(): void public function testUnionMultipleException(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage( + 'The number of fields in the selections are different: name, firstname != name' + ); $union = $this->request ->select('name') ->from('user') @@ -1550,6 +1566,9 @@ public function testUnionAllMultiple(): void public function testUnionAllMultipleException(): void { $this->expectException(ColumnsNotFoundException::class); + $this->expectExceptionMessage( + 'The number of fields in the selections are different: name, firstname != name' + ); $union = $this->request ->select('name') ->from('user') @@ -1629,6 +1648,7 @@ public function testUnionListOrder(): void public function testPredicate(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The error operator is not supported.'); \Queryflatfile\Where::predicate('', 'error', ''); } diff --git a/tests/unit/SchemaJsonTest.php b/tests/unit/SchemaJsonTest.php index 73c4bdd..d46a4f6 100644 --- a/tests/unit/SchemaJsonTest.php +++ b/tests/unit/SchemaJsonTest.php @@ -3,6 +3,7 @@ namespace Queryflatfile\Tests\unit; use Queryflatfile\Driver\Json; +use Queryflatfile\Exception\Query\TableNotFoundException; use Queryflatfile\Request; use Queryflatfile\Schema; use Queryflatfile\TableAlter; @@ -117,16 +118,18 @@ public function testHasColumns(): void self::assertFalse($this->bdd->hasColumn('error', 'error')); } - public function testSetIncrementsableNotFoundException(): void + public function testSetIncrementsTableNotFoundException(): void { - $this->expectException(\Exception::class); + $this->expectException(TableNotFoundException::class); + $this->expectExceptionMessage('The error table is missing.'); $this->bdd->setIncrement('error', 1); } public function testSetIncrementsException(): void { $this->expectException(\Exception::class); - $this->bdd->setIncrement('test_void', 1); + $this->expectExceptionMessage('Table test_second does not have an incremental value.'); + $this->bdd->setIncrement('test_second', 1); } public function testAlterTableAdd(): void @@ -292,6 +295,7 @@ public function testAlterTableDrop(): void public function testAlterTableException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The error table is missing.'); $this->bdd->alterTable('error', static function () { }); } @@ -299,6 +303,7 @@ public function testAlterTableException(): void public function testAlterTableAddException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('id field does not exists in test table.'); $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->string('id'); }); @@ -307,6 +312,7 @@ public function testAlterTableAddException(): void public function testAlterTableModifyException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('error field does not exists in test table.'); $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->string('error')->modify(); }); @@ -315,6 +321,7 @@ public function testAlterTableModifyException(): void public function testAlterTableModifyTypeIntegerException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The value_s column type string can not be changed with the integer type.'); $this->bdd->alterTable('test_second', static function (TableAlter $table): void { $table->integer('value_s')->modify(); }); @@ -323,6 +330,7 @@ public function testAlterTableModifyTypeIntegerException(): void public function testAlterTableModifyTypeStringException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The value_i column type integer can not be changed with the string type.'); $this->bdd->alterTable('test_second', static function (TableAlter $table): void { $table->string('value_i')->modify(); }); @@ -331,6 +339,7 @@ public function testAlterTableModifyTypeStringException(): void public function testAlterTableModifyColumnsValueException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The test table can not have multiple incremental values.'); $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->increments('name')->modify(); }); @@ -339,6 +348,7 @@ public function testAlterTableModifyColumnsValueException(): void public function testAlterTableRenameColumnsNotFoundException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('error field does not exists in test table.'); $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->renameColumn('error', 'error'); }); @@ -347,6 +357,7 @@ public function testAlterTableRenameColumnsNotFoundException(): void public function testAlterTableRenameException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('name field does exists in test table.'); $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->renameColumn('name', 'id'); }); @@ -355,6 +366,7 @@ public function testAlterTableRenameException(): void public function testAlterTableDropException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('error field does not exists in test table.'); $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->dropColumn('error'); }); @@ -363,6 +375,7 @@ public function testAlterTableDropException(): void public function testAlterTableAddIncrementsException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The test table can not have multiple incremental values.'); $this->bdd->alterTable('test', static function (TableAlter $table): void { $table->increments('error'); }); @@ -387,6 +400,7 @@ public function testTruncateTable(): void public function testTruncateTableException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The error table is missing.'); $this->bdd->truncateTable('error'); } @@ -401,6 +415,7 @@ public function testDropTable(): void public function testDropTableException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('The test table is missing.'); $this->bdd->dropTable('test'); $this->bdd->dropTable('test'); } diff --git a/tests/unit/TableAlterTest.php b/tests/unit/TableAlterTest.php index 152ea98..1035fa8 100644 --- a/tests/unit/TableAlterTest.php +++ b/tests/unit/TableAlterTest.php @@ -46,18 +46,21 @@ public function testModify(): void public function testDropException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('No column selected for value default.'); $this->object->dropColumn('0')->valueDefault('test'); } public function testRenameException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('No column selected for value default.'); $this->object->renameColumn('0', '1')->valueDefault('test'); } public function testModifyException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('No column selected for value default.'); $this->object->char('0')->modify()->valueDefault('test'); } } diff --git a/tests/unit/TableBuilderTest.php b/tests/unit/TableBuilderTest.php index 0c66376..b5ae367 100644 --- a/tests/unit/TableBuilderTest.php +++ b/tests/unit/TableBuilderTest.php @@ -28,6 +28,9 @@ public function testIncrements(): void public function testIncrementsException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'Only one incremental column is allowed per table.' + ); $this->object ->increments('id') ->increments('error'); @@ -48,6 +51,9 @@ public function testChar(): void public function testCharException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The length passed in parameter is not of numeric type.' + ); $this->object->char('id2', -1); } @@ -75,6 +81,9 @@ public function testString(): void public function testStringException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The length passed in parameter is not of numeric type.' + ); $this->object->string('id', -1); } @@ -152,6 +161,7 @@ public function testNullable(): void public function testNullableException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('No column selected for nullable.'); $this->object->nullable(); } @@ -167,12 +177,16 @@ public function testUnsigned(): void public function testUnsignedException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('No column selected for unsigned.'); $this->object->unsigned(); } public function testUnsignedTypeException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'Impossiblie of unsigned type string only integer.' + ); $this->object->string('id')->unsigned(); } @@ -188,6 +202,7 @@ public function testComment(): void public function testCommentException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('No column selected for comment.'); $this->object->comment('identifiant'); } @@ -224,72 +239,104 @@ public function testValueDefault(): void public function testValueDefaultException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('No column selected for value default.'); $this->object->valueDefault('1'); } public function testValueDefaultIncrementException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'An incremental type column can not have a default value.' + ); $this->object->increments('0')->valueDefault(2); } public function testValueDefaultCharException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (1) for column 0 does not correspond to type char.' + ); $this->object->char('0')->valueDefault(1); } public function testValueDefaultCharLenghtException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value is larger than the specified size.' + ); $this->object->char('0')->valueDefault('error'); } public function testValueDefaultTextException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (1) for column 0 does not correspond to type text.' + ); $this->object->text('0')->valueDefault(1); } public function testValueDefaultStringException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (1) for column 0 does not correspond to type string.' + ); $this->object->string('0')->valueDefault(1); } public function testValueDefaultIntegerException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (error) for column 0 does not correspond to type integer.' + ); $this->object->integer('0')->valueDefault('error'); } public function testValueDefaultFloatException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (error) for column 0 does not correspond to type float.' + ); $this->object->float('0')->valueDefault('error'); } public function testValueDefaultBoolException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (1) for column 0 does not correspond to type boolean.' + ); $this->object->boolean('0')->valueDefault('1'); } public function testValueDefaultDateException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (1) for column 0 does not correspond to type date.' + ); $this->object->date('0')->valueDefault('1'); } public function testValueDefaultDatetimesException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'The default value (1) for column 0 does not correspond to type datetime.' + ); $this->object->datetime('0')->valueDefault('1'); } public function testCheckValueException(): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage('Type error not supported'); TableBuilder::filterValue('testName', 'error', 'testValue'); } } From 67d09241ef37e80f63b7c5199f58b5123f5fbf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20NO=C3=8BL?= Date: Sun, 14 Nov 2021 19:05:39 +0100 Subject: [PATCH 08/12] feat: add GitHub actions (#44) * feat: add github actions * fix: php8.1 tests * fix: phpstan and phpcs for ci --- .gitattributes | 3 +- .github/workflows/github-actions.yml | 49 +++++++++++++++++++++ .php-cs-fixer.dist.php | 2 - .travis.yml | 29 ------------- README.md | 2 +- README_fr.md | 2 +- phpstan.neon.dist | 2 +- src/Request.php | 64 ++++++++++++++++------------ src/RequestInterface.php | 6 +-- src/Schema.php | 4 +- src/Where.php | 25 ++++++++--- src/WhereHandler.php | 10 ++--- tests/unit/RequestTest.php | 18 ++++---- 13 files changed, 128 insertions(+), 88 deletions(-) create mode 100644 .github/workflows/github-actions.yml delete mode 100644 .travis.yml diff --git a/.gitattributes b/.gitattributes index 9d344da..19fb15a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,7 @@ /tests export-ignore .coveralls.yml export-ignore .gitattributes export-ignore +.github export-ignore .gitignore export-ignore .php-cs-fixer.dist.php export-ignore .travis.yml export-ignore @@ -11,4 +12,4 @@ phpunit.xml export-ignore phpunit.xml.dist export-ignore phpstan.neon.dist export-ignore USAGE.md export-ignore -README_fr.md export-ignore \ No newline at end of file +README_fr.md export-ignore diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml new file mode 100644 index 0000000..998489a --- /dev/null +++ b/.github/workflows/github-actions.yml @@ -0,0 +1,49 @@ +name: Tests + +on: [push, pull_request] + +jobs: + tests: + name: Tests PHP ${{ matrix.php }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + php: [7.2, 7.3, 7.4, 8.0, 8.1] + experimental: [false] + include: + - php: 7.4 + analysis: true + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: xdebug + + - name: Install dependencies with Composer + uses: ramsey/composer-install@v1 + + - name: Coding standards + if: matrix.analysis + run: bin/php-cs-fixer fix --ansi --dry-run --using-cache=no --verbose + + - name: Static analysis + if: matrix.analysis + run: bin/phpstan --memory-limit=1G analyse + + - name: Unit tests + run: bin/phpunit --coverage-clover clover.xml + + - name: Upload coverage results to Coveralls + if: matrix.analysis + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + composer require php-coveralls/php-coveralls -n -W + bin/php-coveralls --coverage_clover=clover.xml -v diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 5fd064a..118ad1a 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -78,8 +78,6 @@ 'phpdoc_no_access' => true, /* Ordonne les annotations PHPDoc. */ 'phpdoc_order' => true, - /* Les docblocks ne doivent être utilisés que sur des éléments structurels. */ - 'phpdoc_to_comment' => true, /* Supprime les lignes vides au début et fin de la PHPDoc. */ 'phpdoc_trim' => true, /* Les variables locales, dynamiques et directement référencées ne doivent pas être affectées ni directement renvoyées par une fonction ou une méthode. */ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index fd2fde7..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: php -sudo: required - -matrix: - include: - - php: 7.2 - - php: 7.3 - - php: 7.4 - env: - - ANALYSIS='true' - - XDEBUG_MODE=coverage - - php: 8.0 - -before_script: - - if [[ "$ANALYSIS" == 'true' ]]; then pecl install -f igbinary ; fi - - if [[ "$ANALYSIS" == 'true' ]]; then pecl install -f msgpack ; fi - - if [[ "$ANALYSIS" == 'true' ]]; then composer require php-coveralls/php-coveralls:^2.2.0 ; fi - - composer install -n - -script: - - if [[ "$ANALYSIS" == 'true' ]]; then XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover clover.xml ; fi - - if [[ "$ANALYSIS" != 'true' ]]; then vendor/bin/phpunit --no-coverage --testsuite=standard ; fi - - vendor/bin/phpstan --memory-limit=1G - -after_success: -- if [[ "$ANALYSIS" == 'true' ]]; then vendor/bin/php-coveralls --coverage_clover=clover.xml -v ; fi - -notifications: - email: false \ No newline at end of file diff --git a/README.md b/README.md index 175a653..2ecda36 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Queryflatfile -[![Build Status](https://travis-ci.org/soosyze/queryflatfile.svg?branch=master)](https://travis-ci.org/soosyze/queryflatfile "Travis") +[![Build Status](https://github.com/soosyze/queryflatfile/workflows/Tests/badge.svg?branch=master)](https://github.com/soosyze/queryflatfile/actions?query=branch:master "Tests") [![Coverage Status](https://coveralls.io/repos/github/soosyze/queryflatfile/badge.svg?branch=master)](https://coveralls.io/github/soosyze/queryflatfile?branch=master "Coveralls") [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/soosyze/queryflatfile/blob/master/LICENSE "LICENSE") [![Packagist](https://img.shields.io/packagist/v/soosyze/queryflatfile.svg)](https://packagist.org/packages/soosyze/queryflatfile "Packagist") diff --git a/README_fr.md b/README_fr.md index b11b992..f440d87 100644 --- a/README_fr.md +++ b/README_fr.md @@ -1,6 +1,6 @@ # Queryflatfile -[![Build Status](https://travis-ci.org/soosyze/queryflatfile.svg?branch=master)](https://travis-ci.org/soosyze/queryflatfile "Travis") +[![Build Status](https://github.com/soosyze/queryflatfile/workflows/Tests/badge.svg?branch=master)](https://github.com/soosyze/queryflatfile/actions?query=branch:master "Tests") [![Coverage Status](https://coveralls.io/repos/github/soosyze/queryflatfile/badge.svg?branch=master)](https://coveralls.io/github/soosyze/queryflatfile?branch=master "Coveralls") [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/soosyze/queryflatfile/blob/master/LICENSE "LICENSE") [![Packagist](https://img.shields.io/packagist/v/soosyze/queryflatfile.svg)](https://packagist.org/packages/soosyze/queryflatfile "Packagist") diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 53f82fb..8acad65 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,4 +7,4 @@ parameters: - src - tests tmpDir: build/phpStan - checkMissingIterableValueType: false \ No newline at end of file + checkMissingIterableValueType: false diff --git a/src/Request.php b/src/Request.php index a561ff8..eac9cb8 100644 --- a/src/Request.php +++ b/src/Request.php @@ -23,30 +23,30 @@ * * @author Mathieu NOËL * - * @method Request where( callable|string $column, null|string $operator = null, mixed $value = null ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notWhere( callable|string $column, null|string $operator = null, mixed $value = null ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orWhere( callable|string $column, null|string $operator = null, mixed $value = null ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotWhere( callable|string $column, null|string $operator = null, mixed $value = null ) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request where(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notWhere(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orWhere(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotWhere(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where * - * @method Request between( string $column, mixed $min, mixed $max ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orBetween( string $column, mixed $min, mixed $max ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notBetween( string $column, mixed $min, mixed $max ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotBetween( string $column, mixed $min, mixed $max ) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request between(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orBetween(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notBetween(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotBetween(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where * - * @method Request in( string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orIn( string $column, array $values ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notIn( string $column, array $values ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotIn( string $column, array $values ) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request in(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where * - * @method Request isNull( string $column, string $condition = '===' ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orIsNull( string $column ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request isNotNull( string $column ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orIsNotNull( string $column ) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request isNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orIsNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request isNotNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orIsNotNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where * - * @method Request regex( string $column, string $pattern ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orRegex( string $column, string $pattern ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notRegex( string $column, string $pattern ) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotRegex( string $column, string $pattern ) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request regex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where */ class Request extends RequestHandler { @@ -106,13 +106,18 @@ public function __toString(): string if ($this->execute) { $output .= strtoupper($this->execute) . ' '; } else { - $output .= sprintf('SELECT %s ', $this->columns ? implode(', ', $this->columns) : '*'); + $output .= sprintf('SELECT %s ', $this->columns ? addslashes(implode(', ', $this->columns)) : '*'); } if ($this->from) { - $output .= sprintf('FROM %s ', $this->from); + $output .= sprintf('FROM %s ', addslashes($this->from)); } foreach ($this->joins as $value) { - $output .= sprintf('%s JOIN %s ON %s ', strtoupper($value[ 'type' ]), $value[ 'table' ], (string) $value[ 'where' ]); + $output .= sprintf( + '%s JOIN %s ON %s ', + strtoupper($value[ 'type' ]), + addslashes($value[ 'table' ]), + (string) $value[ 'where' ] + ); } if ($this->where) { $output .= sprintf('WHERE %s ', (string) $this->where); @@ -125,19 +130,22 @@ public function __toString(): string if ($this->orderBy) { $output .= 'ORDER BY '; foreach ($this->orderBy as $field => $order) { - $output .= sprintf('%s %s, ', $field, $order === SORT_ASC ? 'ASC' : 'DESC'); + $output .= sprintf( + '%s %s, ', + addslashes($field), + $order === SORT_ASC ? 'ASC' : 'DESC' + ); } $output = trim($output, ', ') . ' '; } if ($this->limit) { - $output .= sprintf('LIMIT %s ', (string) $this->limit); + $output .= sprintf('LIMIT %d ', (string) $this->limit); } if ($this->offset) { - $output .= sprintf('OFFSET %s ', (string) $this->offset); + $output .= sprintf('OFFSET %d ', (string) $this->offset); } - $output = trim($output) . ';'; - return htmlspecialchars($output); + return trim($output) . ';'; } /** diff --git a/src/RequestInterface.php b/src/RequestInterface.php index 9bd0c3a..64362fa 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -157,8 +157,8 @@ public function delete(); * Enregistre une union 'simple' entre 2 ensembles. * Le résultat de l'union ne possède pas de doublon de ligne. * - * @param \Queryflatfile\Request $request Seconde requête. - * @param string $type (simple|all) Type d'union. + * @param RequestInterface $request Seconde requête. + * @param string $type (simple|all) Type d'union. * * @return $this */ @@ -168,7 +168,7 @@ public function union(RequestInterface $request, string $type = self::UNION_SIMP * Enregistre une union all entre 2 ensembles. * Les doublons de lignes figure dans le resultat de l'union. * - * @param \Queryflatfile\Request $request + * @param RequestInterface $request * * @return $this */ diff --git a/src/Schema.php b/src/Schema.php index e94a2be..4447414 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -734,7 +734,9 @@ private static function filterFieldModify( if (!isset($fields[ $name ])) { throw new ColumnsNotFoundException( sprintf( - "%s field does not exists in %s table.", $name, $table + '%s field does not exists in %s table.', + $name, + $table ) ); } diff --git a/src/Where.php b/src/Where.php index ac275ee..f8bf7d7 100644 --- a/src/Where.php +++ b/src/Where.php @@ -39,20 +39,25 @@ public function __toString(): string case 'where': $value = \is_int($where[ 'value' ]) ? $where[ 'value' ] - : "'{$where[ 'value' ]}'"; - $output .= sprintf('%s%s %s %s ', $not, $where[ 'column' ], $where[ 'condition' ], $value); + : sprintf('\'%s\'', addslashes($where[ 'value' ])); + $output .= sprintf('%s%s %s %s ', $not, addslashes($where[ 'column' ]), $where[ 'condition' ], $value); break; case 'like': - $output .= sprintf('%s %sLIKE %s ', $where[ 'column' ], $not, $where[ 'value' ]); + $output .= sprintf('%s %sLIKE %s ', addslashes($where[ 'column' ]), $not, addslashes($where[ 'value' ])); break; case 'isNull': - $output .= sprintf('%s IS %sNULL ', $where[ 'column' ], $not); + $output .= sprintf('%s IS %sNULL ', addslashes($where[ 'column' ]), $not); break; case 'in': - $output .= sprintf('%s %sIN %s ', $where[ 'column' ], $not, implode(', ', $where[ 'value' ])); + $output .= sprintf( + '%s %sIN %s ', + addslashes($where[ 'column' ]), + $not, + addslashes(implode(', ', $where[ 'value' ])) + ); break; case 'whereCallback': @@ -60,11 +65,17 @@ public function __toString(): string break; case 'between': - $output .= sprintf('%s %sBETWEEN %s AND %s ', $where[ 'column' ], $not, $where[ 'value' ][ 'min' ], $where[ 'value' ][ 'max' ]); + $output .= sprintf( + '%s %sBETWEEN %s AND %s ', + addslashes($where[ 'column' ]), + $not, + addslashes((string) $where[ 'value' ][ 'min' ]), + addslashes((string) $where[ 'value' ][ 'max' ]) + ); break; case 'regex': - $output .= sprintf('%s %sREGEX %s ', $where[ 'column' ], $not, $where[ 'value' ]); + $output .= sprintf('%s %sREGEX %s ', addslashes($where[ 'column' ]), $not, $where[ 'value' ]); break; } diff --git a/src/WhereHandler.php b/src/WhereHandler.php index 2edd346..6a35644 100644 --- a/src/WhereHandler.php +++ b/src/WhereHandler.php @@ -49,7 +49,7 @@ class WhereHandler * Ajoute une condition simple pour la requête. * Si la valeur du champ est égal (non égale, supérieur à, ...) par rapport à une valeur. * - * @param callable|string $column Sous condition ou une colonne. + * @param \Closure|string $column Sous condition ou une colonne. * @param null|string $operator Type de condition. * @param null|numeric|string $value Valeur de teste. * @param string $bool Porte logique de la condition (and|or). @@ -64,7 +64,7 @@ public function where( string $bool = self::EXP_AND, bool $not = false ): self { - if (\is_callable($column)) { + if ($column instanceof \Closure) { $this->whereCallback($column, $bool, $not); return $this; @@ -97,7 +97,7 @@ public function where( /** * Alias inverse de la fonction where(). * - * @param callable|string $column Sous condition ou une colonne. + * @param \Closure|string $column Sous condition ou une colonne. * @param mixed $value Valeur de teste. */ public function notWhere($column, ?string $operator = null, $value = null): self @@ -110,7 +110,7 @@ public function notWhere($column, ?string $operator = null, $value = null): self /** * Alias avec la porte logique 'OR' de la fonction where(). * - * @param callable|string $column Sous condition ou une colonne. + * @param \Closure|string $column Sous condition ou une colonne. * @param mixed $value Valeur de teste. */ public function orWhere($column, ?string $operator = null, $value = null): self @@ -123,7 +123,7 @@ public function orWhere($column, ?string $operator = null, $value = null): self /** * Alias inverse avec la porte logique 'OR' de la fonction where(). * - * @param callable|string $column Sous condition ou une colonne. + * @param \Closure|string $column Sous condition ou une colonne. * @param mixed $value Valeur de teste. */ public function orNotWhere($column, ?string $operator = null, $value = null): self diff --git a/tests/unit/RequestTest.php b/tests/unit/RequestTest.php index 6dc0cd2..7ed52f3 100644 --- a/tests/unit/RequestTest.php +++ b/tests/unit/RequestTest.php @@ -246,11 +246,11 @@ public function whereNotEqualsProvider(): \Generator { yield [ '<>', '1', - 'SELECT id FROM user WHERE id <> \'1\';' + 'SELECT id FROM user WHERE id <> \'1\';' ]; yield [ '<>', 1, - 'SELECT id FROM user WHERE id <> 1;' + 'SELECT id FROM user WHERE id <> 1;' ]; yield [ '!=', '1', @@ -330,14 +330,14 @@ public function whereOperatorProvider(): \Generator { yield [ '<', 1, - 'SELECT * FROM user WHERE id < 1;', + 'SELECT * FROM user WHERE id < 1;', [ [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ] ] ]; yield [ '<=', 1, - 'SELECT * FROM user WHERE id <= 1;', + 'SELECT * FROM user WHERE id <= 1;', [ [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], [ 'id' => 1, 'name' => 'DUPOND', 'firstname' => 'Jean' ] @@ -345,14 +345,14 @@ public function whereOperatorProvider(): \Generator ]; yield [ '>', 5, - 'SELECT * FROM user WHERE id > 5;', + 'SELECT * FROM user WHERE id > 5;', [ [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] ] ]; yield [ '>=', 5, - 'SELECT * FROM user WHERE id >= 5;', + 'SELECT * FROM user WHERE id >= 5;', [ [ 'id' => 5, 'name' => 'MEYER', 'firstname' => 'Eva' ], [ 'id' => 6, 'name' => 'ROBERT', 'firstname' => null ] @@ -970,7 +970,7 @@ public function testWhereAndGroup(): void }); self::assertEquals( - 'SELECT * FROM user WHERE id >= 2 AND (name = \'DUPOND\' OR firstname = \'Eva\');', + 'SELECT * FROM user WHERE id >= 2 AND (name = \'DUPOND\' OR firstname = \'Eva\');', (string) $data ); self::assertEquals( @@ -1135,7 +1135,7 @@ public function testOrderByDescFetch(): void ->orderBy('name', SORT_DESC); self::assertEquals( - 'SELECT name FROM user WHERE id >= 4 ORDER BY name DESC;', + 'SELECT name FROM user WHERE id >= 4 ORDER BY name DESC;', (string) $data ); self::assertEquals( @@ -1154,7 +1154,7 @@ public function testOrderByDescLimitOffset(): void ->limit(2, 1); self::assertEquals( - 'SELECT name FROM user WHERE id >= 3 ORDER BY name DESC LIMIT 2 OFFSET 1;', + 'SELECT name FROM user WHERE id >= 3 ORDER BY name DESC LIMIT 2 OFFSET 1;', (string) $data ); self::assertEquals( From c2f471c6176b227de4ac8bd283ee97bad4bac38a Mon Sep 17 00:00:00 2001 From: noelma Date: Sun, 21 Nov 2021 19:40:01 +0100 Subject: [PATCH 09/12] feat: update phpstan 1.1 and fix data types --- composer.json | 4 ++-- src/Driver/Igbinary.php | 14 +++++++++++-- src/Driver/Json.php | 16 ++++++++++----- src/Driver/Txt.php | 7 ++++++- src/Request.php | 16 +++++++-------- src/RequestHandler.php | 17 ++++------------ src/RequestInterface.php | 4 ++-- src/Schema.php | 2 +- src/TableBuilder.php | 20 +++++++++++++------ src/Where.php | 14 +++++++------ src/WhereHandler.php | 34 +++++++++++++++---------------- tests/unit/RequestTest.php | 41 ++++++++++++-------------------------- 12 files changed, 98 insertions(+), 91 deletions(-) diff --git a/composer.json b/composer.json index 26e6e48..4563dd9 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,8 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", - "phpstan/phpstan": "^0.12.85", - "phpstan/phpstan-phpunit": "^0.12.18", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^8.5" }, "suggest": { diff --git a/src/Driver/Igbinary.php b/src/Driver/Igbinary.php index d448e7e..5005a38 100644 --- a/src/Driver/Igbinary.php +++ b/src/Driver/Igbinary.php @@ -42,7 +42,12 @@ public function getExtension(): string */ public function serializeData(array $data): string { - return igbinary_serialize($data); + $serializeData = igbinary_serialize($data); + if (!is_string($serializeData)) { + throw new \Exception('An error occurred in serializing the data.'); + } + + return $serializeData; } /** @@ -50,6 +55,11 @@ public function serializeData(array $data): string */ public function unserializeData(string $data): array { - return igbinary_unserialize($data); + $dataUnserialize = igbinary_unserialize($data); + if (!is_array($dataUnserialize)) { + throw new \Exception('An error occurred in deserializing the data.'); + } + + return $dataUnserialize; } } diff --git a/src/Driver/Json.php b/src/Driver/Json.php index fbb5ab7..1d070f4 100644 --- a/src/Driver/Json.php +++ b/src/Driver/Json.php @@ -42,11 +42,12 @@ public function getExtension(): string */ public function serializeData(array $data): string { - $encode = json_encode($data, JSON_UNESCAPED_UNICODE); + $serializeData = json_encode($data, JSON_UNESCAPED_UNICODE); + if (!is_string($serializeData)) { + throw new \Exception('An error occurred in serializing the data.'); + } - return $encode === false - ? '{}' - :$encode; + return $serializeData; } /** @@ -54,6 +55,11 @@ public function serializeData(array $data): string */ public function unserializeData(string $data): array { - return json_decode($data, true, 512, JSON_UNESCAPED_UNICODE); + $dataUnserialize = json_decode($data, true, 512, JSON_UNESCAPED_UNICODE); + if (!is_array($dataUnserialize)) { + throw new \Exception('An error occurred in deserializing the data.'); + } + + return $dataUnserialize; } } diff --git a/src/Driver/Txt.php b/src/Driver/Txt.php index febac7f..7db0637 100644 --- a/src/Driver/Txt.php +++ b/src/Driver/Txt.php @@ -45,6 +45,11 @@ public function serializeData(array $data): string */ public function unserializeData(string $data): array { - return unserialize($data); + $dataUnserialize = unserialize($data); + if (!is_array($dataUnserialize)) { + throw new \Exception('An error occurred in deserializing the data.'); + } + + return $dataUnserialize; } } diff --git a/src/Request.php b/src/Request.php index eac9cb8..b6279ff 100644 --- a/src/Request.php +++ b/src/Request.php @@ -23,15 +23,15 @@ * * @author Mathieu NOËL * - * @method Request where(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notWhere(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orWhere(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotWhere(\Closure|string $column, null|string $operator = null, mixed $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request where(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where * - * @method Request between(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orBetween(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notBetween(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotBetween(string $column, mixed $min, mixed $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request between(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where * * @method Request in(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where * @method Request orIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where diff --git a/src/RequestHandler.php b/src/RequestHandler.php index 6548b78..4f63008 100644 --- a/src/RequestHandler.php +++ b/src/RequestHandler.php @@ -121,7 +121,7 @@ public function from(string $table) public function insertInto(string $table, array $columns) { $this->execute = self::INSERT; - $this->from($table)->select($columns); + $this->from($table)->select(...$columns); return $this; } @@ -171,19 +171,10 @@ public function rightJoin(string $table, $column, ?string $operator = null, ?str /** * {@inheritdoc} */ - public function select(...$columns) + public function select(string ...$columns) { foreach ($columns as $column) { - /* Dans le cas ou les colonnes sont normales. */ - if (!\is_array($column)) { - $this->columns[] = $column; - - continue; - } - /* Dans le cas ou les colonnes sont dans un tableau. */ - foreach ($column as $fields) { - $this->columns[] = $fields; - } + $this->columns[] = $column; } return $this; @@ -213,7 +204,7 @@ public function unionAll(RequestInterface $request) public function update(string $table, array $columns) { $this->execute = self::UPDATE; - $this->from($table)->select(array_keys($columns)); + $this->from($table)->select(...array_keys($columns)); $this->values = $columns; return $this; diff --git a/src/RequestInterface.php b/src/RequestInterface.php index 64362fa..2e47425 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -49,11 +49,11 @@ interface RequestInterface * Enregistre les champs sélectionnées par la requête. * En cas d'absence de selection, la requêtes retournera toutes les champs. * - * @param mixed $columns Liste ou tableau des noms des colonnes. + * @param string $columns Liste ou tableau des noms des colonnes. * * @return $this */ - public function select(...$columns); + public function select(string ...$columns); /** * Enregistre le nom de la source des données principale de la requête. diff --git a/src/Schema.php b/src/Schema.php index 4447414..125462b 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -340,7 +340,7 @@ public function alterTable(string $table, callable $callback): self * * @throws ColumnsValueException * - * @return mixed Valeur par defaut. + * @return bool|null|numeric|string Valeur par defaut. */ public static function getValueDefault(string $name, array &$params) { diff --git a/src/TableBuilder.php b/src/TableBuilder.php index c4a894f..d60b3d3 100644 --- a/src/TableBuilder.php +++ b/src/TableBuilder.php @@ -265,7 +265,7 @@ public function unsigned(): self * Enregistre une valeur par défaut au champ précédent. * Lève une exception si la valeur par défaut ne correspond pas au type de valeur passée en paramètre. * - * @param mixed $value Valeur à tester. + * @param bool|null|numeric|string $value Valeur à tester. * * @throws TableBuilderException * @@ -291,14 +291,14 @@ public function valueDefault($value): self * Retourne la valeur s'il correspond au type déclaré. * Sinon déclenche une exception. * - * @param string $name Nom du champ. - * @param string $type Type de donnée (string|text|int|float|bool|char|date|datetime). - * @param mixed $value Valeur à tester. - * @param array $args Arguments de tests optionnels (length). + * @param string $name Nom du champ. + * @param string $type Type de donnée (string|text|int|float|bool|char|date|datetime). + * @param bool|null|numeric|string $value Valeur à tester. + * @param array $args Arguments de tests optionnels (length). * * @throws ColumnsValueException * - * @return mixed + * @return bool|null|numeric|string */ public static function filterValue(string $name, string $type, $value, array $args = []) { @@ -346,6 +346,10 @@ public static function filterValue(string $name, string $type, $value, array $ar break; case self::TYPE_DATE: + if (!\is_string($value)) { + throw new ColumnsValueException($error); + } + $value = (string) $value; if (strtolower($value) === self::CURRENT_DATE_DEFAULT) { return self::CURRENT_DATE_DEFAULT; } @@ -355,6 +359,10 @@ public static function filterValue(string $name, string $type, $value, array $ar throw new ColumnsValueException($error); case self::TYPE_DATETIME: + if (!\is_string($value)) { + throw new ColumnsValueException($error); + } + $value = (string) $value; if (strtolower($value) === self::CURRENT_DATETIME_DEFAULT) { return self::CURRENT_DATETIME_DEFAULT; } diff --git a/src/Where.php b/src/Where.php index f8bf7d7..d68ad08 100644 --- a/src/Where.php +++ b/src/Where.php @@ -185,15 +185,15 @@ public function executeJoin(array $row, array &$rowTable): bool /** * Retourne TRUE si la condition est validée. * - * @param mixed $columns Valeur à tester. - * @param string $operator Condition à réaliser. - * @param mixed $value Valeur de comparaison. + * @param bool|null|numeric|string $columns Valeur à tester. + * @param string $operator Condition à réaliser. + * @param array|bool|null|numeric|string $value Valeur de comparaison. * * @throws \Exception * * @return bool */ - public static function predicate($columns, string $operator, $value): bool + protected static function predicate($columns, string $operator, $value): bool { switch ($operator) { case '==': @@ -216,14 +216,16 @@ public static function predicate($columns, string $operator, $value): bool case '>=': return $columns >= $value; case 'in': + /** @var array $value */ return in_array($columns, $value); case 'regex': if ($columns === null) { return false; } - - return preg_match((string) $value, (string) $columns) === 1; + /** @var string $value */ + return preg_match($value, (string) $columns) === 1; case 'between': + /** @var array{min: string|numeric, max: string|numeric} $value */ return $columns >= $value[ 'min' ] && $columns <= $value[ 'max' ]; } diff --git a/src/WhereHandler.php b/src/WhereHandler.php index 6a35644..c05d67b 100644 --- a/src/WhereHandler.php +++ b/src/WhereHandler.php @@ -97,8 +97,8 @@ public function where( /** * Alias inverse de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param mixed $value Valeur de teste. + * @param \Closure|string $column Sous condition ou une colonne. + * @param null|numeric|string $value Valeur de teste. */ public function notWhere($column, ?string $operator = null, $value = null): self { @@ -110,8 +110,8 @@ public function notWhere($column, ?string $operator = null, $value = null): self /** * Alias avec la porte logique 'OR' de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param mixed $value Valeur de teste. + * @param \Closure|string $column Sous condition ou une colonne. + * @param null|numeric|string $value Valeur de teste. */ public function orWhere($column, ?string $operator = null, $value = null): self { @@ -123,8 +123,8 @@ public function orWhere($column, ?string $operator = null, $value = null): self /** * Alias inverse avec la porte logique 'OR' de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param mixed $value Valeur de teste. + * @param \Closure|string $column Sous condition ou une colonne. + * @param null|numeric|string $value Valeur de teste. */ public function orNotWhere($column, ?string $operator = null, $value = null): self { @@ -137,11 +137,11 @@ public function orNotWhere($column, ?string $operator = null, $value = null): se * Ajoute une condition between à la requête. * Si la valeur du champ est compris entre 2 valeurs. * - * @param string $column Nom de la colonne. - * @param mixed $min Valeur minimum ou égale. - * @param mixed $max Valeur maximum ou égale. - * @param string $bool Porte logique de la condition (and|or). - * @param bool $not Inverse la condition. + * @param string $column Nom de la colonne. + * @param numeric|string $min Valeur minimum ou égale. + * @param numeric|string $max Valeur maximum ou égale. + * @param string $bool Porte logique de la condition (and|or). + * @param bool $not Inverse la condition. */ public function between( string $column, @@ -162,8 +162,8 @@ public function between( /** * Alias inverse de la fonction between(). * - * @param mixed $min Valeur minimum ou égale. - * @param mixed $max Valeur maximum ou égale. + * @param numeric|string $min Valeur minimum ou égale. + * @param numeric|string $max Valeur maximum ou égale. */ public function notBetween(string $column, $min, $max): self { @@ -175,8 +175,8 @@ public function notBetween(string $column, $min, $max): self /** * Alias avec la porte logique 'OR' de la fonction between(). * - * @param mixed $min Valeur minimum ou égale. - * @param mixed $max Valeur maximum ou égale. + * @param numeric|string $min Valeur minimum ou égale. + * @param numeric|string $max Valeur maximum ou égale. */ public function orBetween(string $column, $min, $max): self { @@ -188,8 +188,8 @@ public function orBetween(string $column, $min, $max): self /** * Alias inverse avec la porte logique 'OR' de la fonction between(). * - * @param mixed $min Valeur minimum ou égale. - * @param mixed $max Valeur maximum ou égale. + * @param numeric|string $min Valeur minimum ou égale. + * @param numeric|string $max Valeur maximum ou égale. */ public function orNotBetween(string $column, $min, $max): self { diff --git a/tests/unit/RequestTest.php b/tests/unit/RequestTest.php index 7ed52f3..5e888d3 100644 --- a/tests/unit/RequestTest.php +++ b/tests/unit/RequestTest.php @@ -14,8 +14,6 @@ class RequestTest extends \PHPUnit\Framework\TestCase { - private const ROOT = __DIR__ . '/data/'; - /** * @var Schema */ @@ -43,7 +41,7 @@ protected function setUp(): void public function testSelect(): void { - $data1 = $this->request->select([ 'firstname' ])->from('user'); + $data1 = $this->request->select('firstname')->from('user'); self::assertEquals( 'SELECT firstname FROM user;', @@ -54,18 +52,18 @@ public function testSelect(): void $data1->fetch() ); - $data2 = $this->request->select('firstname')->from('user'); + $data2 = $this->request->select()->from('user'); self::assertEquals( - 'SELECT firstname FROM user;', + 'SELECT * FROM user;', (string) $data2 ); self::assertEquals( - [ 'firstname' => 'Mathieu' ], + [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], $data2->fetch() ); - $data3 = $this->request->select()->from('user'); + $data3 = $this->request->from('user'); self::assertEquals( 'SELECT * FROM user;', @@ -75,17 +73,6 @@ public function testSelect(): void [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], $data3->fetch() ); - - $data4 = $this->request->from('user'); - - self::assertEquals( - 'SELECT * FROM user;', - (string) $data4 - ); - self::assertEquals( - [ 'id' => 0, 'name' => 'NOEL', 'firstname' => 'Mathieu' ], - $data4->fetch() - ); } public function testLists(): void @@ -1645,13 +1632,6 @@ public function testUnionListOrder(): void ); } - public function testPredicate(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('The error operator is not supported.'); - \Queryflatfile\Where::predicate('', 'error', ''); - } - /** * @return DriverInterface&MockObject */ @@ -1661,7 +1641,7 @@ private function getDriverMock() $mock->expects(self::any()) ->method('create') ->willReturnCallback( - function (string $path, string $filename): bool { + static function (string $path, string $filename): bool { return in_array($filename, [ 'schema', 'user', 'user_role', 'role' ]); } ); @@ -1669,7 +1649,7 @@ function (string $path, string $filename): bool { $mock->expects(self::any()) ->method('has') ->willReturnCallback( - function (string $path, string $filename): bool { + static function (string $path, string $filename): bool { return in_array($filename, [ 'schema', 'user', 'user_role', 'role' ]); } ); @@ -1702,6 +1682,11 @@ private function loadFixtures(string $filename): array { $json = (string) file_get_contents(dirname(__DIR__) . "/fixtures/$filename.json"); - return json_decode($json, true); + $data = json_decode($json, true); + if (!\is_array($data)) { + throw new \Exception('An error occurred in deserializing the data.'); + } + + return $data; } } From aeadaadd60c3708e4d93374334da6033497c5f3b Mon Sep 17 00:00:00 2001 From: noelma Date: Tue, 23 Nov 2021 21:33:31 +0100 Subject: [PATCH 10/12] refactor: moving where method to parent class --- src/Request.php | 57 ----------------------------------------- src/RequestHandler.php | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/src/Request.php b/src/Request.php index b6279ff..7b80b23 100644 --- a/src/Request.php +++ b/src/Request.php @@ -10,7 +10,6 @@ namespace Queryflatfile; -use BadMethodCallException; use Queryflatfile\Exception\Query\BadFunctionException; use Queryflatfile\Exception\Query\ColumnsNotFoundException; use Queryflatfile\Exception\Query\OperatorNotFound; @@ -22,31 +21,6 @@ * Les requêtes se construisent avec le pattern fluent. * * @author Mathieu NOËL - * - * @method Request where(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * - * @method Request between(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where - * - * @method Request in(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where - * - * @method Request isNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orIsNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request isNotNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orIsNotNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where - * - * @method Request regex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where */ class Request extends RequestHandler { @@ -71,13 +45,6 @@ class Request extends RequestHandler */ private $tableSchema = []; - /** - * Les conditions de la requête. - * - * @var Where|null - */ - private $where = null; - /** * Le schéma de base de données. * @@ -148,30 +115,6 @@ public function __toString(): string return trim($output) . ';'; } - /** - * Permet d'utiliser les méthodes de l'objet \Queryflatfile\Where - * et de personnaliser les closures pour certaines méthodes. - * - * @param string $name Nom de la méthode appelée. - * @param array $args Pararètre de la méthode. - * - * @throws BadMethodCallException - * - * @return $this - */ - public function __call(string $name, array $args): self - { - $this->where = $this->where ?? new Where(); - - if (!method_exists($this->where, $name)) { - throw new BadMethodCallException(sprintf('The %s method is missing.', $name)); - } - - $this->where->$name(...$args); - - return $this; - } - /** * Ajoute un schéma de données à notre requête. * diff --git a/src/RequestHandler.php b/src/RequestHandler.php index 4f63008..fffa18e 100644 --- a/src/RequestHandler.php +++ b/src/RequestHandler.php @@ -10,10 +10,37 @@ namespace Queryflatfile; +use BadMethodCallException; + /** * Met en forme et stock les données pour lancer une requête. * * @author Mathieu NOËL + * + * @method Request where(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * + * @method Request between(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where + * + * @method Request in(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotIn(string $column, array $values) Alias de la fonction de l'objet Queryflatfile\Where + * + * @method Request isNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orIsNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request isNotNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orIsNotNull(string $column) Alias de la fonction de l'objet Queryflatfile\Where + * + * @method Request regex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where */ abstract class RequestHandler implements RequestInterface { @@ -93,6 +120,37 @@ abstract class RequestHandler implements RequestInterface */ protected $values = []; + /** + * Les conditions de la requête. + * + * @var Where|null + */ + protected $where = null; + + /** + * Permet d'utiliser les méthodes de l'objet \Queryflatfile\Where + * et de personnaliser les closures pour certaines méthodes. + * + * @param string $name Nom de la méthode appelée. + * @param array $args Pararètre de la méthode. + * + * @throws BadMethodCallException + * + * @return $this + */ + public function __call(string $name, array $args): self + { + $this->where = $this->where ?? new Where(); + + if (!method_exists($this->where, $name)) { + throw new BadMethodCallException(sprintf('The %s method is missing.', $name)); + } + + $this->where->$name(...$args); + + return $this; + } + /** * {@inheritdoc} * From 69c36acb17cb414e8678e28146bb7cfe8f538251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20NO=C3=8BL?= Date: Sat, 4 Dec 2021 15:37:31 +0100 Subject: [PATCH 11/12] feat: manage tables and fields as objects (#45) --- src/Field.php | 201 +++++++++++++ src/Field/BoolType.php | 37 +++ src/Field/CharType.php | 24 ++ src/Field/DateTimeType.php | 72 +++++ src/Field/DateType.php | 23 ++ src/Field/DropType.php | 37 +++ src/Field/FloatType.php | 37 +++ src/Field/IncrementType.php | 54 ++++ src/Field/IntType.php | 68 +++++ src/Field/RenameType.php | 60 ++++ src/Field/StringType.php | 70 +++++ src/Field/TextType.php | 37 +++ src/Request.php | 57 ++-- src/Schema.php | 391 +++++++++++-------------- src/Table.php | 125 ++++++++ src/TableAlter.php | 63 +---- src/TableBuilder.php | 384 +++++++------------------ tests/unit/RequestExecuteTest.php | 14 +- tests/unit/SchemaJsonTest.php | 75 +++-- tests/unit/TableAlterTest.php | 45 ++- tests/unit/TableBuilderTest.php | 455 +++++++++++++++++------------- 21 files changed, 1473 insertions(+), 856 deletions(-) create mode 100644 src/Field.php create mode 100644 src/Field/BoolType.php create mode 100644 src/Field/CharType.php create mode 100644 src/Field/DateTimeType.php create mode 100644 src/Field/DateType.php create mode 100644 src/Field/DropType.php create mode 100644 src/Field/FloatType.php create mode 100644 src/Field/IncrementType.php create mode 100644 src/Field/IntType.php create mode 100644 src/Field/RenameType.php create mode 100644 src/Field/StringType.php create mode 100644 src/Field/TextType.php create mode 100644 src/Table.php diff --git a/src/Field.php b/src/Field.php new file mode 100644 index 0000000..1392c44 --- /dev/null +++ b/src/Field.php @@ -0,0 +1,201 @@ + + */ +abstract class Field +{ + public const OPT_CREATE = 'create'; + + public const OPT_DROP = 'drop'; + + public const OPT_MODIFY = 'modify'; + + public const OPT_RENAME = 'rename'; + + public const TYPE = ''; + + protected const INVALID_ARGUMENT_MESSAGE = 'The value of the %s field must be of type %s: %s given.'; + + /** + * @var bool|null|numeric|string + */ + protected $valueDefault; + + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $opt = self::OPT_CREATE; + + /** + * @var null|string + */ + protected $comment = null; + + /** + * @var bool + */ + protected $isNullable = false; + + public function __construct(string $name) + { + $this->name = $name; + } + + /** + * Enregistre un commentaire. + * + * @param string $comment + * + * @return $this + */ + public function comment(string $comment): self + { + $this->comment = $comment; + + return $this; + } + + /** + * Enregistre le champ comme acceptant la valeur NULL. + * + * @return $this + */ + public function nullable(): self + { + $this->isNullable = true; + + return $this; + } + + /** + * Enregistre une valeur par défaut au champ précédent. + * Lève une exception si la valeur par défaut ne correspond pas au type de valeur passée en paramètre. + * + * @param bool|null|numeric|string $value Valeur à tester. + * + * @throws ColumnsValueException + * + * @return bool|null|numeric|string + */ + abstract public function filterValue($value); + + /** + * Enregistre une valeur par défaut au champ précédent. + * Lève une exception si la valeur par défaut ne correspond pas au type de valeur passée en paramètre. + * + * @param bool|null|numeric|string $value Valeur à tester. + * + * @throws TableBuilderException + * + * @return $this + */ + public function valueDefault($value) + { + $this->valueDefault = $this->filterValue($value); + + return $this; + } + + /** + * Retourne la valeur par defaut. + * + * @throws ColumnsValueException + * + * @return bool|null|numeric|string Valeur par defaut. + */ + public function getValueDefault() + { + if (isset($this->valueDefault)) { + return $this->valueDefault; + } + if ($this->isNullable) { + return null; + } + + throw new ColumnsValueException( + sprintf('%s not nullable or not default.', $this->name) + ); + } + + /** + * Enregistre la modification du champ précédent. + * + * @return void + */ + public function modify(): void + { + $this->opt = self::OPT_MODIFY; + } + + /** + * Retourne le nom de l'opération du champ. + * + * @return string + */ + public function getOpt(): string + { + return $this->opt; + } + + /** + * Retourne le nom du champ. + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): self + { + $this->name = $name; + + return $this; + } + + /** + * Retourne les données du champ. + * + * @return array + */ + public function toArray(): array + { + $data = []; + if (static::TYPE !== '') { + $data[ 'type' ] = static::TYPE; + } + if ($this->isNullable) { + $data[ 'nullable' ] = $this->isNullable; + } + if ($this->comment !== null) { + $data[ '_comment' ] = $this->comment; + } + if (isset($this->valueDefault)) { + $data[ 'default' ] = $this->valueDefault; + } + + return $data; + } +} diff --git a/src/Field/BoolType.php b/src/Field/BoolType.php new file mode 100644 index 0000000..9aed5ed --- /dev/null +++ b/src/Field/BoolType.php @@ -0,0 +1,37 @@ + + */ +class BoolType extends Field +{ + public const TYPE = 'boolean'; + + /** + * {@inheritdoc} + * + * return bool + */ + public function filterValue($value) + { + if (!\is_bool($value)) { + throw new \InvalidArgumentException( + sprintf(self::INVALID_ARGUMENT_MESSAGE, $this->name, self::TYPE, gettype($value)) + ); + } + + return $value; + } +} diff --git a/src/Field/CharType.php b/src/Field/CharType.php new file mode 100644 index 0000000..90d728a --- /dev/null +++ b/src/Field/CharType.php @@ -0,0 +1,24 @@ + + */ +class CharType extends StringType +{ + public const TYPE = 'char'; + + /** + * @var int + */ + protected $length = 1; +} diff --git a/src/Field/DateTimeType.php b/src/Field/DateTimeType.php new file mode 100644 index 0000000..2c38fef --- /dev/null +++ b/src/Field/DateTimeType.php @@ -0,0 +1,72 @@ + + */ +class DateTimeType extends Field +{ + public const CURRENT_DEFAULT = 'current_datetime'; + + public const TYPE = 'datetime'; + + protected const FORMAT = 'Y-m-d H:i:s'; + + /** + * {@inheritdoc} + * + * return string + */ + public function filterValue($value) + { + if (!\is_string($value)) { + throw new \InvalidArgumentException( + sprintf(self::INVALID_ARGUMENT_MESSAGE, $this->name, 'string', gettype($value)) + ); + } + if (strtolower($value) === static::CURRENT_DEFAULT) { + return static::CURRENT_DEFAULT; + } + if (($timestamp = strtotime($value))) { + return date(static::FORMAT, $timestamp); + } + + throw new ColumnsValueException( + sprintf('The value of the %s field must be a valid date: %s given', $this->name, $value) + ); + } + + /** + * {@inheritdoc} + */ + public function getValueDefault() + { + if (isset($this->valueDefault)) { + if ($this->valueDefault === static::CURRENT_DEFAULT) { + return date(static::FORMAT, time()); + } + + /* Si les variables magiques ne sont pas utilisé alors la vrais valeur par defaut est retourné. */ + return $this->valueDefault; + } + if ($this->isNullable) { + return null; + } + + throw new ColumnsValueException( + sprintf('%s not nullable or not default.', $this->name) + ); + } +} diff --git a/src/Field/DateType.php b/src/Field/DateType.php new file mode 100644 index 0000000..2f8e256 --- /dev/null +++ b/src/Field/DateType.php @@ -0,0 +1,23 @@ + + */ +class DateType extends DateTimeType +{ + public const CURRENT_DEFAULT = 'current_date'; + + public const TYPE = 'date'; + + protected const FORMAT = 'Y-m-d'; +} diff --git a/src/Field/DropType.php b/src/Field/DropType.php new file mode 100644 index 0000000..d5afa24 --- /dev/null +++ b/src/Field/DropType.php @@ -0,0 +1,37 @@ + + */ +class DropType extends Field +{ + protected $opt = self::OPT_DROP; + + public function filterValue($value) + { + return null; + } + + /** + * {@inheritdoc} + */ + public function toArray(): array + { + $data = parent::toArray(); + $data[ 'opt' ] = $this->opt; + + return $data; + } +} diff --git a/src/Field/FloatType.php b/src/Field/FloatType.php new file mode 100644 index 0000000..8455b40 --- /dev/null +++ b/src/Field/FloatType.php @@ -0,0 +1,37 @@ + + */ +class FloatType extends Field +{ + public const TYPE = 'float'; + + /** + * {@inheritdoc} + * + * return float + */ + public function filterValue($value) + { + if (!\is_float($value)) { + throw new \InvalidArgumentException( + sprintf(self::INVALID_ARGUMENT_MESSAGE, $this->name, self::TYPE, gettype($value)) + ); + } + + return (float) $value; + } +} diff --git a/src/Field/IncrementType.php b/src/Field/IncrementType.php new file mode 100644 index 0000000..229ae8d --- /dev/null +++ b/src/Field/IncrementType.php @@ -0,0 +1,54 @@ + + */ +class IncrementType extends Field +{ + public const TYPE = 'increments'; + + /** + * {@inheritdoc} + * + * return int + */ + public function filterValue($value) + { + if (!\is_int($value)) { + throw new \InvalidArgumentException( + sprintf(self::INVALID_ARGUMENT_MESSAGE, $this->name, 'integer', gettype($value)) + ); + } + + return (int) $value; + } + + /** + * @throws ColumnsValueException + */ + public function getValueDefault() + { + throw new ColumnsValueException('An incremental type column can not have a default value.'); + } + + /** + * @throws ColumnsValueException + */ + public function valueDefault($value) + { + throw new ColumnsValueException('An incremental type column can not have a default value.'); + } +} diff --git a/src/Field/IntType.php b/src/Field/IntType.php new file mode 100644 index 0000000..99b8628 --- /dev/null +++ b/src/Field/IntType.php @@ -0,0 +1,68 @@ + + */ +class IntType extends Field +{ + public const TYPE = 'integer'; + + /** + * @var bool|null + */ + private $isUnsigned = false; + + /** + * {@inheritdoc} + * + * return int + */ + public function filterValue($value) + { + if (!\is_int($value)) { + throw new \InvalidArgumentException( + sprintf(self::INVALID_ARGUMENT_MESSAGE, $this->name, self::TYPE, gettype($value)) + ); + } + + return (int) $value; + } + + /** + * {@inheritdoc} + */ + public function toArray(): array + { + $data = parent::toArray(); + + if ($this->isUnsigned) { + $data[ 'unsigned' ] = $this->isUnsigned; + } + + return $data; + } + + /** + * Enregistre le champ (uniquement de type integer) comme étant non signié. + * + * @return $this + */ + public function unsigned(): self + { + $this->isUnsigned = true; + + return $this; + } +} diff --git a/src/Field/RenameType.php b/src/Field/RenameType.php new file mode 100644 index 0000000..2b18d75 --- /dev/null +++ b/src/Field/RenameType.php @@ -0,0 +1,60 @@ + + */ +class RenameType extends Field +{ + protected $opt = self::OPT_RENAME; + + /** + * @var string + */ + protected $to; + + public function __construct(string $name, string $to) + { + parent::__construct($name); + $this->to = $to; + } + + /** + * {@inheritdoc} + */ + public function filterValue($value) + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getTo(): string + { + return $this->to; + } + + /** + * {@inheritdoc} + */ + public function toArray(): array + { + $data = parent::toArray(); + $data[ 'opt' ] = $this->opt; + $data[ 'to' ] = $this->to; + + return $data; + } +} diff --git a/src/Field/StringType.php b/src/Field/StringType.php new file mode 100644 index 0000000..fb1f738 --- /dev/null +++ b/src/Field/StringType.php @@ -0,0 +1,70 @@ + + */ +class StringType extends TextType +{ + public const TYPE = 'string'; + + /** + * @var int + */ + protected $length = 255; + + public function __construct(string $name, int $length) + { + if ($length < 0) { + throw new \InvalidArgumentException('The length passed in parameter is not of numeric type.'); + } + parent::__construct($name); + $this->length = $length; + } + + /** + * {@inheritdoc} + * + * return string + */ + public function filterValue($value) + { + /** @var string $str */ + $str = parent::filterValue($value); + + if (strlen($str) > $this->length) { + throw new \LengthException( + sprintf( + 'The value of the %s field must be less than or equal to %s characters: %s given', + $this->name, + $this->length, + strlen($str) + ) + ); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function toArray(): array + { + $data = parent::toArray(); + $data[ 'length' ] = $this->length; + + return $data; + } +} diff --git a/src/Field/TextType.php b/src/Field/TextType.php new file mode 100644 index 0000000..18f2d11 --- /dev/null +++ b/src/Field/TextType.php @@ -0,0 +1,37 @@ + + */ +class TextType extends Field +{ + public const TYPE = 'text'; + + /** + * {@inheritdoc} + * + * return string + */ + public function filterValue($value) + { + if (!\is_string($value)) { + throw new \InvalidArgumentException( + sprintf(self::INVALID_ARGUMENT_MESSAGE, $this->name, 'string', gettype($value)) + ); + } + + return $value; + } +} diff --git a/src/Request.php b/src/Request.php index 7b80b23..52cb592 100644 --- a/src/Request.php +++ b/src/Request.php @@ -15,6 +15,7 @@ use Queryflatfile\Exception\Query\OperatorNotFound; use Queryflatfile\Exception\Query\QueryException; use Queryflatfile\Exception\Query\TableNotFoundException; +use Queryflatfile\Field\IncrementType; /** * Réalise des requêtes à partir d'un schéma de données passé en paramètre. @@ -27,7 +28,7 @@ class Request extends RequestHandler /** * Toutes les configurations du schéma des champs utilisés. * - * @var array + * @var array */ private $allColumnsSchema; @@ -41,9 +42,9 @@ class Request extends RequestHandler /** * Le schéma des tables utilisées par la requête. * - * @var array + * @var Table */ - private $tableSchema = []; + private $table; /** * Le schéma de base de données. @@ -147,8 +148,8 @@ public function getTableData(string $name): array public function from(string $table): self { parent::from($table); - $this->tableSchema = $this->schema->getSchemaTable($table); - $this->tableData = $this->getTableData($table); + $this->table = $this->schema->getTableSchema($table); + $this->tableData = $this->getTableData($table); return $this; } @@ -431,10 +432,10 @@ protected function executeOrderBy(array &$data, array $orderBy): void protected function executeInsert(): void { /* Si l'une des colonnes est de type incrémental. */ - $increment = $this->getIncrement(); + $increment = $this->table->getIncrement(); /* Je charge les colonnes de mon schéma. */ - $schemaColumns = $this->tableSchema[ 'fields' ]; - $count = count($this->columns); + $fields = $this->table->getFields(); + $count = count($this->columns); foreach ($this->values as $values) { /* Pour chaque ligne je vérifie si le nombre de colonne correspond au nombre valeur insérée. */ @@ -461,27 +462,27 @@ protected function executeInsert(): void } $data = []; - foreach ($schemaColumns as $field => $arg) { + foreach ($fields as $fieldName => $field) { /* Si mon champs existe dans le schema. */ - if (isset($row[ $field ])) { - $data[ $field ] = TableBuilder::filterValue($field, $arg[ 'type' ], $row[ $field ], $arg); + if (isset($row[ $fieldName ])) { + $data[ $fieldName ] = $field->filterValue($row[ $fieldName ]); /* Si le champ est de type incrémental et que sa valeur est supérieure à celui enregistrer dans le schéma. */ - if ($arg[ 'type' ] === TableBuilder::TYPE_INCREMENT && ($data[ $field ] > $increment)) { - $increment = $data[ $field ]; + if ($field instanceof IncrementType && ($data[ $fieldName ] > $increment)) { + $increment = $data[ $fieldName ]; } continue; } /* Si mon champ n'existe pas et qu'il de type incrémental. */ - if ($arg[ 'type' ] === TableBuilder::TYPE_INCREMENT) { + if ($field instanceof IncrementType) { ++$increment; - $data[ $field ] = $increment; + $data[ $fieldName ] = $increment; continue; } /* Sinon on vérifie si une valeur par défaut lui est attribué. */ - $data[ $field ] = $this->schema->getValueDefault($field, $arg); + $data[ $fieldName ] = $field->getValueDefault(); } $this->tableData[] = $data; @@ -525,27 +526,17 @@ protected function executeDelete(): void $this->tableData = array_values($this->tableData); } - /** - * Retourne les champs incrémental de la courrante. - * - * @return int|null Increment de la table courante - */ - protected function getIncrement(): ?int - { - return $this->tableSchema[ 'increments' ]; - } - /** * Charge les colonnes de la table courante et des tables de jointure. */ private function loadAllColumnsSchema(): void { - $this->allColumnsSchema = $this->tableSchema[ 'fields' ]; + $this->allColumnsSchema = $this->table->getFields(); foreach ($this->joins as $value) { $this->allColumnsSchema = array_merge( $this->allColumnsSchema, - $this->schema->getSchemaTable($value[ 'table' ])[ 'fields' ] + $this->schema->getTableSchema($value[ 'table' ])->getFields() ); } } @@ -688,15 +679,13 @@ private function diffColumns(array $columns): void * * @param string $table Nom de la table. */ - private function getRowTableNull($table): array + private function getRowTableNull(string $table): array { - /* Le schéma de la table à joindre. */ - $sch = $this->schema->getSchemaTable($table); /* Prend les noms des champs de la table à joindre. */ - $rowTableKey = array_keys($sch[ 'fields' ]); + $rowTableKey = $this->schema->getTableSchema($table)->getFieldsName(); /* Prend les noms des champs dans la requête précédente. */ - if (isset($this->tableSchema[ 'fields' ])) { - $rowTableAllKey = array_keys($this->tableSchema[ 'fields' ]); + if ($this->table->getFields() !== []) { + $rowTableAllKey = $this->table->getFieldsName(); $rowTableKey = array_merge($rowTableKey, $rowTableAllKey); } /* Utilise les noms pour créer un tableau avec des valeurs null. */ diff --git a/src/Schema.php b/src/Schema.php index 125462b..ecf3aec 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -14,10 +14,12 @@ use Queryflatfile\DriverInterface; use Queryflatfile\Exception\Exception; -use Queryflatfile\Exception\Query\ColumnsValueException; use Queryflatfile\Exception\Query\TableNotFoundException; use Queryflatfile\Exception\TableBuilder\ColumnsNotFoundException; -use Queryflatfile\TableBuilder; +use Queryflatfile\Exception\TableBuilder\ColumnsValueException; +use Queryflatfile\Field\DropType; +use Queryflatfile\Field\IncrementType; +use Queryflatfile\Field\RenameType; /** * Pattern fluent pour la gestion d'un schéma de données. @@ -64,7 +66,7 @@ class Schema /** * Schéma des tables. * - * @var array + * @var array */ protected $schema = []; @@ -134,7 +136,7 @@ public function setPathRoot(string $root = ''): self * Modifie la valeur incrémentale d'une table. * * @param string $table Nom de la table. - * @param int $increment Tableau associatif des valeurs incrémentales. + * @param int $increment Nouvelle valeur incrémentale. * * @throws TableNotFoundException * @throws Exception @@ -147,15 +149,15 @@ public function setIncrement(string $table, int $increment): bool throw new TableNotFoundException($table); } - if (!isset($this->schema[ $table ][ 'increments' ])) { + if (!$this->schema[ $table ]->hasIncrement()) { throw new Exception( sprintf('Table %s does not have an incremental value.', $table) ); } - $this->schema[ $table ][ 'increments' ] = $increment; + $this->schema[ $table ]->setIncrement($increment); - return $this->save($this->name, $this->schema); + return $this->save($this->name, $this->toArray()); } /** @@ -174,19 +176,19 @@ public function getIncrement(string $table): int throw new TableNotFoundException($table); } - if ($this->schema[ $table ][ 'increments' ] === null) { + if ($this->schema[ $table ]->getIncrement() === null) { throw new Exception( sprintf('Table %s does not have an incremental value.', $table) ); } - return $this->schema[ $table ][ 'increments' ]; + return $this->schema[ $table ]->getIncrement(); } /** - * Génère le schéma s'il n'existe pas en fonction du fichier de configuration. + * Retourne le schema des tables. * - * @return array Schéma de la base de données. + * @return array Schéma de la base de données. */ public function getSchema(): array { @@ -194,7 +196,12 @@ public function getSchema(): array return $this->schema; } $this->create($this->name); - $this->schema = $this->read($this->name); + $schema = $this->read($this->name); + + /** @var string $tableName */ + foreach ($schema as $tableName => $table) { + $this->schema[ $tableName ] = TableBuilder::createTableFromArray($tableName, $table); + } return $this->schema; } @@ -206,9 +213,9 @@ public function getSchema(): array * * @throws TableNotFoundException * - * @return array Schéma de la table. + * @return Table Schéma de la table. */ - public function getSchemaTable(string $table): array + public function getTableSchema(string $table): Table { if (!$this->hasTable($table)) { throw new TableNotFoundException($table); @@ -263,11 +270,9 @@ public function createTable(string $table, ?callable $callback = null): self throw new Exception(sprintf('Table %s exist.', $table)); } - $builder = self::tableBuilder($callback); + $this->schema[ $table ] = self::tableBuilder($table, $callback)->getTable(); - $this->schema[ $table ] = $builder->getTableSchema(); - - $this->save($this->name, $this->schema); + $this->save($this->name, $this->toArray()); $this->create($table); return $this; @@ -298,72 +303,39 @@ public function createTableIfNotExists(string $table, ?callable $callback = null * Modifie les champs du schéma de données. * * @param string $table Nom de la table. - * @param callable $callback fonction(TableBuilder $table) pour créer les champs. + * @param callable $callback fonction(TableAleter $tableAlter) pour manipuler les champs. * * @return $this */ public function alterTable(string $table, callable $callback): self { - $schema = $this->getSchemaTable($table); - $fields = $schema[ 'fields' ]; - $tableBuilder = self::tableAlterBuilder($callback)->build(); - $dataTable = $this->read($table); - - foreach ($tableBuilder as $name => $params) { - if (!isset($params[ 'opt' ])) { - self::filterFieldAdd($table, $fields, $name, $params[ 'type' ]); - self::add($schema, $dataTable, $name, $params); - } elseif ($params[ 'opt' ] === TableAlter::OPT_RENAME) { - self::filterFieldRename($table, $fields, $name, $params[ 'to' ]); - self::rename($schema, $dataTable, $name, $params[ 'to' ]); - } elseif ($params[ 'opt' ] === TableAlter::OPT_MODIFY) { - self::filterFieldModify($table, $fields, $name, $params[ 'type' ]); - self::modify($schema, $dataTable, $name, $params); - } elseif ($params[ 'opt' ] === TableAlter::OPT_DROP) { - self::filterFieldDrop($table, $fields, $name); - self::drop($schema, $dataTable, $name); + $tableSchema = $this->getTableSchema($table); + $tableBuilder = self::tableAlterBuilder($table, $callback)->getTable(); + $tableData = $this->read($table); + + foreach ($tableBuilder->getFields() as $field) { + if ($field->getOpt() === Field::OPT_CREATE) { + self::filterFieldAdd($tableSchema, $field); + self::add($tableSchema, $field, $tableData); + } elseif ($field->getOpt() === Field::OPT_RENAME) { + self::filterFieldRename($tableSchema, $field); + self::rename($tableSchema, $field, $tableData); + } elseif ($field->getOpt() === Field::OPT_MODIFY) { + self::filterFieldModify($tableSchema, $field); + self::modify($tableSchema, $field, $tableData); + } elseif ($field->getOpt() === Field::OPT_DROP) { + self::filterFieldDrop($tableSchema, $field); + self::drop($tableSchema, $field, $tableData); } } - $this->schema[ $table ] = $schema; - $this->save($this->name, $this->schema); - $this->save($table, $dataTable); + $this->schema[ $table ] = $tableSchema; + $this->save($this->name, $this->toArray()); + $this->save($table, $tableData); return $this; } - /** - * Retourne la valeur par defaut du champ passé en paramêtre. - * - * @param string $name Nom du champ. - * @param array $params Différente configurations. - * - * @throws ColumnsValueException - * - * @return bool|null|numeric|string Valeur par defaut. - */ - public static function getValueDefault(string $name, array &$params) - { - if (isset($params[ 'default' ])) { - if ($params[ 'type' ] === TableBuilder::TYPE_DATE && $params[ 'default' ] === TableBuilder::CURRENT_DATE_DEFAULT) { - return date('Y-m-d', time()); - } - if ($params[ 'type' ] === TableBuilder::TYPE_DATETIME && $params[ 'default' ] === TableBuilder::CURRENT_DATETIME_DEFAULT) { - return date('Y-m-d H:i:s', time()); - } - - /* Si les variables magiques ne sont pas utilisé alors la vrais valeur par defaut est retourné. */ - return $params[ 'default' ]; - } - if (isset($params[ 'nullable' ])) { - return null; - } - - throw new ColumnsValueException( - sprintf('%s not nullable or not default.', $name) - ); - } - /** * Détermine une table existe. * @@ -386,7 +358,9 @@ public function hasTable(string $table): bool */ public function hasColumn(string $table, string $column): bool { - return isset($this->getSchema()[ $table ][ 'fields' ][ $column ]) && $this->driver->has($this->root . $this->path, $table); + return isset($this->getSchema()[ $table ]) && + $this->getSchema()[ $table ]->hasField($column) && + $this->driver->has($this->root . $this->path, $table); } /** @@ -405,10 +379,10 @@ public function truncateTable(string $table): bool } $deleteSchema = true; - if ($this->schema[ $table ][ 'increments' ] !== null) { - $this->schema[ $table ][ 'increments' ] = 0; + if ($this->schema[ $table ]->hasIncrement()) { + $this->schema[ $table ]->setIncrement(0); - $deleteSchema = $this->save($this->name, $this->schema); + $deleteSchema = $this->save($this->name, $this->toArray()); } $deleteData = $this->save($table, []); @@ -432,7 +406,7 @@ public function dropTable(string $table): bool unset($this->schema[ $table ]); $deleteData = $this->delete($table); - $deleteSchema = $this->save($this->name, $this->schema); + $deleteSchema = $this->save($this->name, $this->toArray()); return $deleteSchema && $deleteData; } @@ -512,154 +486,130 @@ protected function delete(string $file): bool /** * Ajoute un champ dans les paramètre de la table et ses données. * - * @param array $schema Schéma de la table. - * @param array $dataTable Les données de la table. - * @param string $name Nom du champ. - * @param array $params Nouveaux paramètres. + * @param Table $table Schéma de la table. + * @param Field $field Nouveau champ. + * @param array $tableData Les données de la table. * * @return void */ protected static function add( - array &$schema, - array &$dataTable, - string $name, - array $params + Table &$table, + Field $field, + array &$tableData ): void { - $schema[ 'fields' ][ $name ] = $params; + $table->addField($field); - $increment = $params[ 'type' ] === TableBuilder::TYPE_INCREMENT + $increment = $field instanceof IncrementType ? 0 : null; try { - $valueDefault = self::getValueDefault($name, $params); - } catch (ColumnsValueException $e) { + $valueDefault = $field->getValueDefault(); + } catch (ColumnsValueException|\InvalidArgumentException $e) { $valueDefault = ''; } - foreach ($dataTable as &$data) { - $data[ $name ] = $increment === null + foreach ($tableData as &$data) { + $data[ $field->getName() ] = $increment === null ? $valueDefault : ++$increment; } - if ($params[ 'type' ] === TableBuilder::TYPE_INCREMENT) { - $schema[ 'increments' ] = $increment; + if ($increment !== null) { + $table->setIncrement($increment); } } /** * Modifie un champ dans les paramètre de la table et ses données. * - * @param array $schema Schéma de la table. - * @param array $dataTable Les données de la table. - * @param string $name Nom du champ. - * @param array $params Nouveaux paramètres. + * @param Table $table Schéma de la table. + * @param Field $field Champ modifié. + * @param array $tableData Les données de la table. * * @return void */ protected static function modify( - array &$schema, - array &$dataTable, - string $name, - array $params + Table &$table, + Field $field, + array &$tableData ): void { - unset($params[ 'opt' ]); - $schema[ 'fields' ][ $name ] = $params; + $table->addField($field); - $increment = $params[ 'type' ] === TableBuilder::TYPE_INCREMENT + $increment = $field instanceof IncrementType ? 0 : null; try { - $valueDefault = self::getValueDefault($name, $params); - foreach ($dataTable as &$data) { - $data[ $name ] = $valueDefault; + $valueDefault = $field->getValueDefault(); + foreach ($tableData as &$data) { + $data[ $field->getName() ] = $valueDefault; } - } catch (ColumnsValueException $e) { + } catch (ColumnsValueException | \InvalidArgumentException $e) { } - if ($params[ 'type' ] === TableBuilder::TYPE_INCREMENT) { - $schema[ 'increments' ] = $increment; + if ($increment !== null) { + $table->setIncrement($increment); } } /** * Renomme un champ dans les paramètre de la table et ses données. * - * @param array $schema Les champs de la table. - * @param array $dataTable Les données de la table. - * @param string $name Nom du champ. - * @param string $to Nouveau nom du champ. + * @param Table $table Schéma de la table. + * @param RenameType $fieldRename champ à renommer + * @param array $tableData Les données de la table. * * @return void */ protected static function rename( - array &$schema, - array &$dataTable, - string $name, - string $to + Table &$table, + RenameType $fieldRename, + array &$tableData ): void { - $schema[ 'fields' ][ $to ] = $schema[ 'fields' ][ $name ]; - unset($schema[ 'fields' ][ $name ]); - foreach ($dataTable as &$data) { - $data[ $to ] = $data[ $name ]; - unset($data[ $name ]); + $table->renameField($fieldRename->getName(), $fieldRename->getTo()); + + foreach ($tableData as &$data) { + $data[ $fieldRename->getTo() ] = $data[ $fieldRename->getName() ]; + unset($data[ $fieldRename->getName() ]); } } /** * Supprime un champ dans les paramètre de la table et ses données. * - * @param array $schema Les champs de la table. - * @param array $dataTable Les données de la table. - * @param string $name Nom du champ. + * @param Table $table Schéma de la table. + * @param DropType $fieldDrop Champ à supprimer + * @param array $tableData Les données de la table. * * @return void */ protected static function drop( - array &$schema, - array &$dataTable, - string $name + Table &$table, + DropType $fieldDrop, + array &$tableData ): void { - foreach (array_keys($dataTable) as $key) { - unset($dataTable[ $key ][ $name ]); + foreach (array_keys($tableData) as $key) { + unset($tableData[ $key ][ $fieldDrop->getName() ]); } - if ($schema[ 'fields' ][ $name ][ 'type' ] === TableBuilder::TYPE_INCREMENT) { - $schema[ 'increments' ] = null; - } - unset($schema[ 'fields' ][ $name ]); - } - - /** - * Retour true si l'un des champs est de type incrementale. - * - * @param array $fields - * - * @return bool - */ - protected static function isFieldIncrement(array $fields): bool - { - foreach ($fields as $field) { - if ($field[ 'type' ] === TableBuilder::TYPE_INCREMENT) { - return true; - } + if ($table->getField($fieldDrop->getName()) instanceof IncrementType) { + $table->setIncrement(null); } - - return false; + $table->unsetField($fieldDrop->getName()); } /** * Passe en premier paramètre d'une fonction anonyme un objet TableBuilder et le retourne. * + * @param string $table Nom de la table. * @param callable $callback Fonction anonyme. * * @return TableBuilder */ - protected static function tableAlterBuilder(callable $callback): TableBuilder + protected static function tableAlterBuilder(string $table, callable $callback): TableBuilder { - $builder = new TableAlter(); + $builder = new TableAlter($table); call_user_func_array($callback, [ &$builder ]); return $builder; @@ -668,13 +618,14 @@ protected static function tableAlterBuilder(callable $callback): TableBuilder /** * Passe en premier paramètre d'une fonction anonyme un objet TableBuilder et le retourne. * + * @param string $table Nom de la table. * @param callable|null $callback Fonction anonyme. * * @return TableBuilder */ - protected static function tableBuilder(?callable $callback = null): TableBuilder + protected static function tableBuilder(string $table, ?callable $callback = null): TableBuilder { - $builder = new TableBuilder(); + $builder = new TableBuilder($table); if ($callback !== null) { call_user_func_array($callback, [ &$builder ]); } @@ -683,138 +634,138 @@ protected static function tableBuilder(?callable $callback = null): TableBuilder } /** - * Vérifie si les opérations du champ sont conformes. + * Vérifie si les opérations d'ajout du champ sont conformes. * - * @param string $table Nom de la table. - * @param array $fields Paramètres du champ en base. - * @param string $name Nom du champ. - * @param string $type Type de donnée. + * @param Table $tableSchema Le schéma de la table. + * @param Field $field Nouveau champ. * * @throws Exception * @throws ColumnsNotFoundException */ - private static function filterFieldAdd( - string $table, - array $fields, - string $name, - string $type - ): void { + private static function filterFieldAdd(Table $tableSchema, Field $field): void + { /* Si un champ est ajouté il ne doit pas exister dans le schéma. */ - if (isset($fields[ $name ])) { + if ($tableSchema->hasField($field->getName())) { throw new Exception( - sprintf('%s field does not exists in %s table.', $name, $table) + sprintf( + '%s field does not exists in %s table.', + $field->getName(), + $tableSchema->getName() + ) ); } - if ($type === TableBuilder::TYPE_INCREMENT && self::isFieldIncrement($fields)) { + if ($tableSchema->hasIncrement() && $field instanceof IncrementType) { throw new ColumnsValueException( sprintf( 'The %s table can not have multiple incremental values.', - $table + $tableSchema->getName() ) ); } } /** - * @param string $table Nom de la table. - * @param array $fields Paramètres du champ en base. - * @param string $name Nom du champ. - * @param string $type Type de donnée. + * Vérifie si les opérations de modification du champ sont conformes. + * + * @param Table $tableSchema Le schéma de la table. + * @param Field $field Champ à modifier. * * @throws ColumnsNotFoundException * @throws ColumnsValueException * @throws Exception */ - private static function filterFieldModify( - string $table, - array $fields, - string $name, - string $type - ): void { - if (!isset($fields[ $name ])) { - throw new ColumnsNotFoundException( + private static function filterFieldModify(Table $tableSchema, Field $field): void + { + if (!$tableSchema->hasField($field->getName())) { + throw new Exception( sprintf( '%s field does not exists in %s table.', - $name, - $table + $field->getName(), + $tableSchema->getName() ) ); } - if ($type === TableBuilder::TYPE_INCREMENT && self::isFieldIncrement($fields)) { + if ($tableSchema->hasIncrement() && $field instanceof IncrementType) { throw new ColumnsValueException( sprintf( 'The %s table can not have multiple incremental values.', - $table + $tableSchema->getName() ) ); } + $fieldOld = $tableSchema->getField($field->getName()); + /* Si le type change, les données présents doivent être d'un type équivalent. */ - $modifyNumber = in_array($type, [ 'integer', 'float', 'increments' ]) && - !in_array($fields[ $name ][ 'type' ], [ 'integer', 'float', 'increments' ]); - $modifyString = in_array($type, [ 'text', 'string', 'char' ]) && - !in_array($fields[ $name ][ 'type' ], [ 'text', 'string', 'char' ]); - $modifyDate = in_array($type, [ 'date', 'datetime' ]) && - !in_array($fields[ $name ][ 'type' ], [ 'date', 'datetime', 'string', 'text' ]); + $modifyNumber = in_array($field::TYPE, [ 'integer', 'float', 'increments' ]) && + !in_array($fieldOld::TYPE, [ 'integer', 'float', 'increments' ]); + $modifyString = in_array($field::TYPE, [ 'text', 'string', 'char' ]) && + !in_array($fieldOld::TYPE, [ 'text', 'string', 'char' ]); + $modifyDate = in_array($field::TYPE, [ 'date', 'datetime' ]) && + !in_array($fieldOld::TYPE, [ 'date', 'datetime', 'string', 'text' ]); if ($modifyString || $modifyNumber || $modifyDate) { throw new Exception( sprintf( 'The %s column type %s can not be changed with the %s type.', - $name, - $fields[ $name ][ 'type' ], - $type + $field->getName(), + $fieldOld::TYPE, + $field::TYPE ) ); } } /** - * @param string $table Nom de la table. - * @param array $fields Paramètres du champ en base. - * @param string $name Nom du champ. - * @param string $to Nouveau nom du champ. + * Vérifie si les opérations de renommage du champ sont conformes. + * + * @param Table $table Le schéma de la table. + * @param RenameType $field Champ à renommer. * * @throws ColumnsNotFoundException * @throws Exception */ - private static function filterFieldRename( - string $table, - array $fields, - string $name, - string $to - ): void { - if (!isset($fields[ $name ])) { + private static function filterFieldRename(Table $table, RenameType $field): void + { + if (!$table->hasField($field->getName())) { throw new ColumnsNotFoundException( - sprintf('%s field does not exists in %s table.', $name, $table) + sprintf('%s field does not exists in %s table.', $field->getName(), $table->getName()) ); } /* Si le champ à renommer existe dans le schema. */ - if (isset($fields[ $to ])) { + if ($table->hasField($field->getTo())) { throw new Exception( - sprintf('%s field does exists in %s table.', $name, $table) + sprintf('%s field does exists in %s table.', $field->getName(), $table->getName()) ); } } /** - * @param string $table Nom de la table. - * @param array $fields Paramètres du champ en base. - * @param string $name Nom du champ. + * Vérifie si les opérations de suppression du champ sont conformes. + * + * @param Table $table Le schéma de la table. + * @param DropType $field Champ à supprimer * * @throws ColumnsNotFoundException * * @return void */ - private static function filterFieldDrop( - string $table, - array $fields, - string $name - ): void { - if (!isset($fields[ $name ])) { + private static function filterFieldDrop(Table $table, DropType $field): void + { + if (!$table->hasField($field->getName())) { throw new ColumnsNotFoundException( - sprintf('%s field does not exists in %s table.', $name, $table) + sprintf('%s field does not exists in %s table.', $field->getName(), $table->getName()) ); } } + + private function toArray(): array + { + $tables = []; + foreach ($this->schema as $name => $table) { + $tables[ $name ] = $table->toArray(); + } + + return $tables; + } } diff --git a/src/Table.php b/src/Table.php new file mode 100644 index 0000000..df5b5a3 --- /dev/null +++ b/src/Table.php @@ -0,0 +1,125 @@ + + */ + protected $fields = []; + + /** + * La valeur des champs incrémentaux. + * + * @var int|null + */ + private $increment = null; + + public function __construct(string $name) + { + $this->name = $name; + } + + /** + * Ajoute un nouveau champ. + * + * @param Field $field Champ. + * + * @return void + */ + public function addField(Field $field): void + { + if ($field instanceof IncrementType) { + $this->increment = 0; + } + + $this->fields[ $field->getName() ] = $field; + } + + public function getField(string $name): Field + { + if (!isset($this->fields[ $name ])) { + throw new \Exception(); + } + + return $this->fields[ $name ]; + } + + public function getFields(): array + { + return $this->fields; + } + + public function getFieldsName(): array + { + return array_keys($this->fields); + } + + public function getName(): string + { + return $this->name; + } + + public function getIncrement(): ?int + { + return $this->increment; + } + + public function hasField(string $name): bool + { + return isset($this->fields[ $name ]); + } + + public function hasIncrement(): bool + { + return $this->increment !== null; + } + + public function renameField(string $from, string $to): void + { + $this->fields[ $to ] = $this->fields[ $from ]; + unset($this->fields[ $from ]); + $this->fields[ $to ]->setName($to); + } + + public function setIncrement(?int $increment): void + { + $this->increment = $increment; + } + + public function toArray(): array + { + $fields = []; + foreach ($this->fields as $name => $field) { + $fields[ $name ] = $field->toArray(); + } + + return [ + 'fields' => $fields, + 'increments' => $this->increment + ]; + } + + public function unsetField(string $name): void + { + unset($this->fields[ $name ]); + } +} diff --git a/src/TableAlter.php b/src/TableAlter.php index 6bdaa84..c658ebf 100644 --- a/src/TableAlter.php +++ b/src/TableAlter.php @@ -10,7 +10,8 @@ namespace Queryflatfile; -use Queryflatfile\Exception\TableBuilder\ColumnsNotFoundException; +use Queryflatfile\Field\DropType; +use Queryflatfile\Field\RenameType; /** * Pattern fluent pour la création et configuration des types de données. @@ -19,24 +20,16 @@ */ class TableAlter extends TableBuilder { - public const OPT_DROP = 'drop'; - - public const OPT_MODIFY = 'modify'; - - public const OPT_RENAME = 'rename'; - /** * Enregistre la suppression d'une colonne. * * @param string $name Nom de la colonne. * - * @return $this + * @return void */ - public function dropColumn(string $name): self + public function dropColumn(string $name): void { - $this->builder[ $name ][ 'opt' ] = self::OPT_DROP; - - return $this; + $this->table->addField(new DropType($name)); } /** @@ -45,50 +38,10 @@ public function dropColumn(string $name): self * @param string $from Nom de la colonne. * @param string $to Nouveau nom de la colonne. * - * @return $this - */ - public function renameColumn(string $from, string $to): self - { - $this->builder[ $from ] = [ 'opt' => self::OPT_RENAME, 'to' => $to ]; - - return $this; - } - - /** - * Enregistre la modification du champ précédent. - * - * @return $this + * @return void */ - public function modify(): self + public function renameColumn(string $from, string $to): void { - $this->checkPreviousBuild('modify'); - $key = key($this->builder); - - $this->builder[ $key ][ 'opt' ] = self::OPT_MODIFY; - - return $this; - } - - /** - * Retourne le champs courant. - * Déclenche une exception si le champ courant n'existe pas ou - * si le champ courant est une opération. - * - * @param string $opt Nom de l'opération réalisé. - * - * @throws ColumnsNotFoundException - * - * @return array Paramètres du champ. - */ - protected function checkPreviousBuild(string $opt): array - { - $current = parent::checkPreviousBuild($opt); - if (isset($current[ 'opt' ])) { - throw new ColumnsNotFoundException( - sprintf('No column selected for %s.', $opt) - ); - } - - return $current; + $this->table->addField(new RenameType($from, $to)); } } diff --git a/src/TableBuilder.php b/src/TableBuilder.php index d60b3d3..1396de9 100644 --- a/src/TableBuilder.php +++ b/src/TableBuilder.php @@ -10,9 +10,16 @@ namespace Queryflatfile; -use Queryflatfile\Exception\TableBuilder\ColumnsNotFoundException; -use Queryflatfile\Exception\TableBuilder\ColumnsValueException; use Queryflatfile\Exception\TableBuilder\TableBuilderException; +use Queryflatfile\Field\BoolType; +use Queryflatfile\Field\CharType; +use Queryflatfile\Field\DateTimeType; +use Queryflatfile\Field\DateType; +use Queryflatfile\Field\FloatType; +use Queryflatfile\Field\IncrementType; +use Queryflatfile\Field\IntType; +use Queryflatfile\Field\StringType; +use Queryflatfile\Field\TextType; /** * Pattern fluent pour la création et configuration des types de données. @@ -21,41 +28,15 @@ */ class TableBuilder { - public const CURRENT_DATE_DEFAULT = 'current_date'; - - public const CURRENT_DATETIME_DEFAULT = 'current_datetime'; - - public const TYPE_BOOL = 'boolean'; - - public const TYPE_CHAR = 'char'; - - public const TYPE_DATE = 'date'; - - public const TYPE_DATETIME = 'datetime'; - - public const TYPE_FLOAT = 'float'; - - public const TYPE_INCREMENT = 'increments'; - - public const TYPE_INT = 'integer'; - - public const TYPE_STRING = 'string'; - - public const TYPE_TEXT = 'text'; - /** - * Les champs et leurs paramètres. - * - * @var array + * @var Table */ - protected $builder = []; + protected $table; - /** - * La valeur des champs incrémentaux. - * - * @var int|null - */ - private $increment = null; + public function __construct(string $name) + { + $this->table = new Table($name); + } /** * Enregistre un champ de type `boolean`, true ou false. @@ -63,13 +44,13 @@ class TableBuilder * * @param string $name Nom du champ. * - * @return $this + * @return Field */ - public function boolean(string $name): self + public function boolean(string $name): Field { - $this->builder[ $name ][ 'type' ] = self::TYPE_BOOL; + $this->table->addField(new BoolType($name)); - return $this; + return $this->table->getField($name); } /** @@ -81,16 +62,13 @@ public function boolean(string $name): self * * @throws TableBuilderException * - * @return $this + * @return Field */ - public function char(string $name, int $length = 1): self + public function char(string $name, int $length = 1): Field { - if ($length < 0) { - throw new TableBuilderException('The length passed in parameter is not of numeric type.'); - } - $this->builder[ $name ] = [ 'type' => self::TYPE_CHAR, 'length' => $length ]; + $this->table->addField(new CharType($name, $length)); - return $this; + return $this->table->getField($name); } /** @@ -98,13 +76,13 @@ public function char(string $name, int $length = 1): self * * @param string $name Nom du champ. * - * @return $this + * @return Field */ - public function date(string $name): self + public function date(string $name): Field { - $this->builder[ $name ][ 'type' ] = self::TYPE_DATE; + $this->table->addField(new DateType($name)); - return $this; + return $this->table->getField($name); } /** @@ -112,13 +90,13 @@ public function date(string $name): self * * @param string $name Nom du champ. * - * @return $this + * @return Field */ - public function datetime(string $name): self + public function datetime(string $name): Field { - $this->builder[ $name ][ 'type' ] = self::TYPE_DATETIME; + $this->table->addField(new DateTimeType($name)); - return $this; + return $this->table->getField($name); } /** @@ -128,13 +106,13 @@ public function datetime(string $name): self * * @param string $name Nom du champ. * - * @return $this + * @return Field */ - public function float(string $name): self + public function float(string $name): Field { - $this->builder[ $name ][ 'type' ] = self::TYPE_FLOAT; + $this->table->addField(new FloatType($name)); - return $this; + return $this->table->getField($name); } /** @@ -146,18 +124,17 @@ public function float(string $name): self * * @throws TableBuilderException * - * @return $this + * @return Field */ - public function increments(string $name): self + public function increments(string $name): Field { - if ($this->increment !== null) { + if ($this->table->getIncrement() !== null) { throw new TableBuilderException('Only one incremental column is allowed per table.'); } - $this->builder[ $name ][ 'type' ] = self::TYPE_INCREMENT; - $this->increment = 0; + $this->table->addField(new IncrementType($name)); - return $this; + return $this->table->getField($name); } /** @@ -167,13 +144,13 @@ public function increments(string $name): self * * @param string $name Nom du champ. * - * @return $this + * @return IntType */ - public function integer(string $name): self + public function integer(string $name): IntType { - $this->builder[ $name ][ 'type' ] = self::TYPE_INT; - - return $this; + $this->table->addField(new IntType($name)); + /** @var IntType */ + return $this->table->getField($name); } /** @@ -185,16 +162,13 @@ public function integer(string $name): self * * @throws TableBuilderException * - * @return $this + * @return Field */ - public function string(string $name, int $length = 255): self + public function string(string $name, int $length = 255): Field { - if ($length < 0) { - throw new TableBuilderException('The length passed in parameter is not of numeric type.'); - } - $this->builder[ $name ] = [ 'type' => self::TYPE_STRING, 'length' => $length ]; + $this->table->addField(new StringType($name, $length)); - return $this; + return $this->table->getField($name); } /** @@ -203,226 +177,72 @@ public function string(string $name, int $length = 255): self * * @param string $name Nom du champ. * - * @return $this + * @return Field */ - public function text(string $name): self + public function text(string $name): Field { - $this->builder[ $name ][ 'type' ] = self::TYPE_TEXT; + $this->table->addField(new TextType($name)); - return $this; + return $this->table->getField($name); } - /** - * Enregistre un commentaire sur le dernier champ appelé. - * - * @param string $comment Commentaire du champ précédent. - * - * @return $this - */ - public function comment(string $comment): self - { - $this->checkPreviousBuild('comment'); - $this->builder[ key($this->builder) ][ '_comment' ] = $comment; - - return $this; - } - - /** - * Enregistre le champ précédent comme acceptant la valeur NULL. - * - * @return $this - */ - public function nullable(): self - { - $this->checkPreviousBuild('nullable'); - $this->builder[ key($this->builder) ][ 'nullable' ] = true; - - return $this; - } - - /** - * Enregistre le champ précédent (uniquement de type integer) comme étant non signié. - * - * @throws ColumnsValueException - * - * @return $this - */ - public function unsigned(): self + public function getTable(): Table { - $current = $this->checkPreviousBuild('unsigned'); - if ($current[ 'type' ] !== self::TYPE_INT) { - throw new ColumnsValueException( - sprintf('Impossiblie of unsigned type %s only integer.', $current[ 'type' ]) - ); - } - - $this->builder[ key($this->builder) ][ 'unsigned' ] = true; - - return $this; + return $this->table; } /** - * Enregistre une valeur par défaut au champ précédent. - * Lève une exception si la valeur par défaut ne correspond pas au type de valeur passée en paramètre. + * Créer une table à partir d'un tableau de données. * - * @param bool|null|numeric|string $value Valeur à tester. + * @param string $table Nom de la table. + * @param array $data Donnaées pour créer une table. * * @throws TableBuilderException - * - * @return $this - */ - public function valueDefault($value): self - { - $current = $this->checkPreviousBuild('value default'); - $type = $current[ 'type' ]; - - if ($type === self::TYPE_INCREMENT) { - throw new TableBuilderException('An incremental type column can not have a default value.'); - } - - $name = (string) key($this->builder); - - $this->builder[ $name ][ 'default' ] = self::filterValue($name, $type, $value, $current); - - return $this; - } - - /** - * Retourne la valeur s'il correspond au type déclaré. - * Sinon déclenche une exception. - * - * @param string $name Nom du champ. - * @param string $type Type de donnée (string|text|int|float|bool|char|date|datetime). - * @param bool|null|numeric|string $value Valeur à tester. - * @param array $args Arguments de tests optionnels (length). - * - * @throws ColumnsValueException - * - * @return bool|null|numeric|string - */ - public static function filterValue(string $name, string $type, $value, array $args = []) - { - $error = sprintf( - 'The default value (%s) for column %s does not correspond to type %s.', - $value, - $name, - $type - ); - - switch (strtolower($type)) { - case self::TYPE_STRING: - case self::TYPE_CHAR: - if (!\is_string($value)) { - throw new ColumnsValueException($error); - } - if (!isset($args[ 'length' ]) || strlen($value) > $args[ 'length' ]) { - throw new ColumnsValueException('The default value is larger than the specified size.'); - } - - break; - case self::TYPE_TEXT: - if (!\is_string($value)) { - throw new ColumnsValueException($error); - } - - break; - case self::TYPE_INT: - case self::TYPE_INCREMENT: - if (!\is_int($value)) { - throw new ColumnsValueException($error); - } - - return (int) $value; - case self::TYPE_FLOAT: - if (!\is_float($value)) { - throw new ColumnsValueException($error); - } - - return (float) $value; - case self::TYPE_BOOL: - if (!\is_bool($value)) { - throw new ColumnsValueException($error); - } - - break; - case self::TYPE_DATE: - if (!\is_string($value)) { - throw new ColumnsValueException($error); - } - $value = (string) $value; - if (strtolower($value) === self::CURRENT_DATE_DEFAULT) { - return self::CURRENT_DATE_DEFAULT; - } - if (($timestamp = strtotime($value))) { - return date('Y-m-d', $timestamp); - } - - throw new ColumnsValueException($error); - case self::TYPE_DATETIME: - if (!\is_string($value)) { - throw new ColumnsValueException($error); - } - $value = (string) $value; - if (strtolower($value) === self::CURRENT_DATETIME_DEFAULT) { - return self::CURRENT_DATETIME_DEFAULT; - } - if (($timestamp = strtotime($value))) { - return date('Y-m-d H:i:s', $timestamp); - } - - throw new ColumnsValueException($error); - default: - throw new ColumnsValueException( - sprintf('Type %s not supported', $type) - ); - } - - return $value; - } - - /** - * Retourne le tableau contenant les configurations - * - * @return array Les configurations. - */ - public function build(): array - { - return $this->builder; - } - - /** - * Retour le schéma de la table. - * - * @return array - */ - public function getTableSchema(): array - { - return [ - 'fields' => $this->builder, - 'increments' => $this->increment - ]; - } - - /** - * Retourne le champs courant. - * Déclenche une exception si le champ courant n'existe pas ou - * si le champ courant est une opération. - * - * @param string $opt Nom de l'opération réalisé. - * - * @throws ColumnsNotFoundException - * - * @return array Paramètres du champ. - */ - protected function checkPreviousBuild(string $opt): array - { - $current = end($this->builder); - if (!$current) { - throw new ColumnsNotFoundException( - sprintf('No column selected for %s.', $opt) - ); + * @return Table + */ + public static function createTableFromArray(string $table, array $data): Table + { + $tableBuilder = new self($table); + foreach ($data[ 'fields' ] as $name => $value) { + switch ($value[ 'type' ]) { + case BoolType::TYPE: + case DateType::TYPE: + case DateTimeType::TYPE: + case FloatType::TYPE: + case IncrementType::TYPE: + case TextType::TYPE: + $field = $tableBuilder->{$value[ 'type' ]}($name); + + break; + case CharType::TYPE: + case StringType::TYPE: + $field = $tableBuilder->{$value[ 'type' ]}($name, $value[ 'length' ] ?? 0); + + break; + case IntType::TYPE: + $field = $tableBuilder->{$value[ 'type' ]}($name); + + if (isset($value[ 'unsigned' ])) { + $field->unsigned(); + } + + break; + default: + throw new TableBuilderException(sprintf('Type %s not supported.', $value[ 'type' ])); + } + + if (isset($value[ 'nullable' ])) { + $field->nullable(); + } + if (isset($value[ 'default' ])) { + $field->valueDefault($value[ 'default' ]); + } + if (isset($value[ '_comment' ])) { + $field->comment($value[ '_comment' ]); + } } + $tableBuilder->table->setIncrement($data[ 'increments' ] ?? null); - return $current; + return $tableBuilder->table; } } diff --git a/tests/unit/RequestExecuteTest.php b/tests/unit/RequestExecuteTest.php index fff2cfc..bec340f 100644 --- a/tests/unit/RequestExecuteTest.php +++ b/tests/unit/RequestExecuteTest.php @@ -56,17 +56,17 @@ protected function setUp(): void public function testCreateTable(): void { $this->bdd->createTable('user', static function (TableBuilder $table): void { - $table->increments('id') - ->string('name')->nullable() - ->string('firstname')->nullable(); + $table->increments('id'); + $table->string('name')->nullable(); + $table->string('firstname')->nullable(); }); $this->bdd->createTable('user_role', static function (TableBuilder $table): void { - $table->integer('id_user') - ->integer('id_role'); + $table->integer('id_user'); + $table->integer('id_role'); }); $this->bdd->createTable('role', static function (TableBuilder $table): void { - $table->increments('id_role') - ->string('labelle'); + $table->increments('id_role'); + $table->string('labelle'); }); self::assertFileExists(self::ROOT . 'user.' . $this->bdd->getExtension()); diff --git a/tests/unit/SchemaJsonTest.php b/tests/unit/SchemaJsonTest.php index d46a4f6..1404cdb 100644 --- a/tests/unit/SchemaJsonTest.php +++ b/tests/unit/SchemaJsonTest.php @@ -31,9 +31,9 @@ protected function setUp(): void $this->request = new Request($this->bdd); $this->bdd->createTableIfNotExists('test', static function (TableBuilder $table): void { - $table->increments('id') - ->string('name') - ->string('firstname'); + $table->increments('id'); + $table->string('name'); + $table->string('firstname'); }); $this->request->insertInto('test', [ 'name', 'firstname' ]) @@ -43,8 +43,8 @@ protected function setUp(): void ->execute(); $this->bdd->createTable('test_second', static function (TableBuilder $table): void { - $table->integer('value_i') - ->string('value_s'); + $table->integer('value_i'); + $table->string('value_s'); }); $this->request->insertInto('test_second', [ 'value_i', 'value_s' ]) @@ -73,7 +73,12 @@ protected function tearDown(): void public function testGetSchema(): void { - self::assertEquals($this->bdd->getSchema(), [ + $schema = []; + foreach ($this->bdd->getSchema() as $name => $table) { + $schema[$name] = $table->toArray(); + } + + self::assertEquals($schema, [ 'test' => [ 'fields' => [ 'id' => [ 'type' => 'increments' ], @@ -94,7 +99,7 @@ public function testGetSchema(): void public function testGetSchemaTable(): void { - self::assertEquals($this->bdd->getSchemaTable('test'), [ + self::assertEquals($this->bdd->getTableSchema('test')->toArray(), [ 'fields' => [ 'id' => [ 'type' => 'increments' ], 'name' => [ 'type' => 'string', 'length' => 255 ], @@ -135,19 +140,21 @@ public function testSetIncrementsException(): void public function testAlterTableAdd(): void { $this->bdd->alterTable('test', static function (TableAlter $table): void { - $table - ->string('field_s_default')->valueDefault('foo') - ->string('field_s_null')->nullable() - ->string('field_s'); + $table->string('field_s_default')->valueDefault('foo'); + $table->string('field_s_null')->nullable(); + $table->string('field_s'); }); - self::assertEquals($this->bdd->getSchemaTable('test')[ 'fields' ], [ - 'id' => [ 'type' => 'increments' ], - 'name' => [ 'type' => 'string', 'length' => 255 ], - 'firstname' => [ 'type' => 'string', 'length' => 255 ], - 'field_s_default' => [ 'type' => 'string', 'length' => 255, 'default' => 'foo' ], - 'field_s_null' => [ 'type' => 'string', 'length' => 255, 'nullable' => true ], - 'field_s' => [ 'type' => 'string', 'length' => 255 ] + self::assertEquals($this->bdd->getTableSchema('test')->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'increments' ], + 'name' => [ 'type' => 'string', 'length' => 255 ], + 'firstname' => [ 'type' => 'string', 'length' => 255 ], + 'field_s_default' => [ 'type' => 'string', 'length' => 255, 'default' => 'foo' ], + 'field_s_null' => [ 'type' => 'string', 'length' => 255, 'nullable' => true ], + 'field_s' => [ 'type' => 'string', 'length' => 255 ] + ], + 'increments' => 3 ]); self::assertEquals($this->bdd->read('test'), [ [ @@ -181,7 +188,7 @@ public function testAlterTableAddIncrement(): void $table->increments('id'); }); - self::assertEquals($this->bdd->getSchemaTable('test_second'), [ + self::assertEquals($this->bdd->getTableSchema('test_second')->toArray(), [ 'fields' => [ 'id' => [ 'type' => 'increments' ], 'value_i' => [ 'type' => 'integer' ], @@ -213,10 +220,13 @@ public function testAlterTableRename(): void $table->renameColumn('name', '_name'); }); - self::assertEquals($this->bdd->getSchemaTable('test')[ 'fields' ], [ - 'id' => [ 'type' => 'increments' ], - '_name' => [ 'type' => 'string', 'length' => 255 ], - 'firstname' => [ 'type' => 'string', 'length' => 255 ] + self::assertEquals($this->bdd->getTableSchema('test')->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'increments' ], + '_name' => [ 'type' => 'string', 'length' => 255 ], + 'firstname' => [ 'type' => 'string', 'length' => 255 ] + ], + 'increments' => 3 ]); self::assertEquals($this->bdd->read('test'), [ [ @@ -241,9 +251,12 @@ public function testAlterTableModify(): void $table->float('value_i')->valueDefault(1.0)->modify(); }); - self::assertEquals($this->bdd->getSchemaTable('test_second')[ 'fields' ], [ - 'value_i' => [ 'type' => 'float', 'default' => 1.0 ], - 'value_s' => [ 'type' => 'string', 'length' => 255 ] + self::assertEquals($this->bdd->getTableSchema('test_second')->toArray(), [ + 'fields' => [ + 'value_i' => [ 'type' => 'float', 'default' => 1.0 ], + 'value_s' => [ 'type' => 'string', 'length' => 255 ] + ], + 'increments' => null ]); self::assertEquals($this->bdd->read('test_second'), [ [ 'value_i' => 1, 'value_s' => 'value1' ], @@ -258,7 +271,7 @@ public function testAlterTableModifyIncrement(): void $table->increments('value_i')->modify(); }); - self::assertEquals($this->bdd->getSchemaTable('test_second'), [ + self::assertEquals($this->bdd->getTableSchema('test_second')->toArray(), [ 'fields' => [ 'value_i' => [ 'type' => 'increments' ], 'value_s' => [ 'type' => 'string', 'length' => 255 ] @@ -275,11 +288,11 @@ public function testAlterTableModifyIncrement(): void public function testAlterTableDrop(): void { $this->bdd->alterTable('test', static function (TableAlter $table): void { - $table->dropColumn('id') - ->dropColumn('firstname'); + $table->dropColumn('id'); + $table->dropColumn('firstname'); }); - self::assertEquals($this->bdd->getSchemaTable('test'), [ + self::assertEquals($this->bdd->getTableSchema('test')->toArray(), [ 'fields' => [ 'name' => [ 'type' => 'string', 'length' => 255 ] ], @@ -385,7 +398,7 @@ public function testTruncateTable(): void { $output = $this->bdd->truncateTable('test'); - self::assertEquals($this->bdd->getSchemaTable('test'), [ + self::assertEquals($this->bdd->getTableSchema('test')->toArray(), [ 'fields' => [ 'id' => [ 'type' => 'increments' ], 'name' => [ 'type' => 'string', 'length' => 255 ], diff --git a/tests/unit/TableAlterTest.php b/tests/unit/TableAlterTest.php index 1035fa8..6fa8d58 100644 --- a/tests/unit/TableAlterTest.php +++ b/tests/unit/TableAlterTest.php @@ -2,6 +2,7 @@ namespace Queryflatfile\Tests\unit; +use Queryflatfile\Field; use Queryflatfile\TableAlter; class TableAlterTest extends \PHPUnit\Framework\TestCase @@ -13,15 +14,18 @@ class TableAlterTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $this->object = new TableAlter; + $this->object = new TableAlter('test'); } public function testDrop(): void { $this->object->dropColumn('0'); - self::assertEquals($this->object->build(), [ - '0' => [ 'opt' => TableAlter::OPT_DROP ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + '0' => [ 'opt' => Field::OPT_DROP ] + ], + 'increments' => null ]); } @@ -29,8 +33,11 @@ public function testRename(): void { $this->object->renameColumn('0', '1'); - self::assertEquals($this->object->build(), [ - '0' => [ 'opt' => TableAlter::OPT_RENAME, 'to' => '1' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + '0' => [ 'opt' => Field::OPT_RENAME, 'to' => '1' ] + ], + 'increments' => null ]); } @@ -38,29 +45,11 @@ public function testModify(): void { $this->object->char('0')->modify(); - self::assertEquals($this->object->build(), [ - '0' => [ 'type' => 'char', 'length' => 1, 'opt' => TableAlter::OPT_MODIFY ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + '0' => [ 'type' => 'char', 'length' => 1 ] + ], + 'increments' => null ]); } - - public function testDropException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('No column selected for value default.'); - $this->object->dropColumn('0')->valueDefault('test'); - } - - public function testRenameException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('No column selected for value default.'); - $this->object->renameColumn('0', '1')->valueDefault('test'); - } - - public function testModifyException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('No column selected for value default.'); - $this->object->char('0')->modify()->valueDefault('test'); - } } diff --git a/tests/unit/TableBuilderTest.php b/tests/unit/TableBuilderTest.php index b5ae367..32816ed 100644 --- a/tests/unit/TableBuilderTest.php +++ b/tests/unit/TableBuilderTest.php @@ -2,6 +2,7 @@ namespace Queryflatfile\Tests\unit; +use Queryflatfile\Exception\TableBuilder\ColumnsValueException; use Queryflatfile\TableBuilder; class TableBuilderTest extends \PHPUnit\Framework\TestCase @@ -13,15 +14,18 @@ class TableBuilderTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $this->object = new TableBuilder; + $this->object = new TableBuilder('test'); } public function testIncrements(): void { $this->object->increments('id'); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'increments' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'increments' ] + ], + 'increments' => 0 ]); } @@ -31,20 +35,21 @@ public function testIncrementsException(): void $this->expectExceptionMessage( 'Only one incremental column is allowed per table.' ); - $this->object - ->increments('id') - ->increments('error'); + $this->object->increments('id'); + $this->object->increments('error'); } public function testChar(): void { - $this->object - ->char('id') - ->char('id2', 2); + $this->object->char('id'); + $this->object->char('id2', 2); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'char', 'length' => 1 ], - 'id2' => [ 'type' => 'char', 'length' => 2 ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'char', 'length' => 1 ], + 'id2' => [ 'type' => 'char', 'length' => 2 ] + ], + 'increments' => null ]); } @@ -61,20 +66,25 @@ public function testText(): void { $this->object->text('id'); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'text' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'text' ] + ], + 'increments' => null ]); } public function testString(): void { - $this->object - ->string('id') - ->string('id2', 256); + $this->object->string('id'); + $this->object->string('id2', 256); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'string', 'length' => 255 ], - 'id2' => [ 'type' => 'string', 'length' => 256 ], + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'string', 'length' => 255 ], + 'id2' => [ 'type' => 'string', 'length' => 256 ], + ], + 'increments' => null ]); } @@ -91,8 +101,11 @@ public function testInteger(): void { $this->object->integer('id'); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'integer' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'integer' ] + ], + 'increments' => null ]); } @@ -100,8 +113,11 @@ public function testFloat(): void { $this->object->float('id'); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'float' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'float' ] + ], + 'increments' => null ]); } @@ -109,8 +125,11 @@ public function testBoolean(): void { $this->object->boolean('id'); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'boolean' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'boolean' ] + ], + 'increments' => null ]); } @@ -118,8 +137,11 @@ public function testDate(): void { $this->object->date('id'); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'date' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'date' ] + ], + 'increments' => null ]); } @@ -127,216 +149,251 @@ public function testDatetime(): void { $this->object->datetime('id'); - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'datetime' ] + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'datetime' ] + ], + 'increments' => null ]); } public function testNullable(): void { - $this->object - ->increments('0')->nullable() - ->char('1')->nullable() - ->text('2')->nullable() - ->string('3')->nullable() - ->integer('4')->nullable() - ->float('5')->nullable() - ->boolean('6')->nullable() - ->date('7')->nullable() - ->datetime('8')->nullable(); - - self::assertEquals($this->object->build(), [ - '0' => [ 'type' => 'increments', 'nullable' => true ], - '1' => [ 'type' => 'char', 'length' => 1, 'nullable' => true ], - '2' => [ 'type' => 'text', 'nullable' => true ], - '3' => [ 'type' => 'string', 'length' => 255, 'nullable' => true ], - '4' => [ 'type' => 'integer', 'nullable' => true ], - '5' => [ 'type' => 'float', 'nullable' => true ], - '6' => [ 'type' => 'boolean', 'nullable' => true ], - '7' => [ 'type' => 'date', 'nullable' => true ], - '8' => [ 'type' => 'datetime', 'nullable' => true ], + $this->object->increments('0')->nullable(); + $this->object->char('1')->nullable(); + $this->object->text('2')->nullable(); + $this->object->string('3')->nullable(); + $this->object->integer('4')->nullable(); + $this->object->float('5')->nullable(); + $this->object->boolean('6')->nullable(); + $this->object->date('7')->nullable(); + $this->object->datetime('8')->nullable(); + + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + '0' => [ 'type' => 'increments', 'nullable' => true ], + '1' => [ 'type' => 'char', 'length' => 1, 'nullable' => true ], + '2' => [ 'type' => 'text', 'nullable' => true ], + '3' => [ 'type' => 'string', 'length' => 255, 'nullable' => true ], + '4' => [ 'type' => 'integer', 'nullable' => true ], + '5' => [ 'type' => 'float', 'nullable' => true ], + '6' => [ 'type' => 'boolean', 'nullable' => true ], + '7' => [ 'type' => 'date', 'nullable' => true ], + '8' => [ 'type' => 'datetime', 'nullable' => true ], + ], + 'increments' => 0 ]); + self::assertEquals(null, $this->object->getTable()->getField('7')->getValueDefault()); + self::assertEquals(null, $this->object->getTable()->getField('8')->getValueDefault()); } - public function testNullableException(): void + public function testDateNullableException(): void { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('No column selected for nullable.'); - $this->object->nullable(); - } + $this->object->date('7'); - public function testUnsigned(): void - { - $this->object->integer('id')->unsigned(); - - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'integer', 'unsigned' => true ] - ]); - } - - public function testUnsignedException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('No column selected for unsigned.'); - $this->object->unsigned(); - } - - public function testUnsignedTypeException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'Impossiblie of unsigned type string only integer.' - ); - $this->object->string('id')->unsigned(); - } - - public function testComment(): void - { - $this->object->increments('id')->comment('identifiant'); - - self::assertEquals($this->object->build(), [ - 'id' => [ 'type' => 'increments', '_comment' => 'identifiant' ] - ]); - } - - public function testCommentException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('No column selected for comment.'); - $this->object->comment('identifiant'); - } - - public function testValueDefault(): void - { - $this->object - ->increments('0') - ->char('1')->valueDefault('a') - ->text('2')->valueDefault('test') - ->string('3')->valueDefault('test') - ->integer('4')->valueDefault(1) - ->float('5')->valueDefault(1.1) - ->boolean('6')->valueDefault(true) - ->date('7')->valueDefault('2017-11-26') - ->date('7.1')->valueDefault('current_date') - ->datetime('8')->valueDefault('2017-11-26 22:00:00') - ->datetime('8.1')->valueDefault('current_datetime'); - - self::assertEquals($this->object->build(), [ - '0' => [ 'type' => 'increments' ], - '1' => [ 'type' => 'char', 'length' => 1, 'default' => 'a' ], - '2' => [ 'type' => 'text', 'default' => 'test' ], - '3' => [ 'type' => 'string', 'length' => 255, 'default' => 'test' ], - '4' => [ 'type' => 'integer', 'default' => 1 ], - '5' => [ 'type' => 'float', 'default' => 1.1 ], - '6' => [ 'type' => 'boolean', 'default' => true ], - '7' => [ 'type' => 'date', 'default' => '2017-11-26' ], - '7.1' => [ 'type' => 'date', 'default' => 'current_date' ], - '8' => [ 'type' => 'datetime', 'default' => '2017-11-26 22:00:00' ], - '8.1' => [ 'type' => 'datetime', 'default' => 'current_datetime' ], - ]); - } - - public function testValueDefaultException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('No column selected for value default.'); - $this->object->valueDefault('1'); - } - - public function testValueDefaultIncrementException(): void - { $this->expectException(\Exception::class); $this->expectExceptionMessage( - 'An incremental type column can not have a default value.' + '7 not nullable or not default.' ); - $this->object->increments('0')->valueDefault(2); + self::assertEquals(null, $this->object->getTable()->getField('7')->getValueDefault()); } - public function testValueDefaultCharException(): void + public function testDatetimeNullableException(): void { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'The default value (1) for column 0 does not correspond to type char.' - ); - $this->object->char('0')->valueDefault(1); - } + $this->object->datetime('8'); - public function testValueDefaultCharLenghtException(): void - { $this->expectException(\Exception::class); $this->expectExceptionMessage( - 'The default value is larger than the specified size.' + '8 not nullable or not default.' ); - $this->object->char('0')->valueDefault('error'); + self::assertEquals(null, $this->object->getTable()->getField('8')->getValueDefault()); } - public function testValueDefaultTextException(): void + public function testUnsigned(): void { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'The default value (1) for column 0 does not correspond to type text.' - ); - $this->object->text('0')->valueDefault(1); - } + $this->object->integer('id')->unsigned(); - public function testValueDefaultStringException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'The default value (1) for column 0 does not correspond to type string.' - ); - $this->object->string('0')->valueDefault(1); + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'integer', 'unsigned' => true ] + ], + 'increments' => null + ]); } - public function testValueDefaultIntegerException(): void + public function testComment(): void { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'The default value (error) for column 0 does not correspond to type integer.' - ); - $this->object->integer('0')->valueDefault('error'); - } + $this->object->increments('id')->comment('identifiant'); - public function testValueDefaultFloatException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'The default value (error) for column 0 does not correspond to type float.' - ); - $this->object->float('0')->valueDefault('error'); + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + 'id' => [ 'type' => 'increments', '_comment' => 'identifiant' ] + ], + 'increments' => null + ]); } - public function testValueDefaultBoolException(): void + public function testValueDefault(): void { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'The default value (1) for column 0 does not correspond to type boolean.' - ); - $this->object->boolean('0')->valueDefault('1'); + $this->object->increments('0'); + $this->object->char('1')->valueDefault('a'); + $this->object->text('2')->valueDefault('test'); + $this->object->string('3')->valueDefault('test'); + $this->object->integer('4')->valueDefault(1); + $this->object->float('5')->valueDefault(1.1); + $this->object->boolean('6')->valueDefault(true); + $this->object->date('7')->valueDefault('2017-11-26'); + $this->object->date('7.1')->valueDefault('current_date'); + $this->object->datetime('8')->valueDefault('2017-11-26 22:00:00'); + $this->object->datetime('8.1')->valueDefault('current_datetime'); + + self::assertEquals($this->object->getTable()->toArray(), [ + 'fields' => [ + '0' => [ 'type' => 'increments' ], + '1' => [ 'type' => 'char', 'length' => 1, 'default' => 'a' ], + '2' => [ 'type' => 'text', 'default' => 'test' ], + '3' => [ 'type' => 'string', 'length' => 255, 'default' => 'test' ], + '4' => [ 'type' => 'integer', 'default' => 1 ], + '5' => [ 'type' => 'float', 'default' => 1.1 ], + '6' => [ 'type' => 'boolean', 'default' => true ], + '7' => [ 'type' => 'date', 'default' => '2017-11-26' ], + '7.1' => [ 'type' => 'date', 'default' => 'current_date' ], + '8' => [ 'type' => 'datetime', 'default' => '2017-11-26 22:00:00' ], + '8.1' => [ 'type' => 'datetime', 'default' => 'current_datetime' ], + ], + 'increments' => 0 + ]); + self::assertEquals('2017-11-26', $this->object->getTable()->getField('7')->getValueDefault()); + self::assertEquals(date('Y-m-d', time()), $this->object->getTable()->getField('7.1')->getValueDefault()); + self::assertEquals('2017-11-26 22:00:00', $this->object->getTable()->getField('8')->getValueDefault()); + self::assertEquals(date('Y-m-d H:i:s', time()), $this->object->getTable()->getField('8.1')->getValueDefault()); } - public function testValueDefaultDateException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage( - 'The default value (1) for column 0 does not correspond to type date.' + /** + * @param class-string<\Throwable> $exceptionClass + * @param mixed $valueDefault + * + * @dataProvider getValueDefaultException + */ + public function testValueDefaulException( + string $method, + $valueDefault, + string $exceptionClass, + string $exceptionMessage + ): void { + $tableBuilder = new TableBuilder('test'); + + $this->expectException($exceptionClass); + $this->expectExceptionMessage($exceptionMessage); + $tableBuilder->$method('0')->valueDefault($valueDefault); + } + + public function getValueDefaultException(): \Generator + { + yield [ + 'boolean', 1, + \InvalidArgumentException::class, 'The value of the 0 field must be of type boolean: integer given.' + ]; + yield [ + 'char', 1, + \InvalidArgumentException::class, 'The value of the 0 field must be of type string: integer given.' + ]; + yield [ + 'char', 'error', + \LengthException::class, 'The value of the 0 field must be less than or equal to 1 characters: 5 given' + ]; + yield [ + 'date', 1, + \InvalidArgumentException::class, 'The value of the 0 field must be of type string: integer given.' + ]; + yield [ + 'date', '1', + ColumnsValueException::class, 'The value of the 0 field must be a valid date: 1 given' + ]; + yield [ + 'datetime', 1, + \InvalidArgumentException::class, 'The value of the 0 field must be of type string: integer given.' + ]; + yield [ + 'datetime', '1', + ColumnsValueException::class, 'The value of the 0 field must be a valid date: 1 given' + ]; + yield [ + 'float', '1', + \InvalidArgumentException::class, 'The value of the 0 field must be of type float: string given.' + ]; + yield [ + 'increments', 2, + \Exception::class, 'An incremental type column can not have a default value.' + ]; + yield [ + 'integer', '1', + \InvalidArgumentException::class, 'The value of the 0 field must be of type integer: string given.' + ]; + yield [ + 'string', 1, + \InvalidArgumentException::class, 'The value of the 0 field must be of type string: integer given.' + ]; + yield [ + 'string', str_repeat('0', 256), + \LengthException::class, 'The value of the 0 field must be less than or equal to 255 characters: 256 given' + ]; + yield [ + 'text', 1, + \InvalidArgumentException::class, 'The value of the 0 field must be of type string: integer given.' + ]; + } + + public function testCreateTableFromArray(): void + { + $this->object->increments('field_0'); + $this->object->char('field_1')->valueDefault('a'); + $this->object->text('field_2')->valueDefault('test'); + $this->object->string('field_3')->valueDefault('test'); + $this->object->integer('field_4')->valueDefault(1)->unsigned(); + $this->object->float('field_5')->valueDefault(1.1); + $this->object->boolean('field_6')->valueDefault(true); + $this->object->date('field_7')->valueDefault('2017-11-26'); + $this->object->date('field_7.1')->valueDefault('current_date'); + $this->object->datetime('field_8')->valueDefault('2017-11-26 22:00:00'); + $this->object->datetime('field_8.1')->valueDefault('current_datetime'); + + $expected = [ + 'test' => [ + 'fields' => [ + 'field_0' => [ 'type' => 'increments' ], + 'field_1' => [ 'type' => 'char', 'length' => 1, 'default' => 'a' ], + 'field_2' => [ 'type' => 'text', 'default' => 'test' ], + 'field_3' => [ 'type' => 'string', 'length' => 255, 'default' => 'test' ], + 'field_4' => [ 'type' => 'integer', 'default' => 1, 'unsigned' => true ], + 'field_5' => [ 'type' => 'float', 'default' => 1.1 ], + 'field_6' => [ 'type' => 'boolean', 'default' => true ], + 'field_7' => [ 'type' => 'date', 'default' => '2017-11-26' ], + 'field_7.1' => [ 'type' => 'date', 'default' => 'current_date' ], + 'field_8' => [ 'type' => 'datetime', 'default' => '2017-11-26 22:00:00' ], + 'field_8.1' => [ 'type' => 'datetime', 'default' => 'current_datetime' ], + ], + 'increments' => 0 + ] + ]; + + self::assertEquals( + $this->object->getTable(), + TableBuilder::createTableFromArray('test', $expected[ 'test' ]) ); - $this->object->date('0')->valueDefault('1'); } - public function testValueDefaultDatetimesException(): void + public function testCreateTableFromArrayException(): void { + $data = [ + 'fields' => [ + 'field_0' => [ 'type' => 'error' ] + ] + ]; $this->expectException(\Exception::class); $this->expectExceptionMessage( - 'The default value (1) for column 0 does not correspond to type datetime.' + 'Type error not supported.' ); - $this->object->datetime('0')->valueDefault('1'); - } - - public function testCheckValueException(): void - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Type error not supported'); - TableBuilder::filterValue('testName', 'error', 'testValue'); + TableBuilder::createTableFromArray('test', $data); } } From c1cfd320bb9e2a5112729bfbd309eea3348e551b Mon Sep 17 00:00:00 2001 From: noelma Date: Sat, 4 Dec 2021 20:53:37 +0100 Subject: [PATCH 12/12] feat: adding group methods to the where object --- src/RequestHandler.php | 60 +++++++++++++++++++--------- src/RequestInterface.php | 8 ++-- src/Where.php | 6 +-- src/WhereHandler.php | 82 +++++++++++++++++++++++--------------- tests/unit/RequestTest.php | 10 ++--- 5 files changed, 102 insertions(+), 64 deletions(-) diff --git a/src/RequestHandler.php b/src/RequestHandler.php index fffa18e..e63abed 100644 --- a/src/RequestHandler.php +++ b/src/RequestHandler.php @@ -17,10 +17,10 @@ * * @author Mathieu NOËL * - * @method Request where(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request notWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where - * @method Request orNotWhere(\Closure|string $column, null|string $operator = null, null|numeric|string $value = null) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request where(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notWhere(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orWhere(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotWhere(string $column, string $operator, null|scalar $value) Alias de la fonction de l'objet Queryflatfile\Where * * @method Request between(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where * @method Request orBetween(string $column, numeric|string $min, numeric|string $max) Alias de la fonction de l'objet Queryflatfile\Where @@ -41,6 +41,11 @@ * @method Request orRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where * @method Request notRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where * @method Request orNotRegex(string $column, string $pattern) Alias de la fonction de l'objet Queryflatfile\Where + * + * @method Request whereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request notWhereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orWhereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where + * @method Request orNotWhereGroup(\Closure $callable) Alias de la fonction de l'objet Queryflatfile\Where */ abstract class RequestHandler implements RequestInterface { @@ -187,8 +192,16 @@ public function insertInto(string $table, array $columns) /** * {@inheritdoc} */ - public function leftJoin(string $table, $column, ?string $operator = null, ?string $value = null) + public function leftJoin(string $table, $column, ?string $operator = null, $value = null) { + if ($column instanceof \Closure) { + $this->joinGroup(self::JOIN_LEFT, $table, $column); + + return $this; + } + if (!is_string($column) || !is_string($operator)) { + throw new \InvalidArgumentException(''); + } $this->join(self::JOIN_LEFT, $table, $column, $operator, $value); return $this; @@ -219,8 +232,16 @@ public function orderBy(string $columns, int $order = SORT_ASC) /** * {@inheritdoc} */ - public function rightJoin(string $table, $column, ?string $operator = null, ?string $value = null) + public function rightJoin(string $table, $column, ?string $operator = null, $value = null) { + if ($column instanceof \Closure) { + $this->joinGroup(self::JOIN_RIGHT, $table, $column); + + return $this; + } + if (!is_string($column) || !is_string($operator)) { + throw new \InvalidArgumentException(''); + } $this->join(self::JOIN_RIGHT, $table, $column, $operator, $value); return $this; @@ -302,22 +323,23 @@ protected function init() /** * Enregistre une jointure. * - * @param string $type Type de la jointure. - * @param string $table Nom de la table à joindre - * @param string|\Closure $column Nom de la colonne d'une des tables précédentes - * ou une closure pour affiner les conditions. - * @param string|null $operator Opérateur logique ou null pour une closure. - * @param string|null $value Valeur - * ou une colonne de la table jointe (au format nom_table.colonne) - * ou null pour une closure. + * @param string $type Type de la jointure. + * @param string $table Nom de la table à joindre + * @param string $column Nom de la colonne d'une des tables précédentes. + * @param string $operator Opérateur logique ou null pour une closure. + * @param null|scalar $value Valeur ou une colonne de la table jointe (au format nom_table.colonne) */ - private function join(string $type, string $table, $column, ?string $operator = null, ?string $value = null): void + private function join(string $type, string $table, string $column, string $operator, $value): void { - $where = new Where(); + $where = (new Where())->where($column, $operator, $value); + + $this->joins[] = compact('type', 'table', 'where'); + } - $column instanceof \Closure - ? call_user_func_array($column, [ &$where ]) - : $where->where($column, $operator, $value); + private function joinGroup(string $type, string $table, \Closure $callable): void + { + $where = new Where(); + call_user_func_array($callable, [ &$where ]); $this->joins[] = compact('type', 'table', 'where'); } diff --git a/src/RequestInterface.php b/src/RequestInterface.php index 2e47425..2c7b009 100644 --- a/src/RequestInterface.php +++ b/src/RequestInterface.php @@ -71,13 +71,13 @@ public function from(string $table); * @param string|\Closure $column Nom de la colonne d'une des tables précédentes * ou une closure pour affiner les conditions. * @param string|null $operator Opérateur logique ou null pour une closure. - * @param string|null $value Valeur + * @param scalar|null $value Valeur * ou une colonne de la table jointe (au format nom_table.colonne) * ou null pour une closure. * * @return $this */ - public function leftJoin(string $table, $column, ?string $operator = null, ?string $value = null); + public function leftJoin(string $table, $column, ?string $operator = null, $value = null); /** * Enregistre une jointure droite. @@ -86,13 +86,13 @@ public function leftJoin(string $table, $column, ?string $operator = null, ?stri * @param string|\Closure $column Nom de la colonne d'une des tables précédentes * ou une closure pour affiner les conditions. * @param string|null $operator Opérateur logique ou null pour une closure. - * @param string|null $value Valeur + * @param scalar|null $value Valeur * ou une colonne de la table jointe (au format nom_table.colonne) * ou null pour une closure. * * @return $this */ - public function rightJoin(string $table, $column, ?string $operator = null, ?string $value = null); + public function rightJoin(string $table, $column, ?string $operator = null, $value = null); /** * Enregistre une limitation et un décalage au retour de la requête. diff --git a/src/Where.php b/src/Where.php index d68ad08..1bdfc03 100644 --- a/src/Where.php +++ b/src/Where.php @@ -60,7 +60,7 @@ public function __toString(): string ); break; - case 'whereCallback': + case 'whereGroup': $output .= sprintf('%s(%s) ', $not, $where[ 'value' ]); break; @@ -121,7 +121,7 @@ public function execute(array $row): bool $output = true; foreach ($this->where as $key => $value) { /* Si la clause est standard ou une sous clause. */ - $predicate = $value[ 'type' ] === 'whereCallback' + $predicate = $value[ 'type' ] === 'whereGroup' ? $value[ 'value' ]->execute($row) : self::predicate($row[ $value[ 'column' ] ], $value[ 'condition' ], $value[ 'value' ]); /* Si la clause est inversé. */ @@ -161,7 +161,7 @@ public function executeJoin(array $row, array &$rowTable): bool foreach ($this->where as $key => $value) { $predicate = true; - if ($value[ 'type' ] === 'whereCallback') { + if ($value[ 'type' ] === 'whereGroup') { $predicate = $value[ 'value' ]->executeJoin($row, $rowTable); } else { $val = $rowTable[ self::getColumn($value[ 'value' ]) ]; diff --git a/src/WhereHandler.php b/src/WhereHandler.php index c05d67b..65071ae 100644 --- a/src/WhereHandler.php +++ b/src/WhereHandler.php @@ -49,27 +49,21 @@ class WhereHandler * Ajoute une condition simple pour la requête. * Si la valeur du champ est égal (non égale, supérieur à, ...) par rapport à une valeur. * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|string $operator Type de condition. - * @param null|numeric|string $value Valeur de teste. - * @param string $bool Porte logique de la condition (and|or). - * @param bool $not Inverse la condition. + * @param string $column Nom d'une colonne. + * @param string $operator Type de condition. + * @param null|scalar $value Valeur de teste. + * @param string $bool Porte logique de la condition (and|or). + * @param bool $not Inverse la condition. * * @throws OperatorNotFound The condition is not exist. */ public function where( - $column, - ?string $operator = null, - $value = null, + string $column, + string $operator, + $value, string $bool = self::EXP_AND, bool $not = false ): self { - if ($column instanceof \Closure) { - $this->whereCallback($column, $bool, $not); - - return $this; - } - $condition = $this->filterOperator($operator); if (in_array($condition, [ 'like', 'ilike', 'not like', 'not ilike' ])) { @@ -97,10 +91,9 @@ public function where( /** * Alias inverse de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|numeric|string $value Valeur de teste. + * @param null|scalar $value Valeur de teste. */ - public function notWhere($column, ?string $operator = null, $value = null): self + public function notWhere(string $column, string $operator, $value): self { $this->where($column, $operator, $value, self::EXP_AND, true); @@ -110,10 +103,9 @@ public function notWhere($column, ?string $operator = null, $value = null): self /** * Alias avec la porte logique 'OR' de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|numeric|string $value Valeur de teste. + * @param null|scalar $value Valeur de teste. */ - public function orWhere($column, ?string $operator = null, $value = null): self + public function orWhere(string $column, string $operator, $value): self { $this->where($column, $operator, $value, self::EXP_OR); @@ -123,10 +115,9 @@ public function orWhere($column, ?string $operator = null, $value = null): self /** * Alias inverse avec la porte logique 'OR' de la fonction where(). * - * @param \Closure|string $column Sous condition ou une colonne. - * @param null|numeric|string $value Valeur de teste. + * @param null|scalar $value Valeur de teste. */ - public function orNotWhere($column, ?string $operator = null, $value = null): self + public function orNotWhere(string $column, string $operator, $value): self { $this->where($column, $operator, $value, self::EXP_OR, true); @@ -358,13 +349,13 @@ public function orNotRegex(string $column, string $pattern): self /** * Ajoute une sous-condition pour la requête. */ - protected function whereCallback( - callable $column, + public function whereGroup( + \Closure $callable, string $bool = self::EXP_AND, bool $not = false ): void { $where = new Where(); - call_user_func_array($column, [ &$where ]); + call_user_func_array($callable, [ &$where ]); $this->where[] = [ 'type' => __FUNCTION__, @@ -375,6 +366,36 @@ protected function whereCallback( ]; } + /** + * Alias inverse de la fonction whereGroup(). + */ + public function notWhereGroup(\Closure $callable): self + { + $this->whereGroup($callable, self::EXP_AND, true); + + return $this; + } + + /** + * Alias avec la porte logique 'OR' de la fonction whereGroup(). + */ + public function orWhereGroup(\Closure $callable): self + { + $this->whereGroup($callable, self::EXP_OR); + + return $this; + } + + /** + * Alias inverse avec la porte logique 'OR' de la fonction whereGroup(). + */ + public function orNotWhereGroup(\Closure $callable): self + { + $this->whereGroup($callable, self::EXP_OR, true); + + return $this; + } + /** * Ajoute une condition like pour la requête. * @@ -413,19 +434,14 @@ protected function like( /** * Filtre l'opérateur. * - * @param string|null $operator + * @param string $operator * - * @throws QueryException * @throws OperatorNotFound * * @return string */ - private function filterOperator(?string $operator): string + private function filterOperator(string $operator): string { - if ($operator === null) { - throw new QueryException(); - } - $condition = strtolower($operator); if (!in_array($condition, self::CONDITION)) { diff --git a/tests/unit/RequestTest.php b/tests/unit/RequestTest.php index 5e888d3..cf1287f 100644 --- a/tests/unit/RequestTest.php +++ b/tests/unit/RequestTest.php @@ -946,12 +946,12 @@ public function testOrNotWhere(): void ); } - public function testWhereAndGroup(): void + public function testWhereGroup(): void { $data = $this->request ->from('user') ->where('id', '>=', 2) - ->where(static function (WhereHandler $query): void { + ->whereGroup(static function (WhereHandler $query): void { $query->where('name', '=', 'DUPOND') ->orWhere('firstname', '=', 'Eva'); }); @@ -969,12 +969,12 @@ public function testWhereAndGroup(): void ); } - public function testWhereOrGroup(): void + public function testOrWhereGroup(): void { $data = $this->request ->from('user') ->where('name', '=', 'DUPOND') - ->orWhere(static function (WhereHandler $query): void { + ->orWhereGroup(static function (WhereHandler $query): void { $query->where('firstname', '=', 'Eva') ->orWhere('firstname', '=', 'Mathieu'); }); @@ -1322,7 +1322,7 @@ public function testLeftJoinGroupMultiple(): void ->from('user') ->leftJoin('user_role', 'id', '=', 'user_role.id_user') ->leftJoin('role', static function (WhereHandler $query): void { - $query->where(static function (WhereHandler $query): void { + $query->whereGroup(static function (WhereHandler $query): void { $query->where('id_role', '=', 'role.id_role'); }); })