15 Commits

Author SHA1 Message Date
ac9b141928 Merge branch 'develop' into release 2022-09-12 21:45:22 -03:00
758ff0e282 Added more detail in obtaining first result 2022-09-12 21:45:14 -03:00
d5fde83afb FIX 2022-09-09 11:19:11 -04:00
6dda8e1515 FIX 2022-09-09 11:18:36 -04:00
ec19c25681 FIX: autoloads 2022-09-09 10:57:17 -04:00
6525eb2f31 FIX: missing autoload definitions 2022-09-09 10:56:05 -04:00
6b978c371c Working version for 2.0 2022-09-08 20:02:55 -04:00
28e31de2a1 Readme 2022-09-08 20:02:11 -04:00
0df2e95e7f Implementations 2022-09-08 20:02:05 -04:00
7fbc52b702 Abstract 2022-09-08 20:01:52 -04:00
5322d81e1b Interfaces 2022-09-08 20:01:32 -04:00
ce460740cd Dependencies 2022-09-08 18:13:50 -04:00
f87aab17f6 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	.gitignore
2022-09-08 18:12:44 -04:00
25bed5e41e Ignore 2022-09-08 18:10:15 -04:00
607172ccf3 Merge branch 'develop' 2021-10-13 22:46:59 -03:00
15 changed files with 468 additions and 90 deletions

8
.gitignore vendored
View File

@ -1,2 +1,6 @@
**/composer.lock
**/vendor/
# Composer
**/vendor/
**/*.lock
# PHPStorm
**/.idea/

119
Readme.md Normal file
View File

@ -0,0 +1,119 @@
# Database
Database Abstraction Layer
## Requirements
+ PHP 8+
+ PDO
### Recommended
+ [`php-di/php-di`](https://packagist.org/packages/php-di/php-di)
## Installation
`composer.json`
```
{
...
"repositories": [
...
{
"type": "git",
"path": "https://git.provm.cl/ProVM/database.git"
}
...
],
...
"require": {
...
"provm/database": "^2.0"
...
},
...
}
```
## Usage
For `MySQL`/`MariaDB`
Without `DI`
```
$database new MySQL();
$database->setHost(<host>);
$database->setPort(<port>); // If diferent from 3306
$database->setUsername(<username>);
$database->setPassword(<password>);
$connection = new Connection($database);
```
With `DI`
```
$container->set(ProVM\Concept\Database::class, function(Psr\Container\ContainerInterface $container) {
$database = $container->get(ProVM\Database\MySQL::class);
$database->setHost(<host>);
$database->setPort(<port>); // If diferent from 3306
$database->setUsername(<username>);
$database->setPassword(<password>);
return $database;
});
$container->set(ProVM\Concept\Database\Connection::class, function(Psr\Container\ContainerInterface $container) {
return new ProVM\Database\Connection($container->get(ProVM\Concept\Database::class);
});
```
Run query
```
$rs = $connection->query(<query>);
```
Run prepared statement
```
$rs = $connection->prepare(<query>)->execute(<value>);
or
$rs = $connection->execute(<query>, <values>);
```
Get data from ResultSet
```
$data = $rs->getAsArray();
$data_objs = $rs->getAsObject();
```
## Definitions
### Database
Database configuration
+ `Database::host` Connection host name [`string`]
+ `Database::port` Connection port number [`int`]
+ `Database::name` Database name to connect [`string`]
+ `Database::username` Optional. The username to connect [`string`]
+ `Database::password` Optional. The password for the username [`string`]
+ `Database->getDsn()` get dsn string for PDO connection. [`string`]
+ `Database->needsUser()` If a user is needed for the connection.
### Connection
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
### 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
## TODO
+ Implement other database types. eg: PostgreSQL, SQLite

View File

@ -1,8 +0,0 @@
<?php
namespace ProVM\Common\Define;
interface Engine {
public function __construct(string $host, string $name, ?int $port = null);
public function dsn(): string;
public function hasLogin(): bool;
}

View File

@ -1,36 +0,0 @@
<?php
namespace ProVM\Common\Service;
use \Model;
class Database {
protected $settings;
public function __construct($settings) {
$this->settings = $settings;
}
public function load() {
foreach ($this->settings->databases as $name => $settings) {
$engine = $this->getEngine($settings);
$configs = ['connection_string' => $engine->dsn()];
if ($engine->hasLogin()) {
$configs['username'] = $settings->user->name;
$configs['password'] = $settings->user->password;
}
Model::configure($configs, null, $name);
}
if (isset($this->settings->short_names)) {
Model::$short_table_names = $this->settings->short_names;
}
}
protected function getEngine($settings): \ProVM\Common\Define\Engine {
$name = match($settings->engine) {
'mysql' => 'MySQL'
};
$class = implode("\\", [
'ProVM',
'Database',
$name
]);
return new $class($settings->host->name, $settings->name, $settings->host->port ?? null);
}
}

View File

@ -1,21 +1,20 @@
{
"name": "provm/database",
"description": "Database loader for j4mie/paris",
"type": "library",
"require": {
"j4mie/paris": "^1.5"
},
"license": "MIT",
"autoload": {
"psr-4": {
"ProVM\\Database\\": "src/",
"ProVM\\Common\\": "common/"
}
"type": "project",
"require-dev": {
"phpunit/phpunit": "^9.5",
"kint-php/kint": "^4.2"
},
"authors": [
{
"name": "Aldarien",
"email": "aldarien85@gmail.com"
}
]
],
"require": {},
"autoload": {
"psr-4": {
"ProVM\\": "src/"
}
}
}

58
src/Alias/Database.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace ProVM\Alias;
use ProVM\Concept\Database as DatabaseInterface;
abstract class Database implements DatabaseInterface
{
protected string $host;
public function setHost(string $host): DatabaseInterface
{
$this->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;
}
}

20
src/Concept/Database.php Normal file
View File

@ -0,0 +1,20 @@
<?php
namespace ProVM\Concept;
use PDO;
interface Database
{
public function setHost(string $host): Database;
public function getHost(): string;
public function setPort(int $port): Database;
public function getPort(): int;
public function setName(string $name): Database;
public function getName(): string;
public function setUsername(string $username): Database;
public function getUsername(): string;
public function setPassword(string $password): Database;
public function getPassword(): string;
public function needsUser(): bool;
public function getDSN(): string;
}

View File

@ -0,0 +1,18 @@
<?php
namespace ProVM\Concept\Database;
use PDO;
use ProVM\Concept\Database;
interface Connection
{
public function setDatabase(Database $database): Connection;
public function getDatabase(): Database;
public function connect(): Connection;
public function setPDO(PDO $pdo): Connection;
public function getPDO(): PDO;
public function query(string $query): ResultSet;
public function prepare(string $query): ResultSet;
public function execute(string $query, array $values): ResultSet;
public function transaction(): Transaction;
}

View File

@ -0,0 +1,14 @@
<?php
namespace ProVM\Concept\Database;
use PDOStatement;
interface ResultSet
{
public function __construct(PDOStatement $statement);
public function execute(array $values): ResultSet;
public function getAsArray(): array;
public function getAsObject(): array;
public function getFirstAsArray(): array;
public function getFirstAsObject(): object;
}

View File

@ -0,0 +1,12 @@
<?php
namespace ProVM\Concept\Database;
interface Transaction
{
public function begin(): Transaction;
public function query(string $query): ResultSet;
public function prepare(string $query): ResultSet;
public function execute(string $query, array $values): ResultSet;
public function commit(): void;
public function rollBack(): void;
}

View File

@ -0,0 +1,75 @@
<?php
namespace ProVM\Database;
use PDO;
use PDOException;
use ProVM\Concept\Database;
use ProVM\Concept\Database\Connection as ConnectionInterface;
use ProVM\Concept\Database\ResultSet as ResultSetInterface;
use ProVM\Concept\Database\Transaction as TransactionInterface;
class Connection implements ConnectionInterface
{
public function __construct(Database $database)
{
$this->setDatabase($database);
$this->connect();
}
protected Database $database;
public function setDatabase(Database $database): ConnectionInterface
{
$this->database = $database;
return $this;
}
public function getDatabase(): Database
{
return $this->database;
}
public function connect(): ConnectionInterface
{
if (!isset($this->pdo)) {
if ($this->getDatabase()->needsUser()) {
$pdo = new PDO($this->getDatabase()->getDSN(), $this->getDatabase()->getUsername(), $this->getDatabase()->getPassword());
} else {
$pdo = new PDO($this->getDatabase()->getDSN());
}
$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
{
$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 transaction(): TransactionInterface
{
return new Transaction($this->getPDO());
}
}

28
src/Database/MySQL.php Normal file
View File

@ -0,0 +1,28 @@
<?php
namespace ProVM\Database;
use ProVM\Alias\Database;
class MySQL extends Database
{
public function needsUser(): bool
{
return true;
}
public function getDSN(): string
{
$arr = [
"host={$this->getHost()}"
];
if (isset($this->port)) {
$arr []= "port={$this->getPort()}";
}
$arr []= "dbname={$this->getName()}";
return implode(':', [
'mysql',
implode(';', $arr)
]);
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace ProVM\Database;
use PDO;
use PDOStatement;
use ProVM\Concept\Database\ResultSet as RSInterface;
class ResultSet implements RSInterface
{
public function __construct(PDOStatement $statement)
{
$this->setStatement($statement);
}
protected PDOStatement $statement;
public function setStatement(PDOStatement $statement): RSInterface
{
$this->statement = $statement;
return $this;
}
public function getStatement(): PDOStatement
{
return $this->statement;
}
public function execute(array $values): RSInterface
{
$this->getStatement()->execute($values);
return $this;
}
public function getAsArray(): array
{
return $this->getStatement()->fetchAll(PDO::FETCH_ASSOC);
}
public function getAsObject(): array
{
return $this->getStatement()->fetchAll(PDO::FETCH_OBJ);
}
public function getFirstAsArray(): array
{
return $this->getStatement()->fetch(PDO::FETCH_ASSOC);
}
public function getFirstAsObject(): object
{
return $this->getStatement()->fetch(PDO::FETCH_OBJ);
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace ProVM\Database;
use PDO;
use PDOException;
use ProVM\Concept\Database\ResultSet as ResultSetInterface;
use ProVM\Concept\Database\Transaction as TransactionInterface;
class Transaction implements TransactionInterface
{
public function __construct(PDO $pdo)
{
$this->setPDO($pdo);
}
protected PDO $pdo;
public function setPDO(PDO $pdo): TransactionInterface
{
$this->pdo = $pdo;
return $this;
}
public function getPDO(): PDO
{
return $this->pdo;
}
public function begin(): TransactionInterface
{
$this->getPDO()->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();
}
public function rollBack(): void
{
$this->getPDO()->rollBack();
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace ProVM\Database;
use ProVM\Common\Define\Engine;
class MySQL implements Engine {
protected $host;
protected $name;
public function __construct(string $host, string $name, ?int $port = null) {
$host_arr = [
'name' => $host
];
if ($port !== null) {
$host_arr['port'] = $port;
}
$this->host = (object) $host_arr;
$this->name = $name;
}
public function dsn(): string {
$dsn = [
'host=' . $this->host->name
];
if (isset($this->host->port)) {
$dsn []= 'port=' . $this->host->port;
}
$dsn []= 'dbname=' . $this->name;
return 'mysql:' . implode(';', $dsn);
}
public function hasLogin(): bool {
return true;
}
}