Compare commits
26 Commits
50bd59498d
...
master
Author | SHA1 | Date | |
---|---|---|---|
5014cca5be | |||
58df21c54f | |||
1bd3ccb8db | |||
99344fda7d | |||
fb37211f7d | |||
a326904825 | |||
816d8f7b9c | |||
2c6c0e6e55 | |||
b964bd65fe | |||
cc7cd638e3 | |||
ac9b141928 | |||
758ff0e282 | |||
d5fde83afb | |||
6dda8e1515 | |||
ec19c25681 | |||
6525eb2f31 | |||
9ec9fdd731 | |||
6b978c371c | |||
28e31de2a1 | |||
0df2e95e7f | |||
7fbc52b702 | |||
5322d81e1b | |||
ce460740cd | |||
f87aab17f6 | |||
25bed5e41e | |||
607172ccf3 |
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,2 +1,6 @@
|
||||
**/composer.lock
|
||||
**/vendor/
|
||||
# Composer
|
||||
**/vendor/
|
||||
**/*.lock
|
||||
|
||||
# PHPStorm
|
||||
**/.idea/
|
||||
|
128
Readme.md
Normal file
128
Readme.md
Normal file
@ -0,0 +1,128 @@
|
||||
# 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",
|
||||
"url": "https://git.provm.cl/ProVM/database.git"
|
||||
}
|
||||
...
|
||||
],
|
||||
...
|
||||
"require": {
|
||||
...
|
||||
"provm/database": "^2.3"
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
For `MySQL`/`MariaDB`
|
||||
|
||||
Without `DI`
|
||||
```
|
||||
$database new ProVM\Database\MySQL();
|
||||
$database->setHost(<host>);
|
||||
$database->setPort(<port>); // If diferent from 3306
|
||||
$database->setUsername(<username>);
|
||||
$database->setPassword(<password>);
|
||||
|
||||
$connection = new ProVM\Database\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->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
|
||||
|
||||
### 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
|
||||
+ `Connection::transaction` Return a transaction
|
||||
|
||||
### Transaction
|
||||
Transaction
|
||||
+ `Transaction::begin` Begin transaction
|
||||
+ `Transaction::commit` Commit changes
|
||||
+ `Transaction::rollBack` Roll back changes
|
||||
|
||||
### ResultSet
|
||||
Result set to handle PDOStatement
|
||||
+ `ResultSet::execute` Execute a prepared statement
|
||||
+ `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
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,21 +1,23 @@
|
||||
{
|
||||
"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/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Aldarien",
|
||||
"email": "aldarien85@gmail.com"
|
||||
}
|
||||
]
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8",
|
||||
"ext-pdo": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"kint-php/kint": "^5.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ProVM\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/Concept/Database.php
Normal file
18
src/Concept/Database.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace ProVM\Concept;
|
||||
|
||||
interface Database
|
||||
{
|
||||
public function getHost(): string;
|
||||
public function getPort(): int|bool;
|
||||
public function getName(): string;
|
||||
public function getUser(): string;
|
||||
public function getPassword(): string;
|
||||
public function needsUser(): bool;
|
||||
public function setHost(string $host): Database;
|
||||
public function setPort(int $port): Database;
|
||||
public function setName(string $name): Database;
|
||||
public function setUser(string $username): Database;
|
||||
public function setPassword(string $password): Database;
|
||||
public function getDsn(): string;
|
||||
}
|
15
src/Concept/Database/Connection.php
Normal file
15
src/Concept/Database/Connection.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace ProVM\Concept\Database;
|
||||
|
||||
use ProVM\Concept\Database;
|
||||
|
||||
interface Connection
|
||||
{
|
||||
public function connect(): \PDO;
|
||||
|
||||
public function transaction(): Transaction;
|
||||
|
||||
public function query(string $query): ResultSet;
|
||||
public function prepare(string $query): ResultSet;
|
||||
public function execute(string $query, ?array $data = null): ResultSet;
|
||||
}
|
12
src/Concept/Database/ResultSet.php
Normal file
12
src/Concept/Database/ResultSet.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace ProVM\Concept\Database;
|
||||
|
||||
interface ResultSet
|
||||
{
|
||||
public function execute(array $data): ResultSet;
|
||||
|
||||
public function fetchFirst(): array;
|
||||
public function fetchAll(): array;
|
||||
public function fetchFirstAsObject(): object;
|
||||
public function fetchAllAsObjects(): array;
|
||||
}
|
9
src/Concept/Database/Transaction.php
Normal file
9
src/Concept/Database/Transaction.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace ProVM\Concept\Database;
|
||||
|
||||
interface Transaction
|
||||
{
|
||||
public function begin(): Transaction;
|
||||
public function commit(): void;
|
||||
public function rollBack(): void;
|
||||
}
|
68
src/Database/Connection.php
Normal file
68
src/Database/Connection.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace ProVM\Database;
|
||||
|
||||
use PDO;
|
||||
use ProVM\Concept\Database;
|
||||
|
||||
class Connection implements Database\Connection
|
||||
{
|
||||
public function __construct(Database $database)
|
||||
{
|
||||
$this->setDatabase($database);
|
||||
}
|
||||
|
||||
protected Database $database;
|
||||
|
||||
protected function getDatabase(): Database
|
||||
{
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
protected function setDatabase(Database $database): Database\Connection
|
||||
{
|
||||
$this->database = $database;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected PDO $pdo;
|
||||
public function connect(): PDO
|
||||
{
|
||||
if (!isset($this->pdo)) {
|
||||
$dsn = $this->getDatabase()->getDsn();
|
||||
if ($this->getDatabase()->needsUser()) {
|
||||
$this->pdo = new PDO($dsn, $this->getDatabase()->getUser(), $this->getDatabase()->getPassword());
|
||||
} else {
|
||||
$this->pdo = new PDO($dsn);
|
||||
}
|
||||
}
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
protected Database\Transaction $transaction;
|
||||
|
||||
public function transaction(): Database\Transaction
|
||||
{
|
||||
if (!isset($this->transaction)) {
|
||||
$this->transaction = new Transaction($this);
|
||||
}
|
||||
return $this->transaction;
|
||||
}
|
||||
|
||||
public function query(string $query): Database\ResultSet
|
||||
{
|
||||
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 $this->query($query);
|
||||
}
|
||||
}
|
21
src/Database/MySQL.php
Normal file
21
src/Database/MySQL.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace ProVM\Database;
|
||||
|
||||
use ProVM\Implement\Database;
|
||||
|
||||
class MySQL extends Database
|
||||
{
|
||||
public function needsUser(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public function getDsn(): string
|
||||
{
|
||||
$dsn = ["mysql:host={$this->getHost()}"];
|
||||
if ($this->getPort()) {
|
||||
$dsn []= "port={$this->getPort()}";
|
||||
}
|
||||
$dsn []= "dbname={$this->getName()}";
|
||||
return implode(';', $dsn);
|
||||
}
|
||||
}
|
19
src/Database/PostgreSQL.php
Normal file
19
src/Database/PostgreSQL.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace ProVM\Database;
|
||||
|
||||
use ProVM\Implement\Database;
|
||||
|
||||
class PostgreSQL extends Database
|
||||
{
|
||||
public function getDsn(): string
|
||||
{
|
||||
$dsn = ["pgsql:host={$this->getHost()}"];
|
||||
if ($this->getPort()) {
|
||||
$dsn []= "port={$this->getPort()}";
|
||||
}
|
||||
$dsn []= "dbname={$this->getName()}";
|
||||
$dsn []= "user={$this->getUser()}";
|
||||
$dsn []= "password={$this->getPassword()}";
|
||||
return implode(';', $dsn);
|
||||
}
|
||||
}
|
57
src/Database/ResultSet.php
Normal file
57
src/Database/ResultSet.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace ProVM\Database;
|
||||
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use ProVM\Concept\Database;
|
||||
use ProVM\Exception\Database\BlankResult;
|
||||
|
||||
class ResultSet implements Database\ResultSet
|
||||
{
|
||||
public function __construct(PDOStatement $statement)
|
||||
{
|
||||
$this->setStatement($statement);
|
||||
}
|
||||
|
||||
protected PDOStatement $statement;
|
||||
|
||||
protected function getStatement(): PDOStatement
|
||||
{
|
||||
return $this->statement;
|
||||
}
|
||||
protected function setStatement(PDOStatement $statement): ResultSet
|
||||
{
|
||||
$this->statement = $statement;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute(array $data): Database\ResultSet
|
||||
{
|
||||
$this->statement->execute($data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function checkResults(): PDOStatement
|
||||
{
|
||||
if ($this->getStatement()->rowCount() === 0) {
|
||||
throw new BlankResult(query: $this->getStatement()->queryString);
|
||||
}
|
||||
return $this->getStatement();
|
||||
}
|
||||
public function fetchFirst(): array
|
||||
{
|
||||
return $this->checkResults()->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
public function fetchAll(): array
|
||||
{
|
||||
return $this->checkResults()->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
public function fetchFirstAsObject(): object
|
||||
{
|
||||
return $this->checkResults()->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
public function fetchAllAsObjects(): array
|
||||
{
|
||||
return $this->checkResults()->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
}
|
12
src/Database/SQLite.php
Normal file
12
src/Database/SQLite.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace ProVM\Database;
|
||||
|
||||
use ProVM\Implement\Database;
|
||||
|
||||
class SQLite extends Database
|
||||
{
|
||||
public function getDsn(): string
|
||||
{
|
||||
return "sqlite:{$this->getHost()}";
|
||||
}
|
||||
}
|
40
src/Database/Transaction.php
Normal file
40
src/Database/Transaction.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
namespace ProVM\Database;
|
||||
|
||||
use PDO;
|
||||
use ProVM\Concept;
|
||||
|
||||
class Transaction implements Concept\Database\Transaction
|
||||
{
|
||||
public function __construct(Concept\Database\Connection $connection)
|
||||
{
|
||||
$this->setConnection($connection);
|
||||
}
|
||||
|
||||
protected Concept\Database\Connection $connection;
|
||||
|
||||
public function getConnection(): Concept\Database\Connection
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function setConnection(Concept\Database\Connection $connection): Concept\Database\Transaction
|
||||
{
|
||||
$this->connection = $connection;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function begin(): Concept\Database\Transaction
|
||||
{
|
||||
$this->getConnection()->connect()->beginTransaction();
|
||||
return $this;
|
||||
}
|
||||
public function commit(): void
|
||||
{
|
||||
$this->getConnection()->connect()->commit();
|
||||
}
|
||||
public function rollBack(): void
|
||||
{
|
||||
$this->getConnection()->connect()->rollBack();
|
||||
}
|
||||
}
|
16
src/Exception/Database.php
Normal file
16
src/Exception/Database.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace ProVM\Exception;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
abstract class Database extends Exception
|
||||
{
|
||||
const BASE_CODE = 600;
|
||||
|
||||
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
|
||||
{
|
||||
$code += Database::BASE_CODE;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
20
src/Exception/Database/BlankResult.php
Normal file
20
src/Exception/Database/BlankResult.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace ProVM\Exception\Database;
|
||||
|
||||
use Throwable;
|
||||
use ProVM\Exception\Database;
|
||||
|
||||
class BlankResult extends Database
|
||||
{
|
||||
public function __construct(?string $table = null, ?string $query = null, ?Throwable $previous = null)
|
||||
{
|
||||
$message = implode('', [
|
||||
"No results found",
|
||||
($query !== null) ? " in {$query}" : '',
|
||||
($table !== null) ? " in {$table}" : '',
|
||||
'.'
|
||||
]);
|
||||
$code = 1;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
65
src/Implement/Database.php
Normal file
65
src/Implement/Database.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace ProVM\Implement;
|
||||
|
||||
use ProVM\Concept;
|
||||
|
||||
abstract class Database implements Concept\Database
|
||||
{
|
||||
protected string $host;
|
||||
protected int $port;
|
||||
protected string $name;
|
||||
protected string $user;
|
||||
protected string $password;
|
||||
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user