Compare commits

...

1 Commits

Author SHA1 Message Date
sheychen 47c0be00fb WIP 2018-07-11 14:39:17 +02:00
24 changed files with 1804 additions and 1380 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor

View File

@ -7,12 +7,11 @@
"email": "contact@clementbois.fr"
}
],
"require": {
"krutush/krutush": "dev-develop"
},
"require": {},
"autoload": {
"psr-4": {
"Krutush\\Database\\": "src/"
"Krutush\\Database\\": "src/",
"Krutush\\Database\\Exemple\\": "exemple/"
}
}
}

View File

@ -0,0 +1,25 @@
$migration->create(
Krutush\Database\Exemple\Model\UserModel:class,
Krutush\Database\ModelID:class,
[
'id' => [
'column' => 'user_id',
'type' => 'bigint'
],
'first_name' => [
'not_null' => true
],
'last_name' => [
'not_null' => true
],
'supervisor' => [
'foreign' => []
],
'supervised' => [
'virtual' => true,
'foreign' => [
'key' => 'supervisor',
'multiple' => true
]
]
]);

View File

@ -0,0 +1,10 @@
$migration->alter(
Krutush\Database\Exemple\Model\UserModel:class,
[
'first_name' => [
'unique' => 'full_name'
],
'last_name' => [
'unique' => 'full_name'
]
]);

View File

@ -0,0 +1,11 @@
$migration->create(
Krutush\Database\Exemple\Model\OfficeModel:class,
Krutush\Database\ModelID:class,
['name']
)->alter(
Krutush\Database\Exemple\Model\UserModel:class,
[
'office' => [
'foreign' => Krutush\Database\Exemple\Model\OfficeModel:class,
]
]);

View File

@ -0,0 +1,10 @@
<?php
namespace Krutush\Database\Exemple\Model;
use Krutush\Database\ModelID;
class OfficeModel extends ModelID{
public const FIELDS = [
'name'
];
}

View File

@ -0,0 +1,35 @@
<?php
namespace Krutush\Database\Exemple\Model;
use Krutush\Database\ModelID;
class UserModel extends ModelID{
public const FIELDS = [
'id' => [
'column' => 'user_id',
'type' => 'bigint'
],
'first_name' => [
'not_null' => true,
'unique' => 'full_name'
],
'last_name' => [
'not_null' => true,
'unique' => 'full_name'
],
'supervisor' => [
'foreign' => []
],
'supervised' => [
'virtual' => true,
'index' => true,
'foreign' => [
'key' => 'supervisor',
'multiple' => true
]
],
'office' => [
'foreign' => OfficeModel::class,
]
];
}

17
exemple/index.php Normal file
View File

@ -0,0 +1,17 @@
<?php
require_once('../vendor/autoload.php');
(new Krutush\Database\Connection('Databases.php'))->connect();
$UserModel = Krutush\Database\Exemple\Model\UserModel::class;
var_dump($UserModel::getTable());
//var_dump($UserModel::getFields());
var_dump((new $UserModel([
'id' => 42,
'first_name' => 'Pierre De La VIEDDDDDDD',
'last_name' => 'Caillou'
]))->insert());
//TODO: format src before commit

View File

@ -2,32 +2,40 @@
namespace Krutush\Database;
/**
* Load a config file to provide a list of pdo instances (Database)
*/
class Connection{
/** @var array */
protected static $databases = array();
/** @var array */
protected $settings;
/** Load settings */
public function __construct(string $path = null){
if(isset($path))
$this->settings = include($path);
}
public function connect(string $dbname = null){
/** Setup Database */
public function connect(string $dbname = null): Database{
if(static::exists($dbname))
throw new DatabaseException("Allready connect");
throw new \InvalidArgumentException("Allready connect");
$dbname = static::parseName($dbname);
if(!isset($this->settings[$dbname]))
throw new DatabaseException('Can\'t find '.$dbname.' in settings');
throw new \InvalidArgumentException('Can\'t find '.$dbname.' in settings');
static::$databases[$dbname] = new Database($this->settings[$dbname]);
return static::$databases[$dbname];
}
/** If you dont remember how to make a try-catch */
public function tryConnect(string $dbname = null, bool $quiet = false) {
try {
return $this->connect($dbname);
} catch (DatabaseException $e) {
} catch (\Exception $e) {
return $quiet ? false : $e;
}
}
@ -35,7 +43,7 @@ class Connection{
public static function get(string $dbname = null){
$dbname = static::parseName($dbname);
if(!static::exists($dbname))
throw new DatabaseException('Can\'t find "'.$dbname.'"');
throw new \InvalidArgumentException('Can\'t find "'.$dbname.'"');
return static::$databases[$dbname];
}
@ -50,7 +58,7 @@ class Connection{
public static function tryGet(string $dbname = null, bool $quiet = false) {
try {
return static::get($dbname);
} catch (DatabaseException $e) {
} catch (\Exception $e) {
return $quiet ? false : $e;
}
}
@ -58,7 +66,7 @@ class Connection{
public function tryGetCreate(string $dbname = null, bool $quiet = false) {
try {
return $this->getCreate($dbname);
} catch (DatabaseException $e) {
} catch (\Exception $e) {
return $quiet ? false : $e;
}
}

View File

@ -2,11 +2,23 @@
namespace Krutush\Database;
/**
* Extention around PDO
*/
class Database{
/** @var \PDO */
private $pdo;
private $debug = false;
/**
* Requests history if debug == true
*
* @var array
*/
private $requests = [];
/** @var bool */
private $debug = false;
public function __construct(array $settings){
$dns = $settings['driver'] .
':host=' . $settings['host'] .
@ -23,22 +35,29 @@ class Database{
}
public function prepare(string $request){
if($this->debug)
$this->requests[] = $request;
return $this->pdo->prepare($request);
}
public function execute(string $request, array $values = null, $row = false){
if($this->debug)
$time_start = microtime(true);
$req = $this->prepare($request);
$req->execute($values);
if($row == false)
if(!$row)
return $req->fetchAll();
if($this->debug)
$this->requests[] = [
'request' => $request,
'values' => $values,
'fetch' => !$row,
'time' => round((microtime(true) - $time_start) * 1000)
];
return $req;
}
public function select(array $fields = null){
public function select(array $fields = null): Request\Select{
$select = new Request\Select($this);
if(isset($fields))
return $select->fields($fields);
@ -46,7 +65,7 @@ class Database{
return $select;
}
public function insert(array $fields = null){
public function insert(array $fields = null): Request\Insert{
$insert = new Request\Insert($this);
if(isset($fields))
return $insert->fields($fields);
@ -54,7 +73,7 @@ class Database{
return $insert;
}
public function update(array $fields = null){
public function update(array $fields = null): Request\Update{
$update = new Request\Update($this);
if(isset($fields))
return $update->fields($fields);
@ -62,7 +81,7 @@ class Database{
return $update;
}
public function create(string $table = null){
public function create(string $table = null): Request\Create{
$create = new Request\Create($this);
if(isset($table))
return $create->table($table);
@ -70,7 +89,7 @@ class Database{
return $create;
}
public function drop(string $table = null){
public function drop(string $table = null): Request\Drop{
$drop = new Request\Drop($this);
if(isset($table))
return $drop->table($table);
@ -78,7 +97,7 @@ class Database{
return $drop;
}
public function delete(){
public function delete(): Request\Delete{
return new Request\Delete($this);
}
@ -89,5 +108,4 @@ class Database{
public function getRequests(): array{
return $this->requests;
}
//TODO update, delete
}

View File

@ -1,7 +0,0 @@
<?php
namespace Krutush\Database;
use \Exception;
class DatabaseException extends Exception {}

12
src/Migration.php Normal file
View File

@ -0,0 +1,12 @@
<?php
require_once('../../autoload.php');
echo "TODO";
/*
- migrate <file>
- roolback <file>
- new <name>
- create <model>
- status
*/

File diff suppressed because it is too large Load Diff

115
src/ModelID.php Normal file
View File

@ -0,0 +1,115 @@
<?php
namespace Krutush\Database;
use Krutush\Database\Request\Request;
class ModelID extends Model {
/** @var string */
public const ID = null;
/** @var bool */
public const ID_INCLUDE_TABLE = false;
/**
* Get table id name for static::ID or default one
*
* @see static::ID_INCLUDE_TABLE
* @return string
*/
public static function getID(): string{
return static::ID ?? (static::ID_INCLUDE_TABLE ? static::getTable().static::WORD_SEPARATOR : '').'id';
}
public static function getFields(): array{
if(empty(static::FIELDS))
trigger_error('Any data fields');
$fields = static::FIELDS;
$id = static::getID();
$fields[$id] = static::getFieldRawOptions($id);
if(!isset(static::$complete_fields))
static::$complete_fields = static::completeFields($fields);
return static::$complete_fields;
}
/**
* static::FIELDS[$field] with some checks
*
* @param string $field
* @return array options
*/
protected static function getFieldRawOptions(string $field): array{
if($field == static::getID())
return array_merge(
[
'type' => 'int',
'primary' => true,
'not_null' => true,
'custom' => 'AUTO_INCREMENT' //TODO: be smart
],
static::FIELDS[$field] ?? []
);
return parent::getFieldRawOptions($field);
}
/**
* add values to static::getInsert and run it
*
* @param boolean $setID must update id value
*/
public function insert(bool $setID = true){
$res = parent::insert();
if($setID)
$this->{static::getID()} = Connection::get(static::getDatabase())->getLastInsertID();
return $res;
}
/**
* Use static:ID to get row
*
* @param mixed $id int is a good idea
* @param array $loads run static::load
* @return self|null
*/
public static function find($id, array $loads = []): ?self{
return static::first([$id], Request::toParam(static::getID()), $loads);
}
/**
* Same as find but throw exception on null
*
* @param mixed $id int is a good idea
* @param array $loads run static::load
* @return self
*/
public static function findOrFail($id, array $loads = []): self{
return static::firstOrFail([$id], Request::toParam(static::getID()), $loads);
}
/**
* Use static:ID to get rows
*
* @param array $ids array(int) is a good idea
* @param array $loads run static::loads
* @return array
*/
public static function finds(array $ids, array $loads = []): array{
return static::all($ids, Request::inParams(static::getID(), count($ids)), $loads);
}
/**
* Same as find but throw exception on empty
*
* @param array $ids array(int) is a good idea
* @param array $loads run static::loads
* @return array
*/
public static function findsOrFail(array $ids, array $loads = []): array{
return static::allOrFail($ids, Request::inParams(static::getID(), count($ids)), $loads);
}
}

View File

@ -18,33 +18,23 @@ class Create extends Request{
return $this;
}
public function column(string $name, string $type, int $lenght = null, bool $not_null = false, string $more = null): Create{ //Really ?
$this->columns[] = '`'.$name.'` '.$type.($lenght ? '('.$lenght.')' : '').($not_null ? ' NOT NULL' : '').(isset($more) ? ' '.$more : '');
public function column(string $name, string $type, int $lenght = null, bool $not_null = false, string $more = null): Create{
$this->columns[] = compact('name', 'type', 'lenght', 'not_null', 'more');
return $this;
}
public function primary(string $name): Create{
$this->primary[] = '`'.$name.'`';
$this->primary[] = $name;
return $this;
}
public function unique(string $name): Create{
$this->unique[$name] = ['`'.$name.'`'];
public function unique(string $name, array $columns = null): Create{
$this->unique[$name] = $columns ?? [$name];
return $this;
}
public function uniques(string $name, array $columns): Create{
$this->unique[$name] = [$columns];
return $this;
}
public function index(string $name): Create{
$this->index[$name] = ['`'.$name.'`'];
return $this;
}
public function indexs(string $name, array $columns): Create{
$this->index[$name] = [$columns];
public function index(string $name, array $columns = null): Create{
$this->index[$name] = $columns ?? [$name];
return $this;
}
@ -55,10 +45,15 @@ class Create extends Request{
public function sql(){
if(!isset($this->table))
throw new DatabaseException('Any table set');
throw new \UnexpectedValueException('Any table set');
if(empty($this->columns))
throw new DatabaseException('Any columns set');
throw new \UnexpectedValueException('Any columns set');
$columns = [];
foreach($this->columns as $column){
$columns[] = $column['name'].' '.$column['type'].($column['lenght'] ? '('.$column['lenght'].')' : '').($column['not_null'] ? ' NOT NULL' : '').(isset($column['more']) ? ' '.$column['more'] : '');
}
$uniques = [];
foreach ($this->unique as $name => $columns) {
@ -77,10 +72,10 @@ class Create extends Request{
(isset($options['on_update']) ? 'ON UPDATE '.$options['on_update'] : '');
}
return 'CREATE TABLE `'.$this->table.'`('."\n".
$sql = implode(",\n",
return 'CREATE TABLE '.$this->table.'('."\n".
implode(",\n",
array_merge(
$this->columns,
$columns,
(empty($this->primary) ? [] : [
'CONSTRAINT `PK_'.ucfirst(strtolower(strtok($this->table, ' '))).'` PRIMARY KEY ('.implode(', ', $this->primary).')'
]),

View File

@ -2,20 +2,20 @@
namespace Krutush\Database\Request;
/** I'm the WHERE */
class Data extends Request{
/** @var array */
protected $values;
public function values(array $values, bool $add = false){
if($add){
if($add)
$this->values = array_merge($this->values, $values);
}else{
else
$this->values = $values;
}
return $this;
}
public function execute(string $sql, array $values = null){
$values = $values ? ($this->values ? array_merge($this->values, $values) : $values) : $this->values;
return parent::execute($sql, $values);
return parent::execute($sql, $values ? ($this->values ? array_merge($this->values, $values) : $values) : $this->values);
}
}

View File

@ -16,17 +16,23 @@ class Delete extends Data{
return $this;
}
public function where(string $where, bool $add = false): Delete{
$this->where = $add && $this->where ? '('.$this->where.') AND ('.$where.')' : $where;
/**
* @param string|array $where
* @param boolean $add
* @return Delete
*/
public function where($where, bool $add = false): Delete{
$where = is_array($where) ? $where : [$where];
$this->where = $add && $this->where ? array_merge($this->where, $where) : $where;
return $this;
}
public function sql(){
if(!isset($this->table))
throw new DatabaseException('Any table set');
throw new \UnexpectedValueException('Any table set');
$sql = 'DELETE FROM '.$this->table.
($this->where ? ("\n".'WHERE '.$this->where) : '');
($this->where ? ("\n".'WHERE '.static::combineParams($this->where)) : '');
return $sql;
}

View File

@ -15,7 +15,7 @@ class Drop extends Request{
public function sql(){
if(!isset($this->table))
throw new DatabaseException('Any table set');
throw new \UnexpectedValueException('Any table set');
return 'DROP TABLE `'.$this->table.'`';
}

View File

@ -20,11 +20,11 @@ class Insert extends Data{
public function sql(){
if(!isset($this->table))
throw new DatabaseException('Any table set');
throw new \UnexpectedValueException('Any table set');
return 'INSERT INTO `'.$this->table."`\n".
return 'INSERT INTO '.$this->table."\n".
'('.implode(', ', $this->fields).")\n".
'VALUES ('. str_repeat('?, ', count($this->fields)-1).(count($this->fields) > 0 ? '?' : '').')';
'VALUES ('.static::paramList(count($this->fields)).')';
}
public function run(array $values = null){

View File

@ -4,14 +4,39 @@ namespace Krutush\Database\Request;
use Krutush\Database\Database;
class Request{ //TODO: escape and slugify
/** Base of any SQL request */
class Request{
/** @var Database */
protected $db;
/** Create it */
public function __construct(Database $db){
$this->db = $db;
}
/** Run it */
protected function execute(string $sql, array $values = null){
return $this->db->execute($sql, $values, true);
}
/*=== TOOLS ===*/
public static function toParam(string $name, string $operator = '='): string{
return $name.' '.$operator.' ?';
}
public static function toParams(array $names): string{
return implode(', ', array_map(function($name){ return static::toParam($name); }, $names));
}
public static function paramList(int $count): string{
return implode(',', array_fill(0, $count, '?'));
}
public static function inParams(string $name, $params): string{
return $name.' IN ('.static::paramList(is_int($params) ? $params : count($params)).')';
}
public static function combineParams(array $params, string $operator = ' AND '): string{
return implode($operator, $params);
}
}

View File

@ -8,9 +8,16 @@ namespace Krutush\Database\Request;
use Krutush\Database\DatabaseException;
class Select extends Data{
/** @var array */
protected $fields;
/** @var string */
protected $table;
/** @var array */
protected $where;
/** @var string */
protected $group;
protected $order;
protected $limit;
@ -29,13 +36,19 @@ class Select extends Data{
public function join(string $joins, string $type = 'INNER', bool $add = false): Select{
if(!in_array($type, array('INNER', 'LEFT', 'RIGHT')))
throw new DatabaseException('Unknown JOIN type');
throw new \InvalidArgumentException('Unknown JOIN type');
$this->joins = ($add && $this->joins ? $this->joins."\n" : '').$type.' JOIN '.$joins;
return $this;
}
public function where(string $where, bool $add = false): Select{
$this->where = $add && $this->where ? '('.$this->where.') AND ('.$where.')' : $where;
/**
* @param string|array $where
* @param boolean $add
* @return Select
*/
public function where($where, bool $add = false): Select{
$where = is_array($where) ? $where : [$where];
$this->where = $add && $this->where ? array_merge($this->where, $where) : $where;
return $this;
}
@ -61,27 +74,21 @@ class Select extends Data{
public function sql(){
if(!isset($this->table))
throw new DatabaseException('Any table set');
throw new \UnexpectedValueException('Any table set');
$fields = '*';
if(isset($this->fields)){
$numItems = count($this->fields);
$i = 0;
$fields = '';
$lines = [];
foreach($this->fields as $key => $value){
$fields .= $value;
if(is_string($key))
$fields .= ' '.$key;
if(++$i !== $numItems) //Not last
$fields .= ', ';
$lines[] = $value.(is_string($key) ? ' '.$key : '');
}
$fields = implode(', ', $lines);
}
$sql = 'SELECT '.$fields.
"\n".'FROM '.$this->table.
($this->joins ? ("\n".$this->joins) : '').
($this->where ? ("\n".'WHERE '.$this->where) : '').
($this->where ? ("\n".'WHERE '.static::combineParams($this->where)) : '').
($this->group ? ("\n".'GROUP BY '.$this->group) : '').
($this->order ? ("\n".'ORDER BY '.$this->order) : '').
($this->limit ? ("\n".'LIMIT '.$this->limit) : '').

View File

@ -4,9 +4,15 @@ namespace Krutush\Database\Request;
use Krutush\Database\DatabaseException;
/** UPDATE */
class Update extends Data{
/** @var array */
protected $fields;
/** @var string */
protected $table;
/** @var array */
protected $where;
public function fields(array $fields = null, bool $add = false): Update{
@ -19,18 +25,24 @@ class Update extends Data{
return $this;
}
public function where(string $where, bool $add = false): Update{
$this->where = $add && $this->where ? '('.$this->where.') AND ('.$where.')' : $where;
/**
* @param string|array $where
* @param boolean $add
* @return Update
*/
public function where($where, bool $add = false): Update{
$where = is_array($where) ? $where : [$where];
$this->where = $add && $this->where ? array_merge($this->where, $where) : $where;
return $this;
}
public function sql(){
if(!isset($this->table))
throw new DatabaseException('Any table set');
throw new \UnexpectedValueException('Any table set');
return 'UPDATE `'.$this->table."`\n".
'SET '.implode(', ', array_map(function($field){ return $field.' = ?'; }, $this->fields))."\n".
(isset($this->where) ? ('WHERE '.$this->where) : '');
return 'UPDATE '.$this->table."\n".
'SET '.static::toParams($this->fields)."\n".
($this->where ? 'WHERE '.static::combineParams($this->where) : '');
}
public function run(array $values = null){

21
src/TypeHelper.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace Krutush\Database;
class TypeHelper{
private static function dateTimeConverter(string $format, $value): string{
return (is_a($value, \DateTime::class) ? $value : new \DateTime(strval($value)))->format($format);
}
public static function dateConvert($value): string{
return static::dateTimeConverter('Y-m-d', $value);
}
public static function timeConvert($value): string{
return static::dateTimeConverter('H:i:s', $value);
}
public static function datetimeConvert($value): string{
return static::dateTimeConverter('Y-m-d H:i:s', $value);
}
}