big updat
larg
This commit is contained in:
parent
6edc85b731
commit
4fd5e5fbd9
10 changed files with 190 additions and 70 deletions
|
@ -13,7 +13,7 @@ class Configuration {
|
|||
if(self::$data != null)
|
||||
return;
|
||||
|
||||
self::$data = parse_ini_string($data);
|
||||
self::$data = parse_ini_string($data, true);
|
||||
}
|
||||
|
||||
public static function section(string $name): ConfigSection {
|
||||
|
|
|
@ -10,6 +10,7 @@ class Database {
|
|||
/** @var \PDO */
|
||||
private static $dbConn;
|
||||
private static $queries = [];
|
||||
private static $batchQueue = [];
|
||||
|
||||
public static function initialize() {
|
||||
self::$dbConn = new \PDO(
|
||||
|
@ -17,16 +18,36 @@ class Database {
|
|||
conf::section(AMVC_CNF_DB)->value(AMVC_CNF_DB_USER),
|
||||
conf::section(AMVC_CNF_DB)->value(AMVC_CNF_DB_PASS)
|
||||
);
|
||||
|
||||
self::$dbConn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
public static function select($columns): Selectable {
|
||||
return new Selectable($columns);
|
||||
public static function select($columns, $model = null): Selectable {
|
||||
return new Selectable($columns, $model);
|
||||
}
|
||||
|
||||
public static function update($table): Modifiable {
|
||||
return new Modifiable($table);
|
||||
}
|
||||
|
||||
public static function insert($table): Insertable {
|
||||
return new Insertable($table);
|
||||
}
|
||||
|
||||
public static function delete($table): Deletable {
|
||||
return new Deletable($table);
|
||||
}
|
||||
|
||||
public static function batchQuery(Queryable $query) {
|
||||
self::getStatement($query->getRawQuery());
|
||||
$batchQueue[] = [self::hashQuery($query->getRawQuery()), $query->getParams()];
|
||||
}
|
||||
|
||||
public static function rawQuery(string $query, $params = null, int $type = AMVC_DB_FETCH_ROWS): array {
|
||||
$stmt = self::getStatement($query);
|
||||
foreach($params as $name => $param)
|
||||
$stmt->bindParam(":$name", $param);
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
switch($type) {
|
||||
|
@ -76,4 +97,18 @@ class Database {
|
|||
|
||||
return md5($query);
|
||||
}
|
||||
|
||||
protected static function updateDatabase() {
|
||||
self::$dbConn->beginTransaction();
|
||||
|
||||
foreach(self::$batchQueue as $query) {
|
||||
$stmt = self::getStatement($query[0]);
|
||||
foreach($query[1] as $name => $param)
|
||||
$stmt->bindValue(":$name", $param);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
self::$dbConn->commit();
|
||||
self::$batchQueue = [];
|
||||
}
|
||||
}
|
6
AroMVC/Deletable.php
Normal file
6
AroMVC/Deletable.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
namespace AroMVC\Core;
|
||||
|
||||
class Deletable extends Queryable {
|
||||
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
<?php
|
||||
namespace AroMVC\Core;
|
||||
use AroMVC\Core\Database as db;
|
||||
|
||||
abstract class Model {
|
||||
protected $rawData = [];
|
||||
protected $associations = [];
|
||||
protected $hooks = [];
|
||||
|
||||
protected static $table = null;
|
||||
protected $index = "id";
|
||||
protected $deleted = false;
|
||||
protected $modified = [];
|
||||
|
||||
public function __construct() {}
|
||||
public static function withId(int $id) {
|
||||
|
@ -25,6 +29,9 @@ abstract class Model {
|
|||
}
|
||||
|
||||
protected abstract function initialize();
|
||||
protected function setTable(string $tableName) {
|
||||
$this->table = $tableName;
|
||||
}
|
||||
|
||||
protected function get(string $name) {
|
||||
return $this->rawData[$name];
|
||||
|
@ -61,11 +68,15 @@ abstract class Model {
|
|||
$this->associations[strtolower($associationName)] = strtolower($rawName);
|
||||
}
|
||||
|
||||
protected function update() {
|
||||
public static function select(): Selectable {
|
||||
return db::select("*", new static)->from(self::$table);
|
||||
}
|
||||
|
||||
public function update() {
|
||||
|
||||
}
|
||||
|
||||
protected function delete() {
|
||||
public function delete() {
|
||||
|
||||
}
|
||||
}
|
6
AroMVC/Modifiable.php
Normal file
6
AroMVC/Modifiable.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
namespace AroMVC\Core;
|
||||
|
||||
class Modifiable extends Queryable {
|
||||
|
||||
}
|
|
@ -18,6 +18,14 @@ abstract class Queryable {
|
|||
$this->results = db::rawRowQuery($this->query, $this->params);
|
||||
}
|
||||
|
||||
public function getRawQuery() {
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function getParams() {
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/*protected function allowConditionals(bool $allow) {
|
||||
if($allow)
|
||||
$this->setFlag(AMVC_QRY_CNDLS);
|
||||
|
@ -60,4 +68,9 @@ abstract class Queryable {
|
|||
protected function unlock(int $lock) {
|
||||
$this->locks &= ~$lock;
|
||||
}
|
||||
|
||||
protected function testLocked() {
|
||||
if($this->locks != 0)
|
||||
throw new \Exception("Query is locked from previous step and cannot proceed (LOCKVAL $this->locks)");
|
||||
}
|
||||
}
|
|
@ -12,9 +12,9 @@ define("AMVC_QRY_SEL_HAVING", 5);
|
|||
define("AMVC_QRY_SEL_ORDER", 6);
|
||||
define("AMVC_QRY_SEL_LIMIT", 7);
|
||||
|
||||
// TODO allow appending and rewriting of existing data in a query from any point
|
||||
|
||||
class Selectable extends Queryable {
|
||||
/** @var null|\ReflectionClass */
|
||||
protected $model = null;
|
||||
protected $components = [
|
||||
AMVC_QRY_SEL_COLS => [],
|
||||
AMVC_QRY_SEL_FROM => null,
|
||||
|
@ -26,7 +26,22 @@ class Selectable extends Queryable {
|
|||
AMVC_QRY_SEL_LIMIT => null
|
||||
];
|
||||
|
||||
public function __construct($selection) {
|
||||
protected function getModelReflection($model): \ReflectionClass {
|
||||
$type = new \ReflectionClass($model);
|
||||
if(!$type->isSubclassOf("\\AroMVC\\Core\\AroModel"))
|
||||
throw new \Exception("Cannot instantiate non-model object.");
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
protected function isEmpty(int $component): bool {
|
||||
return count($this->components[$component]) == 0;
|
||||
}
|
||||
|
||||
public function __construct($selection, $model = null) {
|
||||
if($model != null)
|
||||
$this->model = $this->getModelReflection($model);
|
||||
|
||||
if(!is_array($selection))
|
||||
$selection = [$selection];
|
||||
|
||||
|
@ -34,10 +49,9 @@ class Selectable extends Queryable {
|
|||
}
|
||||
|
||||
public function execute(): Selectable {
|
||||
if($this->checkFlag(AMVC_QRY_SEL_NEEDON))
|
||||
throw new \Exception("JOIN clause declared in query with no matching ON or USING clause");
|
||||
$this->testLocked();
|
||||
|
||||
if($this->checkFlag(AMVC_QRY_SEL_GROUP) && !$this->checkFlag(AMVC_QRY_SEL_ORDER)) {
|
||||
/*if($this->checkFlag(AMVC_QRY_SEL_GROUP) && !$this->checkFlag(AMVC_QRY_SEL_ORDER)) {
|
||||
if($this->checkFlag(AMVC_QRY_SEL_HAVING)) {
|
||||
$having = array_search("HAVING", $this->query);
|
||||
array_splice($this->query, $having + 2, 0, ["ORDER BY", "null"]);
|
||||
|
@ -45,103 +59,133 @@ class Selectable extends Queryable {
|
|||
$group = array_search("GROUP BY", $this->query);
|
||||
array_splice($this->query, $group + 2, 0, ["ORDER BY", "null"]);
|
||||
}
|
||||
}*/
|
||||
|
||||
$query = [
|
||||
"SELECT",
|
||||
implode(",", $this->components[AMVC_QRY_SEL_COLS]),
|
||||
"FROM",
|
||||
"`". $this->components[AMVC_QRY_SEL_FROM] ."`"
|
||||
];
|
||||
|
||||
foreach($this->components[AMVC_QRY_SEL_JOINS] as $join)
|
||||
array_push($query, implode(" ", $join));
|
||||
|
||||
if(!$this->isEmpty(AMVC_QRY_SEL_WHERE)) {
|
||||
$wheres = array_map(function($x) {
|
||||
return "($x)";
|
||||
}, $this->components[AMVC_QRY_SEL_WHERE]);
|
||||
array_push($query, "WHERE", implode(" AND ", $wheres));
|
||||
}
|
||||
|
||||
if(!$this->isEmpty(AMVC_QRY_SEL_GROUP))
|
||||
array_push($query, "GROUP BY", implode(", ", $this->components[AMVC_QRY_SEL_GROUP]));
|
||||
|
||||
if(!$this->isEmpty(AMVC_QRY_SEL_HAVING)) {
|
||||
$haves = array_map(function($x) {
|
||||
return "($x)";
|
||||
}, $this->components[AMVC_QRY_SEL_HAVING]);
|
||||
array_push($query, "HAVING", implode(" AND ", $haves));
|
||||
}
|
||||
|
||||
if(!$this->isEmpty(AMVC_QRY_SEL_ORDER))
|
||||
array_push($query, "ORDER BY", implode(", ", $this->components[AMVC_QRY_SEL_GROUP]));
|
||||
|
||||
if($this->components[AMVC_QRY_SEL_LIMIT] != null)
|
||||
array_push($query, "LIMIT", $this->components[AMVC_QRY_SEL_LIMIT[0]] .",". $this->components[AMVC_QRY_SEL_LIMIT[1]]);
|
||||
|
||||
$this->query = implode(" ", $query);
|
||||
parent::execute();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function from(string $where): Selectable {
|
||||
$this->testLocked();
|
||||
if($this->components[AMVC_QRY_SEL_FROM] != null)
|
||||
throw new \Exception("Cannot redefine FROM clause after first definition");
|
||||
|
||||
array_push($this->query, "FROM", $where);
|
||||
$this->components[AMVC_QRY_SEL_FROM] = $where;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function columns($columns): Selectable {
|
||||
$this->testLocked();
|
||||
if(!is_array($columns))
|
||||
$columns = [$columns];
|
||||
|
||||
$this->components[AMVC_QRY_SEL_COLS] = $columns;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function join(string $type, string $table): Selectable {
|
||||
if($this->checkPast(AMVC_QRY_SEL_JOINS))
|
||||
throw new \Exception("Invalid JOIN clause, must proceed FROM clause");
|
||||
|
||||
$this->setFlag(AMVC_QRY_SEL_JOINS);
|
||||
$this->setFlag(AMVC_QRY_SEL_NEEDON);
|
||||
array_push($this->query, $type, $table);
|
||||
$this->testLocked();
|
||||
$this->lock(AMVC_QRY_SEL_LCK_JOIN);
|
||||
array_push($this->components[AMVC_QRY_SEL_JOINS], [$type, $table]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function on(string $condition): Selectable {
|
||||
if(!$this->checkFlag(AMVC_QRY_SEL_NEEDON))
|
||||
throw new \Exception("Cannot declare ON clause without prior JOIN clause");
|
||||
if(!$this->checkLock(AMVC_QRY_SEL_LCK_JOIN))
|
||||
throw new \Exception("ON subclause can only follow a JOIN clause");
|
||||
|
||||
$this->clearFlag(AMVC_QRY_SEL_NEEDON);
|
||||
array_push($this->query, "ON", $condition);
|
||||
$join = array_merge(
|
||||
array_pop($this->components[AMVC_QRY_SEL_JOINS]),
|
||||
["ON", $condition]
|
||||
);
|
||||
array_push($this->components[AMVC_QRY_SEL_JOINS], $join);
|
||||
$this->unlock(AMVC_QRY_SEL_LCK_JOIN);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function using($columns): Selectable {
|
||||
if(!$this->checkFlag(AMVC_QRY_SEL_NEEDON))
|
||||
throw new \Exception("Cannot declare USING clause without prior JOIN clause");
|
||||
if(!$this->checkLock(AMVC_QRY_SEL_LCK_JOIN))
|
||||
throw new \Exception("USING subclause can only follow a JOIN clause");
|
||||
|
||||
if(is_array($columns))
|
||||
$columns = implode(",", $columns);
|
||||
|
||||
$this->clearFlag(AMVC_QRY_SEL_NEEDON);
|
||||
array_push($this->query, "USING", "({$columns})");
|
||||
$join = array_merge(
|
||||
array_pop($this->components[AMVC_QRY_SEL_JOINS]),
|
||||
["USING", "($columns)"]
|
||||
);
|
||||
array_push($this->components[AMVC_QRY_SEL_JOINS], $join);
|
||||
$this->unlock(AMVC_QRY_SEL_LCK_JOIN);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function where(string $condition): Selectable {
|
||||
$this->checkFrom();
|
||||
if($this->checkForOrPast(AMVC_QRY_SEL_WHERE))
|
||||
throw new \Exception("Duplicate or misplaced WHERE clause in SELECT query");
|
||||
|
||||
$this->setFlag(AMVC_QRY_SEL_WHERE);
|
||||
array_push($this->query, "WHERE", $condition);
|
||||
$this->testLocked();
|
||||
array_push($this->components[AMVC_QRY_SEL_WHERE], $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function groupBy($columns): Selectable {
|
||||
$this->checkFrom();
|
||||
if($this->checkForOrPast(AMVC_QRY_SEL_GROUP))
|
||||
throw new \Exception("Duplicate or misplaced GROUP BY clause in SELECT query");
|
||||
|
||||
$this->testLocked();
|
||||
if(is_array($columns))
|
||||
$columns = implode(",", $columns);
|
||||
|
||||
$this->setFlag(AMVC_QRY_SEL_GROUP);
|
||||
array_push($this->query, "GROUP BY", $columns);
|
||||
array_push($this->components[AMVC_QRY_SEL_GROUP], $columns);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function having(string $condition): Selectable {
|
||||
$this->checkFrom();
|
||||
if($this->checkForOrPast(AMVC_QRY_SEL_HAVING))
|
||||
throw new \Exception("Duplicate or misplaced HAVING clause in SELECT query");
|
||||
|
||||
array_push($this->query, "HAVING", $condition);
|
||||
$this->testLocked();
|
||||
array_push($this->components[AMVC_QRY_SEL_HAVING], $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orderBy($columns): Selectable {
|
||||
$this->checkFrom();
|
||||
if($this->checkForOrPast(AMVC_QRY_SEL_ORDER))
|
||||
throw new \Exception("Duplicate or misplaced ORDER BY clause in SELECT query");
|
||||
$this->testLocked();
|
||||
if(!is_array($columns))
|
||||
$columns = [$columns];
|
||||
|
||||
if(is_array($columns))
|
||||
$columns = implode(",", $columns);
|
||||
|
||||
$this->setFlag(AMVC_QRY_SEL_ORDER);
|
||||
array_push($this->query, "ORDER BY", $columns);
|
||||
array_push($this->components[AMVC_QRY_SEL_ORDER], $columns);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function limit(int $count, int $offset = 0): Selectable {
|
||||
$this->checkFrom();
|
||||
if($this->checkForOrPast(AMVC_QRY_SEL_LIMIT))
|
||||
throw new \Exception("Duplicate or misplaced LIMIT clause in SELECT query");
|
||||
|
||||
$this->setFlag(AMVC_QRY_SEL_LIMIT);
|
||||
array_push($this->query, "LIMIT", "$offset,$count");
|
||||
$this->testLocked();
|
||||
$this->components[AMVC_QRY_SEL_LIMIT] = [$offset, $count];
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -152,15 +196,14 @@ class Selectable extends Queryable {
|
|||
return $this->results;
|
||||
}
|
||||
|
||||
public function asModels($obj) {
|
||||
public function asModels($model = null) {
|
||||
if($this->results == null)
|
||||
throw new \Exception("Cannot return results from a query that has not executed.");
|
||||
|
||||
$type = new \ReflectionClass($obj);
|
||||
if(!$type->isSubclassOf("\\AroMVC\\Core\\AroModel"))
|
||||
throw new \Exception("Cannot instantiate non-model object.");
|
||||
if($this->model == null || $model != null)
|
||||
$this->model = $this->getModelReflection($model);
|
||||
|
||||
foreach($this->results as $result)
|
||||
yield $type->getMethod("withRow")->invoke(null, $result);
|
||||
yield $this->model->getMethod("withRow")->invoke(null, $result);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ namespace AroMVC\Models;
|
|||
use \AroMVC\Core\Model;
|
||||
|
||||
class State extends Model {
|
||||
protected static $table = "State";
|
||||
|
||||
protected $id;
|
||||
protected $name;
|
||||
|
||||
|
|
4
conf.ini
4
conf.ini
|
@ -1,4 +1,4 @@
|
|||
[Database]
|
||||
dsn = "mysql:host=localhost;dbname=fire"
|
||||
username = "squidlord"
|
||||
dsn = "mysql:host=localhost;dbname=test"
|
||||
username = "root"
|
||||
password = ""
|
16
index.php
16
index.php
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
namespace AroMVC;
|
||||
use AroMVC\Core\Configuration;
|
||||
use AroMVC\Core\Database;
|
||||
use AroMVC\Core\Selectable;
|
||||
use AroMVC\Models\Company;
|
||||
|
@ -22,14 +23,17 @@ spl_autoload_register(function($class) {
|
|||
}
|
||||
});
|
||||
|
||||
// TODO write error handler
|
||||
|
||||
Configuration::initialize(file_get_contents("conf.ini"));
|
||||
Database::initialize();
|
||||
|
||||
/*$tmp = new Selectable("*");
|
||||
$tmp = new Selectable("*");
|
||||
|
||||
$tmp->from("Companies")
|
||||
->where("`name` = ?")
|
||||
->or("`id` = ?")
|
||||
->where("`name` = :name OR `id` = :cid")
|
||||
->params(["name" => "winco", "cid" => 20])
|
||||
->join("LEFT JOIN", "Invoices")
|
||||
->using("id")
|
||||
->execute()
|
||||
->asModels(new Company);*/
|
||||
|
||||
|
||||
->asModels(new Company);
|
Reference in a new issue