|
@@ -1,9 +1,9 @@
|
|
|
<?php
|
|
|
namespace Juju;
|
|
|
- require_once('sql.class.php');
|
|
|
+ require_once('pdo.class.php');
|
|
|
require_once('ORM/relationship.class.php');
|
|
|
require_once('Data/securestring.class.php');
|
|
|
- use \Juju\{ORM\Relationship, Data\SecureString};
|
|
|
+ use \Juju\{Data\SecureString, ORM\Relationship, PDO};
|
|
|
|
|
|
abstract class ORM implements \ArrayAccess, \JsonSerializable {
|
|
|
|
|
@@ -19,7 +19,8 @@
|
|
|
private $_related = [];
|
|
|
private static $aliases = [];
|
|
|
private static $instances = [];
|
|
|
- private static $sql;
|
|
|
+ private static $pdo;
|
|
|
+ private static $table;
|
|
|
|
|
|
private function __construct($idOrData){
|
|
|
if(!isset(self::$aliases[$this->name])){
|
|
@@ -167,22 +168,32 @@
|
|
|
public static function foreign_key_suffix(){
|
|
|
return static::$foreign_key_suffix;
|
|
|
}
|
|
|
- public static function bind($sql){
|
|
|
- if(is_string($sql)){
|
|
|
- $sql = SQL::FromDSN($sql);
|
|
|
+ public static function bind($pdo){
|
|
|
+ if(is_string($pdo)){
|
|
|
+ $pdo = PDO::from($pdo);
|
|
|
}
|
|
|
- if($sql instanceof SecureString){
|
|
|
- $sql = SQL::FromDSN((string)$sql);
|
|
|
+ if($pdo instanceof SecureString){
|
|
|
+ $pdo = PDO::from((string)$pdo);
|
|
|
}
|
|
|
- if($sql instanceof SQL){
|
|
|
- self::$sql = $sql;
|
|
|
+ if($pdo instanceof PDO){
|
|
|
+ self::$pdo = $pdo;
|
|
|
+ self::$table = $pdo->table(static::table_name());
|
|
|
|
|
|
}else{
|
|
|
- throw new \Exception("Invalid argument. Must pass a DSN string or an SQL instance");
|
|
|
+ throw new \Exception("Invalid argument. Must pass a DSN string or a PDO instance");
|
|
|
}
|
|
|
}
|
|
|
public static function query(...$args){
|
|
|
- return self::$sql->query(...$args);
|
|
|
+ return self::$pdo->query(...$args);
|
|
|
+ }
|
|
|
+ public static function exec(...$args){
|
|
|
+ return self::$pdo->exec(...$args);
|
|
|
+ }
|
|
|
+ public static function prepare(...$args){
|
|
|
+ return self::$pdo->prepare(...$args);
|
|
|
+ }
|
|
|
+ public static function quote(...$args){
|
|
|
+ return self::$pdo->quote(...$args);
|
|
|
}
|
|
|
public static function instance(int $id){
|
|
|
$instance = self::cached_instance($id);
|
|
@@ -194,13 +205,14 @@
|
|
|
return null;
|
|
|
}
|
|
|
public static function exists(int $id){
|
|
|
- return (int)self::query(
|
|
|
+ $query = self::query(
|
|
|
"select count(1) as count ".
|
|
|
- "from ".static::table_name().' '.
|
|
|
- "where ".static::primary_key()." = ?",
|
|
|
- 'i',
|
|
|
- $id
|
|
|
- )->assoc_result["count"] > 0;
|
|
|
+ "from `".static::table_name().'` '.
|
|
|
+ "where `".static::primary_key()."` = ".self::quote($id)
|
|
|
+ );
|
|
|
+ $count = $query->fetch()['count'];
|
|
|
+ $query->closeCursor();
|
|
|
+ return (int)$count > 0;
|
|
|
}
|
|
|
public static function cached_instance(int $id){
|
|
|
$name = static::table_name();
|
|
@@ -216,13 +228,9 @@
|
|
|
return !is_null(self::cached_instance($id));
|
|
|
}
|
|
|
public static function delete(int $id){
|
|
|
- $query = self::query(
|
|
|
- "delete from ".static::table_name().' '.
|
|
|
- "where ".static::primary_key()." = ?",
|
|
|
- 'i',
|
|
|
- $id
|
|
|
- );
|
|
|
- return $query->execute() && $query->affected_rows > 0;
|
|
|
+ return self::$table->delete([
|
|
|
+ static::primary_key() => $id
|
|
|
+ ]) > 0;
|
|
|
}
|
|
|
public static function each_cached(callable $fn){
|
|
|
$name = static::table_name();
|
|
@@ -231,41 +239,19 @@
|
|
|
}
|
|
|
}
|
|
|
public static function each_where(callable $fn, array $filter = null, int $start = null, int $amount = null){
|
|
|
- $limit = ' ';
|
|
|
+ $limit = '';
|
|
|
if(!is_null($start) && !is_null($amount)){
|
|
|
- $limit .= "limit {$start}, {$amount}";
|
|
|
+ $limit .= " limit {$start}, {$amount}";
|
|
|
}
|
|
|
- $where = ' ';
|
|
|
- $types = null;
|
|
|
- $bindings = null;
|
|
|
- if(!is_null($filter)){
|
|
|
- $types = '';
|
|
|
- $bindings = array();
|
|
|
- foreach($filter as $key => $val){
|
|
|
- if(is_string($val)){
|
|
|
- $types .= 's';
|
|
|
- }elseif(is_double($val)){
|
|
|
- $types .= 'd';
|
|
|
- }elseif(is_int($val)){
|
|
|
- $types .= 'i';
|
|
|
- }else{
|
|
|
- throw new \Exception("Unknown data type");
|
|
|
- }
|
|
|
- $where .= 'and {$key} = ? ';
|
|
|
- $bindings[] = $val;
|
|
|
- }
|
|
|
- $where = self::str_replace_first(' and ', ' ', $where);
|
|
|
- }
|
|
|
- self::query(
|
|
|
+ $query = self::query(
|
|
|
"select ".static::primary_key().' id '.
|
|
|
- "from ".static::table_name().
|
|
|
- $where.
|
|
|
- $limit,
|
|
|
- $types,
|
|
|
- $bindings
|
|
|
- )->each_assoc(function($row) use($fn){
|
|
|
+ "from ".static::table_name().' '.
|
|
|
+ self::$table->stringFilter($filter).
|
|
|
+ $limit
|
|
|
+ );
|
|
|
+ foreach($query->fetchAll() as $row){
|
|
|
$fn(self::instance((int)$row['id']));
|
|
|
- });
|
|
|
+ }
|
|
|
}
|
|
|
public static function each(callable $fn, int $start = null, int $amount = null){
|
|
|
self::each_where($fn, null, $start, $amount);
|
|
@@ -300,13 +286,13 @@
|
|
|
throw new \Exception('Cannot load, there are pending changes');
|
|
|
}else{
|
|
|
if(!is_null($this->id)){
|
|
|
- $data = self::query(
|
|
|
+ $query = self::query(
|
|
|
"select * " .
|
|
|
"from {$this->name} ".
|
|
|
- "where ".static::primary_key()." = ?",
|
|
|
- 'i',
|
|
|
- $this->id
|
|
|
- )->assoc_result;
|
|
|
+ "where ".static::primary_key()." = ".self::quote($this->id)
|
|
|
+ );
|
|
|
+ $data = $query->fetch();
|
|
|
+ $query->closeCursor();
|
|
|
if($data === false){
|
|
|
throw new \Exception("{$this->name} with ".static::primary_key()." of {$this->id} does not exist");
|
|
|
}
|
|
@@ -318,39 +304,20 @@
|
|
|
public function save(){
|
|
|
if($this->dirty()){
|
|
|
$data = [];
|
|
|
- $set = "set ";
|
|
|
- $types = '';
|
|
|
foreach($this->_changed as $key){
|
|
|
if(isset($this->_data[$key]) || is_null($this->_data[$key])){
|
|
|
$data[$key] = $this->_data[$key];
|
|
|
}else{
|
|
|
- $set .= "{$key} = null";
|
|
|
+ $data[$key] = null;
|
|
|
}
|
|
|
}
|
|
|
- foreach($data as $key => $val){
|
|
|
- if(is_string($val)){
|
|
|
- $types .= 's';
|
|
|
- }elseif(is_double($val)){
|
|
|
- $types .= 'd';
|
|
|
- }elseif(is_int($val)){
|
|
|
- $types .= 'i';
|
|
|
- }else{
|
|
|
- throw new \Exception('Unknown data type');
|
|
|
- }
|
|
|
- $set .= "{$key} = ? ";
|
|
|
- }
|
|
|
if(!is_null($this->id) && !in_array(static::primary_key(), $this->_changed)){
|
|
|
- $data = array_merge(array_values($data), [$this->id]);
|
|
|
- self::query(
|
|
|
- "update {$this->name} {$set} where ".static::primary_key()." = ?",
|
|
|
- $types.'i',
|
|
|
- $data
|
|
|
- )->execute();
|
|
|
+ self::$table->update($data, [
|
|
|
+ static::primary_key() => $this->id
|
|
|
+ ]);
|
|
|
}else{
|
|
|
- self::query(
|
|
|
- "insert {$this->name} {$set}"
|
|
|
- )->execute();
|
|
|
- $this->_data[static::primary_key()] = self::$sql->insert_id;
|
|
|
+ self::$table->insert($data);
|
|
|
+ $this->_data[static::primary_key()] = self::$pdo->lastInsertId();
|
|
|
}
|
|
|
foreach($this->_related as $related){
|
|
|
$related->save();
|
|
@@ -414,15 +381,13 @@
|
|
|
}else{
|
|
|
$sql .= $class::primary_key()." id from {$alias['model']} ";
|
|
|
}
|
|
|
- $sql .= "where ".$alias['foreign_key']." = ?";
|
|
|
+ $sql .= "where ".$alias['foreign_key']." = ".self::quote($this->id);
|
|
|
$related = [];
|
|
|
- self::query(
|
|
|
- $sql,
|
|
|
- 'i',
|
|
|
- $this->id
|
|
|
- )->each_assoc(function($row) use(&$related, $class){
|
|
|
+ $query = self::query($sql);
|
|
|
+ foreach($query->fetchAll() as $row){
|
|
|
$related[] = new $class($row['id']);
|
|
|
- });
|
|
|
+ }
|
|
|
+ $query->closeCursor();
|
|
|
$this->_related[$name] = Relationship::from($this, $name, $alias, $related);
|
|
|
}
|
|
|
}
|