This commit is contained in:
Gregory Letellier
2023-10-26 14:34:20 +02:00
commit a2dc633be4
14 changed files with 5206 additions and 0 deletions

154
src/Db.php Normal file
View File

@@ -0,0 +1,154 @@
<?php
namespace Kletellier\PdoWrapper;
use Dotenv\Dotenv;
use PDO;
use PDOStatement;
class Db
{
protected PDO $pdo;
protected string $connstring;
private static $instance = null;
private string $type;
private ?string $user;
private ?string $password;
private string $database;
private ?string $port;
private ?string $host;
private string $error;
private function __construct($dir)
{
$this->error = "";
$this->loadEnv($dir);
$this->initPdo();
}
private function loadEnv($dir)
{
try {
$dotenv = Dotenv::createImmutable($dir);
$dotenv->load();
} catch (\Throwable $th) {
$this->error = $th->getMessage();
}
$this->parseEnv();
}
private function parseEnv()
{
try {
$this->type = isset($_ENV["DB_TYPE"]) ? $_ENV["DB_TYPE"] : null;
$this->user = isset($_ENV["DB_USER"]) ? $_ENV["DB_USER"] : null;
$this->host = isset($_ENV["DB_HOST"]) ? $_ENV["DB_HOST"] : null;
$this->password = isset($_ENV["DB_PASSWORD"]) ? $_ENV["DB_PASSWORD"] : null;
$this->database = isset($_ENV["DB_DATABASE"]) ? $_ENV["DB_DATABASE"] : null;
$this->port = isset($_ENV["DB_PORT"]) ? $_ENV["DB_PORT"] : null;
} catch (\Throwable $th) {
$this->error = $th->getMessage();
}
}
private function initPdo()
{
$this->getConnString();
$this->pdo = new PDO($this->connstring, $this->user, $this->password);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
private function bindParams(PDOStatement $stmt, array $data): PDOStatement
{
$iParams = 1;
foreach ($data as $value) {
if ($value instanceof \DateTime) {
$stmt->bindValue($iParams, $value->format('Y-m-d H:i:s'), PDO::PARAM_STR);
} else {
switch (gettype($value)) {
case 'integer':
$stmt->bindValue($iParams, $value, PDO::PARAM_INT);
break;
case 'datetime':
$stmt->bindValue($iParams, $value, PDO::PARAM_STR);
break;
case 'boolean':
$stmt->bindValue($iParams, $value, PDO::PARAM_BOOL);
break;
case 'NULL':
$stmt->bindValue($iParams, $value, PDO::PARAM_NULL);
break;
default:
$stmt->bindValue($iParams, $value, PDO::PARAM_STR);
}
}
$iParams++;
}
return $stmt;
}
private function getConnString()
{
$this->connstring = match ($this->type) {
"mysql" => "mysql:host=" . $this->host . ":" . $this->port . ";dbname=" . $this->database,
"sqlite" => "sqlite:" . $this->database,
};
}
public function executeRawQuery(string $rawquery): bool
{
$stmt = $this->pdo->prepare($rawquery);
return $stmt->execute();
}
public function getSelectQuery(Query $query): mixed
{
$stmt = $this->pdo->prepare($query->getQuery());
$stmt = $this->bindParams($stmt, $query->getData());
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function updateQuery(Query $query): bool
{
$stmt = $this->pdo->prepare($query->getQuery());
$stmt = $this->bindParams($stmt, $query->getData());
return $stmt->execute();
}
public function insertQuery(Query $query): mixed
{
$stmt = $this->pdo->prepare($query->getQuery());
$stmt = $this->bindParams($stmt, $query->getData());
$ok = $stmt->execute();
if ($ok) {
return $this->pdo->lastInsertId();
} else {
return false;
}
}
public static function init($dir)
{
return self::retrieveInstance($dir);
}
private static function retrieveInstance($dir = __DIR__)
{
if (self::$instance == null) {
self::$instance = new Db($dir);
}
return self::$instance;
}
public static function getInstance()
{
return self::retrieveInstance();
}
public function getError()
{
return $this->error;
}
}

55
src/Expression.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
namespace Kletellier\PdoWrapper;
class Expression
{
private $condition;
private $operator;
private $value;
public function __construct($condition, $operator, $value)
{
$this->condition = $condition;
$this->operator = $operator;
$this->value = $value;
}
public function raw(): string
{
return "(" . $this->condition . " " . $this->operator . " ? )";
}
public function getCondition()
{
return $this->condition;
}
public function setCondition($condition)
{
$this->condition = $condition;
return $this;
}
public function getOperator()
{
return $this->operator;
}
public function setOperator($operator)
{
$this->operator = $operator;
return $this;
}
public function getValue()
{
return $this->value;
}
public function setValue($value)
{
$this->value = $value;
return $this;
}
}

97
src/Model.php Normal file
View File

@@ -0,0 +1,97 @@
<?php
namespace Kletellier\PdoWrapper;
class Model
{
protected mixed $values;
protected string $table;
protected string $pk;
private Db $db;
private bool $new;
public function __construct()
{
$this->db = Db::getInstance();
$this->new = true;
$this->values = array();
$this->pk = "id";
$this->table = $this->getDefaultTableName();
}
public function getTable(): string
{
return $this->table;
}
public function setTable(string $table): self
{
$this->table = $table;
return $this;
}
public function getPk(): string
{
return $this->pk;
}
public function setPk(string $pk): self
{
$this->pk = $pk;
return $this;
}
public function fillData(mixed $data): self
{
$this->values = $data;
$this->new = false;
return $this;
}
public function save(): bool
{
$ret = false;
$qb = $this->newQueryBuilder();
if ($this->new) {
$query = $qb->getPreparedQuery($this->values);
$id = $this->db->insertQuery($query);
if ($id !== false) {
$this->values[$this->pk] = $id;
$this->new = false;
$ret = true;
}
} else {
$query = $qb->getPreparedQuery($this->values, false);
$ret = $this->db->updateQuery($query);
}
return $ret;
}
public function newQueryBuilder(): QueryBuilder
{
return new QueryBuilder($this->table, $this->pk);
}
public function __set($name, $value): void
{
$this->values[$name] = $value;
}
public function __get($name): mixed
{
if (isset($this->values[$name])) {
return $this->values[$name];
}
return null;
}
private function getDefaultTableName(): string
{
$className = get_class($this);
$classNameParts = explode('\\', $className);
$classNameWithoutNamespace = end($classNameParts);
$table = strtolower($classNameWithoutNamespace);
return $table;
}
}

43
src/Query.php Normal file
View File

@@ -0,0 +1,43 @@
<?php
namespace Kletellier\PdoWrapper;
class Query
{
public string $query;
public array $data;
public function __construct()
{
$this->query = "";
$this->data = [];
}
public function addData($value)
{
$this->data[] = $value;
}
public function getQuery(): string
{
return $this->query;
}
public function setQuery(string $query)
{
$this->query = $query;
return $this;
}
public function getData(): array
{
return $this->data;
}
public function setData(array $data)
{
$this->data = $data;
return $this;
}
}

215
src/QueryBuilder.php Normal file
View File

@@ -0,0 +1,215 @@
<?php
namespace Kletellier\PdoWrapper;
class QueryBuilder
{
protected Db $db;
protected array $columns;
protected array $wheres;
protected array $orders;
protected string $table;
protected string $pk;
protected ?int $take;
protected ?int $offset;
protected string $raw_query;
public function __construct($table, $pk)
{
$this->table = $table;
$this->pk = $pk;
$this->db = Db::getInstance();
$this->reset();
}
public function reset()
{
$this->columns = [];
$this->wheres = [];
$this->orders = [];
$this->offset = null;
$this->take = null;
$this->raw_query = "";
}
public function raw(string $query)
{
$this->raw_query = $query;
return $this;
}
public function take(?int $nb)
{
$this->take = $nb;
return $this;
}
public function offset(?int $offset)
{
$this->offset = $offset;
return $this;
}
public function orderBy($columns, $sort = "ASC")
{
if (is_array($columns)) {
foreach ($columns as $column) {
$this->orders[] = $column . " " . $sort;
}
} else {
$this->orders[] = $columns . " " . $sort;
}
return $this;
}
public function columns($columns)
{
if (is_array($columns)) {
$this->columns = array_merge($this->columns, $columns);
}
if (is_string($columns)) {
$this->columns[] = $columns;
}
return $this;
}
public function where($condition, $operator, $value = null)
{
if ($value === null) {
$this->wheres[] = new Expression($condition, "=", $operator);
} else {
$this->wheres[] = new Expression($condition, $operator, $value);
}
return $this;
}
public function find($id): mixed
{
$this->reset();
$expression = new Expression($this->pk, "=", $id);
$this->wheres = [$expression];
$this->columns = ["*"];
$data = $this->getData();
if (count($data) > 0) {
return $this->prepareModels($data)[0];
}
return null;
}
public function get(): mixed
{
$data = $this->getData();
return $this->prepareModels($data);
}
public function getPreparedQuery(array $data, $insert = true): Query
{
$ret = new Query();
$columns = implode(",", array_keys($data));
$query = "";
if ($insert) {
$keys = implode(",", str_split(str_repeat("?", count($data))));
$query = "INSERT INTO " . $this->table . " (" . $columns . ") VALUES (" . $keys . ");";
foreach ($data as $key => $value) {
$ret->addData($value);
}
} else {
$query = "UPDATE " . $this->table . " SET ";
$up = "";
foreach ($data as $key => $value) {
if ($up != "") {
$up .= ", ";
}
$up .= $key . " = ? ";
$ret->addData($value);
}
$query .= $up . " WHERE " . $this->pk . " = ? ;";
$ret->addData($data[$this->pk]);
}
$ret->setQuery($query);
return $ret;
}
private function prepareModels(mixed $data): mixed
{
$ret = array();
foreach ($data as $row) {
$model = new Model();
$ret[] = $model->setPk($this->pk)->setTable($this->table)->fillData($row);
}
return $ret;
}
private function getData(): mixed
{
return $this->db->getSelectQuery($this->getQuery());
}
private function getConstructedQuery(): Query
{
$ret = new Query();
$ret->setQuery("");
$ret->setData([]);
$columns = implode(",", $this->columns);
$where = "";
foreach ($this->wheres as $expression) {
$sw = $expression->raw();
$ret->addData($expression->getValue());
if ($where != "") {
$where .= " AND ";
}
$where .= $sw;
}
$query = "SELECT " . $columns . " FROM " . $this->table ;
if ($where != "") {
$query .= " WHERE " . $where;
}
if (count($this->orders) > 0) {
$query .= " ORDER BY ";
$iOrder = 0;
foreach ($this->orders as $order) {
if ($iOrder > 0) {
$query .= ",";
}
$query .= $order;
$iOrder++;
}
}
$query .= $this->getLimit();
$ret->setQuery($query);
return $ret;
}
private function getLimit(): string
{
if ($this->take !== null || $this->offset !== null) {
$ret = "";
if ($this->take !== null) {
$ret = " LIMIT " . $this->take;
}
if ($this->offset !== null) {
$ret .= " OFFSET " . $this->offset;
}
return $ret;
} else {
return "";
}
}
private function getQuery(): Query
{
if ($this->raw_query !== "") {
$query = new Query();
$query->setQuery($this->raw_query);
$query->setData([]);
return $query;
} else {
return $this->getConstructedQuery();
}
}
}