big updat

larg
This commit is contained in:
Malloc of Kuzkycyziklistan 2017-02-28 15:46:54 -06:00
parent 6edc85b731
commit 4fd5e5fbd9
10 changed files with 190 additions and 70 deletions

View file

@ -13,7 +13,7 @@ class Configuration {
if(self::$data != null) if(self::$data != null)
return; return;
self::$data = parse_ini_string($data); self::$data = parse_ini_string($data, true);
} }
public static function section(string $name): ConfigSection { public static function section(string $name): ConfigSection {

View file

@ -10,6 +10,7 @@ class Database {
/** @var \PDO */ /** @var \PDO */
private static $dbConn; private static $dbConn;
private static $queries = []; private static $queries = [];
private static $batchQueue = [];
public static function initialize() { public static function initialize() {
self::$dbConn = new \PDO( 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_USER),
conf::section(AMVC_CNF_DB)->value(AMVC_CNF_DB_PASS) 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 { public static function select($columns, $model = null): Selectable {
return new Selectable($columns); 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 { public static function rawQuery(string $query, $params = null, int $type = AMVC_DB_FETCH_ROWS): array {
$stmt = self::getStatement($query); $stmt = self::getStatement($query);
foreach($params as $name => $param) foreach($params as $name => $param)
$stmt->bindParam(":$name", $param); $stmt->bindParam(":$name", $param);
$stmt->execute(); $stmt->execute();
switch($type) { switch($type) {
@ -76,4 +97,18 @@ class Database {
return md5($query); 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
View file

@ -0,0 +1,6 @@
<?php
namespace AroMVC\Core;
class Deletable extends Queryable {
}

View file

@ -1,12 +1,16 @@
<?php <?php
namespace AroMVC\Core; namespace AroMVC\Core;
use AroMVC\Core\Database as db;
abstract class Model { abstract class Model {
protected $rawData = []; protected $rawData = [];
protected $associations = []; protected $associations = [];
protected $hooks = []; protected $hooks = [];
protected static $table = null;
protected $index = "id"; protected $index = "id";
protected $deleted = false; protected $deleted = false;
protected $modified = [];
public function __construct() {} public function __construct() {}
public static function withId(int $id) { public static function withId(int $id) {
@ -25,6 +29,9 @@ abstract class Model {
} }
protected abstract function initialize(); protected abstract function initialize();
protected function setTable(string $tableName) {
$this->table = $tableName;
}
protected function get(string $name) { protected function get(string $name) {
return $this->rawData[$name]; return $this->rawData[$name];
@ -61,11 +68,15 @@ abstract class Model {
$this->associations[strtolower($associationName)] = strtolower($rawName); $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
View file

@ -0,0 +1,6 @@
<?php
namespace AroMVC\Core;
class Modifiable extends Queryable {
}

View file

@ -18,6 +18,14 @@ abstract class Queryable {
$this->results = db::rawRowQuery($this->query, $this->params); $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) { /*protected function allowConditionals(bool $allow) {
if($allow) if($allow)
$this->setFlag(AMVC_QRY_CNDLS); $this->setFlag(AMVC_QRY_CNDLS);
@ -60,4 +68,9 @@ abstract class Queryable {
protected function unlock(int $lock) { protected function unlock(int $lock) {
$this->locks &= ~$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)");
}
} }

View file

@ -12,9 +12,9 @@ define("AMVC_QRY_SEL_HAVING", 5);
define("AMVC_QRY_SEL_ORDER", 6); define("AMVC_QRY_SEL_ORDER", 6);
define("AMVC_QRY_SEL_LIMIT", 7); define("AMVC_QRY_SEL_LIMIT", 7);
// TODO allow appending and rewriting of existing data in a query from any point
class Selectable extends Queryable { class Selectable extends Queryable {
/** @var null|\ReflectionClass */
protected $model = null;
protected $components = [ protected $components = [
AMVC_QRY_SEL_COLS => [], AMVC_QRY_SEL_COLS => [],
AMVC_QRY_SEL_FROM => null, AMVC_QRY_SEL_FROM => null,
@ -26,7 +26,22 @@ class Selectable extends Queryable {
AMVC_QRY_SEL_LIMIT => null 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)) if(!is_array($selection))
$selection = [$selection]; $selection = [$selection];
@ -34,10 +49,9 @@ class Selectable extends Queryable {
} }
public function execute(): Selectable { public function execute(): Selectable {
if($this->checkFlag(AMVC_QRY_SEL_NEEDON)) $this->testLocked();
throw new \Exception("JOIN clause declared in query with no matching ON or USING clause");
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)) { if($this->checkFlag(AMVC_QRY_SEL_HAVING)) {
$having = array_search("HAVING", $this->query); $having = array_search("HAVING", $this->query);
array_splice($this->query, $having + 2, 0, ["ORDER BY", "null"]); 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); $group = array_search("GROUP BY", $this->query);
array_splice($this->query, $group + 2, 0, ["ORDER BY", "null"]); 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(); parent::execute();
return $this; return $this;
} }
public function from(string $where): Selectable { public function from(string $where): Selectable {
$this->testLocked();
if($this->components[AMVC_QRY_SEL_FROM] != null) if($this->components[AMVC_QRY_SEL_FROM] != null)
throw new \Exception("Cannot redefine FROM clause after first definition"); 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; return $this;
} }
public function join(string $type, string $table): Selectable { public function join(string $type, string $table): Selectable {
if($this->checkPast(AMVC_QRY_SEL_JOINS)) $this->testLocked();
throw new \Exception("Invalid JOIN clause, must proceed FROM clause"); $this->lock(AMVC_QRY_SEL_LCK_JOIN);
array_push($this->components[AMVC_QRY_SEL_JOINS], [$type, $table]);
$this->setFlag(AMVC_QRY_SEL_JOINS);
$this->setFlag(AMVC_QRY_SEL_NEEDON);
array_push($this->query, $type, $table);
return $this; return $this;
} }
public function on(string $condition): Selectable { public function on(string $condition): Selectable {
if(!$this->checkFlag(AMVC_QRY_SEL_NEEDON)) if(!$this->checkLock(AMVC_QRY_SEL_LCK_JOIN))
throw new \Exception("Cannot declare ON clause without prior JOIN clause"); throw new \Exception("ON subclause can only follow a JOIN clause");
$this->clearFlag(AMVC_QRY_SEL_NEEDON); $join = array_merge(
array_push($this->query, "ON", $condition); 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; return $this;
} }
public function using($columns): Selectable { public function using($columns): Selectable {
if(!$this->checkFlag(AMVC_QRY_SEL_NEEDON)) if(!$this->checkLock(AMVC_QRY_SEL_LCK_JOIN))
throw new \Exception("Cannot declare USING clause without prior JOIN clause"); throw new \Exception("USING subclause can only follow a JOIN clause");
if(is_array($columns)) if(is_array($columns))
$columns = implode(",", $columns); $columns = implode(",", $columns);
$this->clearFlag(AMVC_QRY_SEL_NEEDON); $join = array_merge(
array_push($this->query, "USING", "({$columns})"); 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; return $this;
} }
public function where(string $condition): Selectable { public function where(string $condition): Selectable {
$this->checkFrom(); $this->testLocked();
if($this->checkForOrPast(AMVC_QRY_SEL_WHERE)) array_push($this->components[AMVC_QRY_SEL_WHERE], $condition);
throw new \Exception("Duplicate or misplaced WHERE clause in SELECT query");
$this->setFlag(AMVC_QRY_SEL_WHERE);
array_push($this->query, "WHERE", $condition);
return $this; return $this;
} }
public function groupBy($columns): Selectable { public function groupBy($columns): Selectable {
$this->checkFrom(); $this->testLocked();
if($this->checkForOrPast(AMVC_QRY_SEL_GROUP))
throw new \Exception("Duplicate or misplaced GROUP BY clause in SELECT query");
if(is_array($columns)) if(is_array($columns))
$columns = implode(",", $columns); $columns = implode(",", $columns);
$this->setFlag(AMVC_QRY_SEL_GROUP); array_push($this->components[AMVC_QRY_SEL_GROUP], $columns);
array_push($this->query, "GROUP BY", $columns);
return $this; return $this;
} }
public function having(string $condition): Selectable { public function having(string $condition): Selectable {
$this->checkFrom(); $this->testLocked();
if($this->checkForOrPast(AMVC_QRY_SEL_HAVING)) array_push($this->components[AMVC_QRY_SEL_HAVING], $condition);
throw new \Exception("Duplicate or misplaced HAVING clause in SELECT query");
array_push($this->query, "HAVING", $condition);
return $this; return $this;
} }
public function orderBy($columns): Selectable { public function orderBy($columns): Selectable {
$this->checkFrom(); $this->testLocked();
if($this->checkForOrPast(AMVC_QRY_SEL_ORDER)) if(!is_array($columns))
throw new \Exception("Duplicate or misplaced ORDER BY clause in SELECT query"); $columns = [$columns];
if(is_array($columns)) array_push($this->components[AMVC_QRY_SEL_ORDER], $columns);
$columns = implode(",", $columns);
$this->setFlag(AMVC_QRY_SEL_ORDER);
array_push($this->query, "ORDER BY", $columns);
return $this; return $this;
} }
public function limit(int $count, int $offset = 0): Selectable { public function limit(int $count, int $offset = 0): Selectable {
$this->checkFrom(); $this->testLocked();
if($this->checkForOrPast(AMVC_QRY_SEL_LIMIT)) $this->components[AMVC_QRY_SEL_LIMIT] = [$offset, $count];
throw new \Exception("Duplicate or misplaced LIMIT clause in SELECT query");
$this->setFlag(AMVC_QRY_SEL_LIMIT);
array_push($this->query, "LIMIT", "$offset,$count");
return $this; return $this;
} }
@ -152,15 +196,14 @@ class Selectable extends Queryable {
return $this->results; return $this->results;
} }
public function asModels($obj) { public function asModels($model = null) {
if($this->results == null) if($this->results == null)
throw new \Exception("Cannot return results from a query that has not executed."); throw new \Exception("Cannot return results from a query that has not executed.");
$type = new \ReflectionClass($obj); if($this->model == null || $model != null)
if(!$type->isSubclassOf("\\AroMVC\\Core\\AroModel")) $this->model = $this->getModelReflection($model);
throw new \Exception("Cannot instantiate non-model object.");
foreach($this->results as $result) foreach($this->results as $result)
yield $type->getMethod("withRow")->invoke(null, $result); yield $this->model->getMethod("withRow")->invoke(null, $result);
} }
} }

View file

@ -3,6 +3,8 @@ namespace AroMVC\Models;
use \AroMVC\Core\Model; use \AroMVC\Core\Model;
class State extends Model { class State extends Model {
protected static $table = "State";
protected $id; protected $id;
protected $name; protected $name;

View file

@ -1,4 +1,4 @@
[Database] [Database]
dsn = "mysql:host=localhost;dbname=fire" dsn = "mysql:host=localhost;dbname=test"
username = "squidlord" username = "root"
password = "" password = ""

View file

@ -1,5 +1,6 @@
<?php <?php
namespace AroMVC; namespace AroMVC;
use AroMVC\Core\Configuration;
use AroMVC\Core\Database; use AroMVC\Core\Database;
use AroMVC\Core\Selectable; use AroMVC\Core\Selectable;
use AroMVC\Models\Company; 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(); Database::initialize();
/*$tmp = new Selectable("*"); $tmp = new Selectable("*");
$tmp->from("Companies") $tmp->from("Companies")
->where("`name` = ?") ->where("`name` = :name OR `id` = :cid")
->or("`id` = ?") ->params(["name" => "winco", "cid" => 20])
->join("LEFT JOIN", "Invoices")
->using("id")
->execute() ->execute()
->asModels(new Company);*/ ->asModels(new Company);