From 6525eb2f314ca13e13a6782e9cb5e27e60c0909b Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Fri, 9 Sep 2022 10:56:05 -0400 Subject: [PATCH 1/7] FIX: missing autoload definitions --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e16a293..d6e9fc6 100644 --- a/composer.json +++ b/composer.json @@ -11,5 +11,8 @@ "email": "aldarien85@gmail.com" } ], - "require": {} + "require": {}, + "autoload": { + "ProVM\\": "src/" + } } From 6dda8e15153629f07d8c9c237f66045f861da7bd Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Fri, 9 Sep 2022 11:18:36 -0400 Subject: [PATCH 2/7] FIX --- composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d6e9fc6..e70f7c1 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,8 @@ ], "require": {}, "autoload": { - "ProVM\\": "src/" + "psr-4": { + "ProVM\\": "src/" + } } } From 758ff0e282975d9d2eae9f08d135e5d50cb2001d Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Mon, 12 Sep 2022 21:45:14 -0300 Subject: [PATCH 3/7] Added more detail in obtaining first result --- src/Concept/Database/ResultSet.php | 3 ++- src/Database/ResultSet.php | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Concept/Database/ResultSet.php b/src/Concept/Database/ResultSet.php index c2163ee..df7bf0b 100644 --- a/src/Concept/Database/ResultSet.php +++ b/src/Concept/Database/ResultSet.php @@ -9,5 +9,6 @@ interface ResultSet public function execute(array $values): ResultSet; public function getAsArray(): array; public function getAsObject(): array; - public function getFirst(): mixed; + public function getFirstAsArray(): array; + public function getFirstAsObject(): object; } diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index c234bd5..71c3517 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -37,7 +37,11 @@ class ResultSet implements RSInterface { return $this->getStatement()->fetchAll(PDO::FETCH_OBJ); } - public function getFirst(): mixed + public function getFirstAsArray(): array + { + return $this->getStatement()->fetch(PDO::FETCH_ASSOC); + } + public function getFirstAsObject(): object { return $this->getStatement()->fetch(PDO::FETCH_OBJ); } From cc7cd638e38554a192d366a4a93123105a9678d5 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Mon, 12 Sep 2022 21:54:20 -0300 Subject: [PATCH 4/7] Added check for empty or false results. --- src/Database/ResultSet.php | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 71c3517..dfe9aed 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -31,18 +31,34 @@ class ResultSet implements RSInterface public function getAsArray(): array { - return $this->getStatement()->fetchAll(PDO::FETCH_ASSOC); + $rs = $this->getStatement()->fetchAll(PDO::FETCH_ASSOC); + if (!$rs) { + throw new \PDOException("No results found."); + } + return $rs; } public function getAsObject(): array { - return $this->getStatement()->fetchAll(PDO::FETCH_OBJ); + $rs = $this->getStatement()->fetchAll(PDO::FETCH_OBJ); + if (!$rs) { + throw new \PDOException("No results found."); + } + return $rs; } public function getFirstAsArray(): array { - return $this->getStatement()->fetch(PDO::FETCH_ASSOC); + $rs = $this->getStatement()->fetch(PDO::FETCH_ASSOC); + if (!$rs or count($rs) === 0) { + throw new \PDOException("No results found."); + } + return $rs; } public function getFirstAsObject(): object { - return $this->getStatement()->fetch(PDO::FETCH_OBJ); + $rs = $this->getStatement()->fetch(PDO::FETCH_OBJ); + if (!$rs or count($rs) === 0) { + throw new \PDOException("No results found."); + } + return $rs; } } From 2c6c0e6e55198c0d28e210c308b8ff59ba997f16 Mon Sep 17 00:00:00 2001 From: Aldarien Date: Thu, 22 Dec 2022 18:58:26 -0300 Subject: [PATCH 5/7] Added exceptions with base numbering 300 --- src/Database/Exception.php | 11 +++++++++++ src/Exception/BlankResult.php | 15 +++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/Database/Exception.php create mode 100644 src/Exception/BlankResult.php diff --git a/src/Database/Exception.php b/src/Database/Exception.php new file mode 100644 index 0000000..c71a39a --- /dev/null +++ b/src/Database/Exception.php @@ -0,0 +1,11 @@ + Date: Thu, 22 Dec 2022 22:24:55 -0300 Subject: [PATCH 6/7] Use exception in ResultSet --- src/Database/ResultSet.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index dfe9aed..6654eb7 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -4,6 +4,7 @@ namespace ProVM\Database; use PDO; use PDOStatement; use ProVM\Concept\Database\ResultSet as RSInterface; +use ProVM\Exception\BlankResult; class ResultSet implements RSInterface { @@ -33,7 +34,7 @@ class ResultSet implements RSInterface { $rs = $this->getStatement()->fetchAll(PDO::FETCH_ASSOC); if (!$rs) { - throw new \PDOException("No results found."); + throw new BlankResult(); } return $rs; } @@ -41,7 +42,7 @@ class ResultSet implements RSInterface { $rs = $this->getStatement()->fetchAll(PDO::FETCH_OBJ); if (!$rs) { - throw new \PDOException("No results found."); + throw new BlankResult(); } return $rs; } @@ -49,7 +50,7 @@ class ResultSet implements RSInterface { $rs = $this->getStatement()->fetch(PDO::FETCH_ASSOC); if (!$rs or count($rs) === 0) { - throw new \PDOException("No results found."); + throw new BlankResult(); } return $rs; } @@ -57,7 +58,7 @@ class ResultSet implements RSInterface { $rs = $this->getStatement()->fetch(PDO::FETCH_OBJ); if (!$rs or count($rs) === 0) { - throw new \PDOException("No results found."); + throw new BlankResult(); } return $rs; } From 99344fda7dd55585adb361f19bc80448f8272eb5 Mon Sep 17 00:00:00 2001 From: Aldarien Date: Tue, 28 Feb 2023 23:41:51 -0300 Subject: [PATCH 7/7] Reorder and Added missing databases --- Readme.md | 35 +++++--- composer.json | 15 ++-- src/Alias/Database.php | 58 -------------- src/Concept/Database.php | 18 ++--- src/Concept/Database/Connection.php | 13 ++- src/Concept/Database/ResultSet.php | 14 ++-- src/Concept/Database/Transaction.php | 3 - src/Database/Connection.php | 79 +++++++++---------- src/Database/MySQL.php | 21 ++--- src/Database/PostgreSQL.php | 19 +++++ src/Database/ResultSet.php | 58 ++++++-------- src/Database/SQLite.php | 12 +++ src/Database/Transaction.php | 55 +++++-------- src/Exception/BlankResult.php | 15 ---- .../Exception.php => Exception/Database.php} | 11 ++- src/Exception/Database/BlankResult.php | 20 +++++ src/Implement/Database.php | 65 +++++++++++++++ 17 files changed, 260 insertions(+), 251 deletions(-) delete mode 100644 src/Alias/Database.php create mode 100644 src/Database/PostgreSQL.php create mode 100644 src/Database/SQLite.php delete mode 100644 src/Exception/BlankResult.php rename src/{Database/Exception.php => Exception/Database.php} (51%) create mode 100644 src/Exception/Database/BlankResult.php create mode 100644 src/Implement/Database.php diff --git a/Readme.md b/Readme.md index aab6d10..f2c9f89 100644 --- a/Readme.md +++ b/Readme.md @@ -28,7 +28,7 @@ Database Abstraction Layer ... "require": { ... - "provm/database": "^2.0" + "provm/database": "^2.3" ... }, ... @@ -41,13 +41,13 @@ For `MySQL`/`MariaDB` Without `DI` ``` -$database new MySQL(); +$database new ProVM\Database\MySQL(); $database->setHost(); $database->setPort(); // If diferent from 3306 $database->setUsername(); $database->setPassword(); -$connection = new Connection($database); +$connection = new ProVM\Database\Connection($database); ``` With `DI` ``` @@ -76,8 +76,20 @@ $rs = $connection->execute(, ); ``` Get data from ResultSet ``` -$data = $rs->getAsArray(); -$data_objs = $rs->getAsObject(); +$data = $rs->fetchFirst(); +$data_object = $rs->fetchFirstAsObject(); +$data_array = $rs->fetchAll(); +$data_array_of_objects = $rs->fetchAllAsObjects(); +``` +Use transactions +``` +$connection->transaction()->begin(); +try { + $connection->execute($query, $values); + $connection->transaction()->commit(); +} catch (PDOException $exception) { + $connection->transaction()->rollBack(); +} ``` ## Definitions @@ -97,23 +109,20 @@ Connection handling + `Connection::query` Query the database + `Connection::prepare` Prepare query statement + `Connection::execute` Prepare and execute a query statement -+ `Conneciton::transaction` Return a transaction ++ `Connection::transaction` Return a transaction ### Transaction Transaction + `Transaction::begin` Begin transaction + `Transaction::commit` Commit changes + `Transaction::rollBack` Roll back changes -+ `Transaction::query` Same as `Connection::query` -+ `Transaction::prepare` Same as `Connection::prepare` -+ `Transaction::execute` Same as `Connection::execute` ### ResultSet Result set to handle PDOStatement + `ResultSet::execute` Execute a prepared statement -+ `ResultSet::getAsArray` Return query results as array of associative arrays -+ `ResultSet::getAsObject` Return query results as array of objects -+ `ResultSet::getFirst` Return first result as object ++ `ResultSet::fetchAll` Return query results as array of associative arrays ++ `ResultSet::fetchAllAsObjects` Return query results as array of objects ++ `ResultSet::fetchFirst` Return first result as associative array ++ `ResultSet::fetchFirstAsObject` Return first result as object ## TODO -+ Implement other database types. eg: PostgreSQL, SQLite diff --git a/composer.json b/composer.json index e70f7c1..85a3f71 100644 --- a/composer.json +++ b/composer.json @@ -1,17 +1,20 @@ { "name": "provm/database", - "type": "project", - "require-dev": { - "phpunit/phpunit": "^9.5", - "kint-php/kint": "^4.2" - }, + "type": "library", "authors": [ { "name": "Aldarien", "email": "aldarien85@gmail.com" } ], - "require": {}, + "require": { + "php": ">=8", + "ext-pdo": "*" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "kint-php/kint": "^5.0" + }, "autoload": { "psr-4": { "ProVM\\": "src/" diff --git a/src/Alias/Database.php b/src/Alias/Database.php deleted file mode 100644 index 8ece3db..0000000 --- a/src/Alias/Database.php +++ /dev/null @@ -1,58 +0,0 @@ -host = $host; - return $this; - } - public function getHost(): string - { - return $this->host; - } - protected int $port; - public function setPort(int $port): DatabaseInterface - { - $this->port = $port; - return $this; - } - public function getPort(): int - { - return $this->port; - } - protected string $name; - public function setName(string $name): DatabaseInterface - { - $this->name = $name; - return $this; - } - public function getName(): string - { - return $this->name; - } - protected string $username; - public function setUsername(string $username): DatabaseInterface - { - $this->username = $username; - return $this; - } - public function getUsername(): string - { - return $this->username; - } - protected string $password; - public function setPassword(string $password): DatabaseInterface - { - $this->password = $password; - return $this; - } - public function getPassword(): string - { - return $this->password; - } -} diff --git a/src/Concept/Database.php b/src/Concept/Database.php index 0ed1e0d..bc9f2a9 100644 --- a/src/Concept/Database.php +++ b/src/Concept/Database.php @@ -1,20 +1,18 @@ setDatabase($database); - $this->connect(); } protected Database $database; - public function setDatabase(Database $database): ConnectionInterface + + protected function getDatabase(): Database + { + return $this->database; + } + + protected function setDatabase(Database $database): Database\Connection { $this->database = $database; return $this; } - public function getDatabase(): Database - { - return $this->database; - } - public function connect(): ConnectionInterface + + protected PDO $pdo; + public function connect(): PDO { if (!isset($this->pdo)) { + $dsn = $this->getDatabase()->getDsn(); if ($this->getDatabase()->needsUser()) { - $pdo = new PDO($this->getDatabase()->getDSN(), $this->getDatabase()->getUsername(), $this->getDatabase()->getPassword()); + $this->pdo = new PDO($dsn, $this->getDatabase()->getUser(), $this->getDatabase()->getPassword()); } else { - $pdo = new PDO($this->getDatabase()->getDSN()); + $this->pdo = new PDO($dsn); } - $this->setPDO($pdo); } - return $this; - } - protected PDO $pdo; - public function setPDO(PDO $pdo): ConnectionInterface - { - $this->pdo = $pdo; - return $this; - } - public function getPDO(): PDO - { return $this->pdo; } - public function query(string $query): ResultSetInterface + + protected Database\Transaction $transaction; + + public function transaction(): Database\Transaction { - $st = $this->getPDO()->query($query); - if (!$st) { - throw new PDOException("Could not run query {$query}"); + if (!isset($this->transaction)) { + $this->transaction = new Transaction($this); } - return new ResultSet($st); + return $this->transaction; } - public function prepare(string $query): ResultSetInterface + + public function query(string $query): Database\ResultSet { - $st = $this->getPDO()->prepare($query); - if (!$st) { - throw new PDOException("Could not prepare query {$query}"); + return new ResultSet($this->connect()->query($query)); + } + public function prepare(string $query): Database\ResultSet + { + return new ResultSet($this->connect()->prepare($query)); + } + public function execute(string $query, ?array $data = null): Database\ResultSet + { + if ($data !== null) { + $rs = $this->prepare($query); + $rs->execute($data); + return $rs; } - return new ResultSet($st); - } - public function execute(string $query, array $values): ResultSetInterface - { - return $this->prepare($query)->execute($values); - } - public function transaction(): TransactionInterface - { - return new Transaction($this->getPDO()); + return $this->query($query); } } diff --git a/src/Database/MySQL.php b/src/Database/MySQL.php index 9836747..a4e009f 100644 --- a/src/Database/MySQL.php +++ b/src/Database/MySQL.php @@ -1,7 +1,7 @@ getHost()}" - ]; - if (isset($this->port)) { - $arr []= "port={$this->getPort()}"; + $dsn = ["mysql:host={$this->getHost()}"]; + if ($this->getPort()) { + $dsn []= "port={$this->getPort()}"; } - $arr []= "dbname={$this->getName()}"; - - return implode(':', [ - 'mysql', - implode(';', $arr) - ]); + $dsn []= "dbname={$this->getName()}"; + return implode(';', $dsn); } } diff --git a/src/Database/PostgreSQL.php b/src/Database/PostgreSQL.php new file mode 100644 index 0000000..fc83fe6 --- /dev/null +++ b/src/Database/PostgreSQL.php @@ -0,0 +1,19 @@ +getHost()}"]; + if ($this->getPort()) { + $dsn []= "port={$this->getPort()}"; + } + $dsn []= "dbname={$this->getName()}"; + $dsn []= "user={$this->getUser()}"; + $dsn []= "password={$this->getPassword()}"; + return implode(';', $dsn); + } +} diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 6654eb7..d357ffc 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -3,10 +3,10 @@ namespace ProVM\Database; use PDO; use PDOStatement; -use ProVM\Concept\Database\ResultSet as RSInterface; -use ProVM\Exception\BlankResult; +use ProVM\Concept\Database; +use ProVM\Exception\Database\BlankResult; -class ResultSet implements RSInterface +class ResultSet implements Database\ResultSet { public function __construct(PDOStatement $statement) { @@ -14,52 +14,44 @@ class ResultSet implements RSInterface } protected PDOStatement $statement; - public function setStatement(PDOStatement $statement): RSInterface + + protected function getStatement(): PDOStatement + { + return $this->statement; + } + protected function setStatement(PDOStatement $statement): ResultSet { $this->statement = $statement; return $this; } - public function getStatement(): PDOStatement - { - return $this->statement; - } - public function execute(array $values): RSInterface + public function execute(array $data): Database\ResultSet { - $this->getStatement()->execute($values); + $this->statement->execute($data); return $this; } - public function getAsArray(): array + protected function checkResults(): PDOStatement { - $rs = $this->getStatement()->fetchAll(PDO::FETCH_ASSOC); - if (!$rs) { - throw new BlankResult(); + if ($this->getStatement()->rowCount() === 0) { + throw new BlankResult(query: $this->getStatement()->queryString); } - return $rs; + return $this->getStatement(); } - public function getAsObject(): array + public function fetchFirst(): array { - $rs = $this->getStatement()->fetchAll(PDO::FETCH_OBJ); - if (!$rs) { - throw new BlankResult(); - } - return $rs; + return $this->checkResults()->fetch(PDO::FETCH_ASSOC); } - public function getFirstAsArray(): array + public function fetchAll(): array { - $rs = $this->getStatement()->fetch(PDO::FETCH_ASSOC); - if (!$rs or count($rs) === 0) { - throw new BlankResult(); - } - return $rs; + return $this->checkResults()->fetchAll(PDO::FETCH_ASSOC); } - public function getFirstAsObject(): object + public function fetchFirstAsObject(): object { - $rs = $this->getStatement()->fetch(PDO::FETCH_OBJ); - if (!$rs or count($rs) === 0) { - throw new BlankResult(); - } - return $rs; + return $this->checkResults()->fetch(PDO::FETCH_OBJ); + } + public function fetchAllAsObjects(): array + { + return $this->checkResults()->fetchAll(PDO::FETCH_OBJ); } } diff --git a/src/Database/SQLite.php b/src/Database/SQLite.php new file mode 100644 index 0000000..f8ab299 --- /dev/null +++ b/src/Database/SQLite.php @@ -0,0 +1,12 @@ +getHost()}"; + } +} diff --git a/src/Database/Transaction.php b/src/Database/Transaction.php index 7901640..3181903 100644 --- a/src/Database/Transaction.php +++ b/src/Database/Transaction.php @@ -2,58 +2,39 @@ namespace ProVM\Database; use PDO; -use PDOException; -use ProVM\Concept\Database\ResultSet as ResultSetInterface; -use ProVM\Concept\Database\Transaction as TransactionInterface; +use ProVM\Concept; -class Transaction implements TransactionInterface +class Transaction implements Concept\Database\Transaction { - public function __construct(PDO $pdo) + public function __construct(Concept\Database\Connection $connection) { - $this->setPDO($pdo); + $this->setConnection($connection); } - protected PDO $pdo; - public function setPDO(PDO $pdo): TransactionInterface + protected Concept\Database\Connection $connection; + + public function getConnection(): Concept\Database\Connection { - $this->pdo = $pdo; + return $this->connection; + } + + public function setConnection(Concept\Database\Connection $connection): Concept\Database\Transaction + { + $this->connection = $connection; return $this; } - public function getPDO(): PDO + + public function begin(): Concept\Database\Transaction { - return $this->pdo; - } - public function begin(): TransactionInterface - { - $this->getPDO()->beginTransaction(); + $this->getConnection()->connect()->beginTransaction(); return $this; } - public function query(string $query): ResultSetInterface - { - $st = $this->getPDO()->query($query); - if (!$st) { - throw new PDOException("Could not run query {$query}"); - } - return new ResultSet($st); - } - public function prepare(string $query): ResultSetInterface - { - $st = $this->getPDO()->prepare($query); - if (!$st) { - throw new PDOException("Could not prepare query {$query}"); - } - return new ResultSet($st); - } - public function execute(string $query, array $values): ResultSetInterface - { - return $this->prepare($query)->execute($values); - } public function commit(): void { - $this->getPDO()->commit(); + $this->getConnection()->connect()->commit(); } public function rollBack(): void { - $this->getPDO()->rollBack(); + $this->getConnection()->connect()->rollBack(); } } diff --git a/src/Exception/BlankResult.php b/src/Exception/BlankResult.php deleted file mode 100644 index 09fb226..0000000 --- a/src/Exception/BlankResult.php +++ /dev/null @@ -1,15 +0,0 @@ -host; + } + public function getPort(): int|bool + { + return $this->port ?? false; + } + public function getName(): string + { + return $this->name; + } + public function getUser(): string + { + return $this->user; + } + public function getPassword(): string + { + return $this->password; + } + + public function setHost(string $host): Concept\Database + { + $this->host = $host; + return $this; + } + public function setPort(int $port): Concept\Database + { + $this->port = $port; + return $this; + } + public function setName(string $name): Concept\Database + { + $this->name = $name; + return $this; + } + public function setUser(string $username): Concept\Database + { + $this->user = $username; + return $this; + } + public function setPassword(string $password): Concept\Database + { + $this->password = $password; + return $this; + } + + public function needsUser(): bool + { + return false; + } +}