diff --git a/MysqliDb.php b/MysqliDb.php index 85a97500..fc0f68ae 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -264,7 +264,7 @@ public function rawQuery ($query, $bindParams = null, $sanitize = true) /** * * @param string $query Contains a user-provided select query. - * @param int $numRows The number of rows total to return. + * @param integer|array $numRows Array to define SQL limit in format Array ($count, $offset) * * @return array Contains the returned rows from the query. */ @@ -321,7 +321,8 @@ public function withTotalCount () { * A convenient SELECT * function. * * @param string $tableName The name of the database table to work with. - * @param integer $numRows The number of rows total to return. + * @param integer|array $numRows Array to define SQL limit in format Array ($count, $offset) + * or only $count * * @return array Contains the returned rows from the select query. */ @@ -453,7 +454,8 @@ public function update($tableName, $tableData) * Delete query. Call the "where" method first. * * @param string $tableName The name of the database table to work with. - * @param integer $numRows The number of rows to delete. + * @param integer|array $numRows Array to define SQL limit in format Array ($count, $offset) + * or only $count * * @return boolean Indicates success. 0 or 1. */ @@ -694,7 +696,8 @@ protected function _buildPair ($operator, $value) { * any passed update data, and the desired rows. * It then builds the SQL query. * - * @param int $numRows The number of rows total to return. + * @param integer|array $numRows Array to define SQL limit in format Array ($count, $offset) + * or only $count * @param array $tableData Should contain an array of data for updating the database. * * @return mysqli_stmt Returns the $stmt object. @@ -937,7 +940,6 @@ protected function _buildGroupBy () { /** * Abstraction method that will build the LIMIT part of the WHERE statement * - * @param int $numRows The number of rows total to return. */ protected function _buildOrderBy () { if (empty ($this->_orderBy)) @@ -957,7 +959,8 @@ protected function _buildOrderBy () { /** * Abstraction method that will build the LIMIT part of the WHERE statement * - * @param int $numRows The number of rows total to return. + * @param integer|array $numRows Array to define SQL limit in format Array ($count, $offset) + * or only $count */ protected function _buildLimit ($numRows) { if (!isset ($numRows)) diff --git a/dbObject.md b/dbObject.md new file mode 100644 index 00000000..c5af81fe --- /dev/null +++ b/dbObject.md @@ -0,0 +1,225 @@ +dbObject - model implementation on top of the MysqliDb +Please note, that this library is not pretending to be a full stack ORM but a simple OOP wrapper for mysqlidb + +
+###Initialization + +1. Include mysqlidb and dbObject classes. + +2. If you want to use model autoloading instead of manually including them in the scripts use autoload () method. + +```php +require_once ("libs/MysqliDb.php"); +require_once ("libs/dbObject.php"); + +// db instance +$db = new Mysqlidb ('localhost', 'user', '', 'testdb'); +// enable class autoloading +dbObject::autoload ("models"); +``` + +3. Create simple user class (models/user.php): + +```php +class user extends dbObject { + protected $dbTable = "users"; + protected $primaryKey = "id"; + protected $dbFields = Array ( + 'login' => Array ('text', 'required'), + 'password' => Array ('text'), + 'createdAt' => Array ('datetime'), + 'updatedAt' => Array ('datetime'), + ); +} +``` +###Insert Row +1. OOP Way. Just create new object of a needed class, fill it in and call save () method. Save will return +record id in case of success and false in case if insert will fail. +```php +$user = new user; +$user->login = 'demo'; +$user->password = 'demo'; +$id = $user->save (); +if ($id) + echo "user created with id = " . $id; +``` + +2. Using arrays +```php +$data = Array ('login' => 'demo', + 'password' => 'demo'); +$user = new user ($data); +$id = $user->save (); +if ($id == null) { + print_r ($user->errors); + echo $db->getLastError; +} else + echo "user created with id = " . $id; +``` + +3. Multisave + +```php +$user = new user; +$user->login = 'demo'; +$user->pass = 'demo'; + +$p = new product; +$p->title = "Apples"; +$p->price = 0.5; +$p->seller = $user; +$p->save (); +``` + +After save() is call both new objects (user and product) will be saved. + +###Selects + +Retrieving objects from the database is pretty much the same process of a get ()/getOne () execution without a need to specify table name. + +All mysqlidb functions like where(), orWhere(), orderBy(), join etc are supported. +Please note that objects returned with join() will not save changes to a joined properties. For this you can use relationships. + +Select row by primary key + +```php +$user = user::byId (1); +echo $user->login; +``` + +Get all users +```php +$users = user::orderBy ('id')->get (); +foreach (users as $u) { + echo $u->login; +} +``` + +Using where with limit +```php +$users = user::where ("login", "demo")->get (Array (10, 20)); +foreach (users as $u) ... +``` + +###Update +To update model properties just set them and call save () method. As well values that needed to by changed could be passed as an array to the save () method. + +```php +$user = user::byId (1); +$user->password = 'demo2'; +$user->save (); +``` +```php +$data = Array ('password', 'demo2'); +$user = user::byId (1); +$user->save ($data); +``` + +###Delete +Use delete() method on any loaded object. +```php +$user = user::byId (1); +$user->delete (); +``` + +###Relations +Currently dbObject supports only hasMany and hasOne relations. To use them declare $relations array in the model class. +After that you can get related object via variable names defined as keys. + +HasOne example: +```php + protected $relations = Array ( + 'person' => Array ("hasOne", "person", 'id'); + ); + + ... + + $user = user::byId (1); + // sql: select * from $persontable where id = $personValue + echo $user->person->firstName . " " . $user->person->lastName . " have the following products:\n"; +``` + +In HasMany Array should be defined target object name (product in example) and a relation key (userid). + +HasMany example: +```php + protected $relations = Array ( + 'products' => Array ("hasMany", "product", 'userid') + ); + + ... + + $user = user::byId (1); + // sql: select * from $product_table where userid = $userPrimaryKey + foreach ($user->products as $p) { + echo $p->title; + } +``` +###Timestamps +Library provides a transparent way to set timestamps of an object creation and its modification: +To enable that define $timestamps array as follows: +```php +protected $timestamps = Array ('createdAt', 'updatedAt'); +``` +Field names cant be changed. + +###Validation and Error checking +Before saving and updating the row dbObject do input validation. In case validation rules are set but their criteria is not met +then save() will return an error with its description. For example: +```php +$id = $user->save(); +if (!$id) { + // show all validation errors + print_r ($user->errors); + echo $db->getLastQuery(); + echo $db->getLastError(); +} +echo "user were created with id" . $id; +``` +Validation rules must be defined in $dbFields array. +```php + protected $dbFields = Array ( + 'login' => Array ('text', 'required'), + 'password' => Array ('text'), + 'createdAt' => Array ('datetime'), + 'updatedAt' => Array ('datetime'), + 'custom' => Array ('/^test/'), + ); +``` +First parameter is a field type. Types could be the one of following: text, bool, int, datetime or a custom regexp. +Second parameter is 'required' and its defines that following entry field be always defined. + +###Array as return values +dbObject can return its data as array instead of object. To do that ArrayBuilder() function should be used in the beginning of the call. +```php + $user = user::ArrayBuilder()->byId (1); + echo $user['login']; + + $users = user::ArrayBuilder()->orderBy ("id", "desc")->get (); + foreach ($users as $u) + echo $u['login']; +``` + +Following call will return data only of the called instance without any relations data. Use with() function to include relation data as well. + +```php + $user = user::ArrayBuilder()->with ("product")->byId (1); + print_r ($user['products']); +``` +###Object serialization + +Object could be easily converted to a json string or an array. + +```php + $user = user::byId (1); + // echo will display json representation of an object + echo $user; + // userJson will contain json representation of an object + $userJson = $user->toJson (); + // userArray will contain array representation of an object + $userArray = $user->toArray (); +``` + +###Examples + +Please look for a use examples in tests/dbObjectTests.php file and test models inside the tests/models/ directory diff --git a/dbObject.php b/dbObject.php new file mode 100644 index 00000000..e1ca50cb --- /dev/null +++ b/dbObject.php @@ -0,0 +1,578 @@ + + * @copyright Copyright (c) 2015 + * @license http://opensource.org/licenses/gpl-3.0.html GNU Public License + * @version 2.0 + * + * @method int count () + * @method mixed byId (string $id, mixed $fields) + * @method mixed get (mixed $limit, mixed $fields) + * @method mixed getOne (mixed $fields) + * @method mixed paginate (int $page, array $fields) + * @method dbObject query ($query, $numRows) + * @method dbObject rawQuery ($query, $bindParams, $sanitize) + * @method dbObject join (string $objectName, string $key, string $joinType) + * @method dbObject with (string $objectName) + * @method dbObject groupBy (string $groupByField) + * @method dbObject orderBy ($orderByField, $orderbyDirection, $customFields) + * @method dbObject where ($whereProp, $whereValue, $operator) + * @method dbObject orWhere ($whereProp, $whereValue, $operator) + * @method dbObject setQueryOption ($options) + * @method dbObject setTrace ($enabled, $stripPrefix) + * @method dbObject withTotalCount () + * @method dbObject startTransaction () + * @method dbObject commit () + * @method dbObject rollback () + * @method dbObject ping () + * @method string getLastError () + * @method string getLastQuery () + **/ +class dbObject { + /** + * Working instance of MysqliDb created earlier + * + * @var MysqliDb + */ + private $db; + /** + * Models path + * + * @var modelPath + */ + private static $modelPath; + /** + * An array that holds object data + * + * @var array + */ + public $data; + /** + * Flag to define is object is new or loaded from database + * + * @var boolean + */ + public $isNew = true; + /** + * Return type: 'Array' to return results as array, 'Object' as object + * + * @var string + */ + public $returnType = 'Object'; + /** + * An array that holds has* objects which should be loaded togeather with main + * object togeather with main object + * + * @var string + */ + private $_with = Array(); + /** + * Per page limit for pagination + * + * @var int + */ + public $pageLimit = 20; + /** + * Variable that holds total pages count of last paginate() query + * + * @var int + */ + public $totalPages = 0; + /** + * An array that holds insert/update/select errors + * + * @var array + */ + public $errors = null; + + /** + * @param array $data Data to preload on object creation + */ + public function __construct ($data = null) { + $this->db = MysqliDb::getInstance(); + if ($data) + $this->data = $data; + } + + /** + * Magic setter function + * + * @return mixed + */ + public function __set ($name, $value) { + $this->data[$name] = $value; + } + + /** + * Magic getter function + * + * @param $name Variable name + * + * @return mixed + */ + public function __get ($name) { + if (property_exists ($this, 'relations') && isset ($this->relations[$name])) { + $relationType = strtolower ($this->relations[$name][0]); + $modelName = $this->relations[$name][1]; + switch ($relationType) { + case 'hasone': + $obj = new $modelName; + $obj->returnType = $this->returnType; + return $obj->byId($this->data[$name]); + break; + case 'hasmany': + $key = $this->relations[$name][2]; + $obj = new $modelName; + $obj->returnType = $this->returnType; + return $obj->where($key, $this->data[$this->primaryKey])->get(); + break; + default: + break; + } + } + + if (isset ($this->data[$name])) { + return $this->data[$name]; + } + + if (property_exists ($this->db, $name)) + return $this->db->$name; + } + + public function __isset ($name) { + if (isset ($this->data[$name])) + return isset ($this->data[$name]); + + if (property_exists ($this->db, $name)) + return isset ($this->db->$name); + } + + public function __unset ($name) { + unset ($this->data[$name]); + } + + + /** + * Helper function to create dbObject with Array return type + * + * @return dbObject + */ + public static function ArrayBuilder () { + $obj = new static; + $obj->returnType = 'Array'; + return $obj; + } + + /** + * Helper function to create dbObject with Object return type. + * Added for consistency. Works same way as new $objname () + * + * @return dbObject + */ + public static function ObjectBuilder () { + $obj = new static; + return $obj; + } + + /** + * @return mixed insert id or false in case of failure + */ + public function insert () { + if (!empty ($this->timestamps) && in_array ("createdAt", $this->timestamps)) + $this->createdAt = date("Y-m-d H:i:s"); + $sqlData = $this->prepareData (); + if (!$this->validate ($sqlData)) + return false; + + $id = $this->db->insert ($this->dbTable, $sqlData); + if (!empty ($this->primaryKey)) + $this->data[$this->primaryKey] = $id; + $this->isNew = false; + + return $id; + } + + /** + * @param array $data Optional update data to apply to the object + */ + public function update ($data = null) { + if (empty ($this->dbFields)) + return false; + + if (empty ($this->data[$this->primaryKey])) + return false; + + if ($data) { + foreach ($data as $k => $v) + $this->$k = $v; + } + + if (!empty ($this->timestamps) && in_array ("updatedAt", $this->timestamps)) + $this->updatedAt = date("Y-m-d H:i:s"); + + $sqlData = $this->prepareData (); + if (!$this->validate ($sqlData)) + return false; + + $this->db->where ($this->primaryKey, $this->data[$this->primaryKey]); + return $this->db->update ($this->dbTable, $sqlData); + } + + /** + * Save or Update object + * + * @return mixed insert id or false in case of failure + */ + public function save ($data = null) { + if ($this->isNew) + return $this->insert(); + return $this->update($data); + } + + /** + * Delete method. Works only if object primaryKey is defined + * + * @return boolean Indicates success. 0 or 1. + */ + public function delete () { + if (empty ($this->data[$this->primaryKey])) + return false; + + $this->db->where ($this->primaryKey, $this->data[$this->primaryKey]); + return $this->db->delete ($this->dbTable); + } + + /** + * Get object by primary key. + * + * @access public + * @param $id Primary Key + * @param array|string $fields Array or coma separated list of fields to fetch + * + * @return dbObject|array + */ + private function byId ($id, $fields = null) { + $this->db->where ($this->dbTable . '.' . $this->primaryKey, $id); + return $this->getOne ($fields); + } + + /** + * Convinient function to fetch one object. Mostly will be togeather with where() + * + * @access public + * @param array|string $fields Array or coma separated list of fields to fetch + * + * @return dbObject + */ + private function getOne ($fields = null) { + $results = $this->db->getOne ($this->dbTable, $fields); + $this->processArrays ($results); + $this->processWith ($results); + if ($this->returnType == 'Array') + return $results; + + $item = new static ($results); + $item->isNew = false; + + return $item; + } + + /** + * Fetch all objects + * + * @access public + * @param integer|array $limit Array to define SQL limit in format Array ($count, $offset) + or only $count + * @param array|string $fields Array or coma separated list of fields to fetch + * + * @return array Array of dbObjects + */ + private function get ($limit = null, $fields = null) { + $objects = Array (); + $results = $this->db->get ($this->dbTable, $limit, $fields); + foreach ($results as &$r) { + $this->processArrays ($r); + $this->processWith ($r); + if ($this->returnType == 'Object') { + $item = new static ($r); + $item->isNew = false; + $objects[] = $item; + } + } + if ($this->returnType == 'Object') + return $objects; + + return $results; + } + + /** + * Function to set witch hasOne or hasMany objects should be loaded togeather with a main object + * + * @access public + * @param string $objectName Object Name + * + * @return dbObject + */ + private function with ($objectName) { + $this->_with[] = $objectName; + + return $this; + } + + /** + * Function to join object with another object. + * + * @access public + * @param string $objectName Object Name + * @param string $key Key for a join from primary object + * @param string $joinType SQL join type: LEFT, RIGHT, INNER, OUTER + * + * @return dbObject + */ + private function join ($objectName, $key = null, $joinType = 'LEFT') { + $joinObj = new $objectName; + if (!$key) + $key = $objectName . "id"; + $joinStr = "{$this->dbTable}.{$key} = {$joinObj->dbTable}.{$joinObj->primaryKey}"; + $this->db->join ($joinObj->dbTable, $joinStr, $joinType); + return $this; + } + + /** + * Function to get a total records count + * + * @return int + */ + private function count () { + $res = $this->db->getValue ($this->dbTable, "count(*)"); + return $res['cnt']; + } + + /** + * Pagination wraper to get() + * + * @access public + * @param int $page Page number + * @param array|string $fields Array or coma separated list of fields to fetch + * @return array + */ + private function paginate ($page, $fields = null) { + $offset = $this->pageLimit * ($page - 1); + $this->db->withTotalCount(); + $results = $this->get (Array ($this->pageLimit, $offset), $fields); + $this->totalPages = round ($this->db->totalCount / $this->pageLimit); + + return $results; + } + + /** + * Catches calls to undefined methods. + * + * Provides magic access to private functions of the class and native public mysqlidb functions + * + * @param string $method + * @param mixed $arg + * + * @return mixed + */ + public function __call ($method, $arg) { + if (method_exists ($this, $method)) + return call_user_func_array (array ($this, $method), $arg); + + call_user_func_array (array ($this->db, $method), $arg); + return $this; + } + + /** + * Catches calls to undefined static methods. + * + * Transparently creating dbObject class to provide smooth API like name::get() name::orderBy()->get() + * + * @param string $method + * @param mixed $arg + * + * @return mixed + */ + public static function __callStatic ($method, $arg) { + $obj = new static; + $result = call_user_func_array (array ($obj, $method), $arg); + if (method_exists ($obj, $method)) + return $result; + return $obj; + } + + /** + * Converts object data to an associative array. + * + * @return array Converted data + */ + public function toArray () { + $data = $this->data; + $this->processWith ($data); + foreach ($data as &$d) { + if ($d instanceof dbObject) + $d = $d->data; + } + return $data; + } + + /** + * Converts object data to a JSON string. + * + * @return string Converted data + */ + public function toJson () { + return json_encode ($this->toArray()); + } + + /** + * Converts object data to a JSON string. + * + * @return string Converted data + */ + public function __toString () { + return $this->toJson (); + } + + /** + * @param array $data + */ + private function processWith (&$data) { + if (count ($this->_with) == 0) + return; + foreach ($this->_with as $w) + $data[$w] = $this->$w; + $this->_with = Array(); + } + + /** + * @param array $data + */ + private function processArrays (&$data) { + if (isset ($this->jsonFields) && is_array ($this->jsonFields)) { + foreach ($this->jsonFields as $key) + $data[$key] = json_decode ($data[$key]); + } + + if (isset ($this->arrayFields) && is_array($this->arrayFields)) { + foreach ($this->arrayFields as $key) + $data[$key] = explode ("|", $data[$key]); + } + } + + /** + * @param array $data + */ + private function validate ($data) { + foreach ($this->dbFields as $key => $desc) { + $type = null; + $required = false; + if (isset ($data[$key])) + $value = $data[$key]; + else + $value = null; + + if (is_array ($value)) + continue; + + if (isset ($desc[0])) + $type = $desc[0]; + if (isset ($desc[1]) && ($desc[1] == 'required')) + $required = true; + + if ($required && strlen ($value) == 0) { + $this->errors[] = Array ($this->dbTable . "." . $key => "is required"); + continue; + } + if ($value == null) + continue; + + switch ($type) { + case "text"; + $regexp = null; + break; + case "int": + $regexp = "/^[0-9]*$/"; + break; + case "bool": + $regexp = '/^[yes|no|0|1|true|false]$/i'; + break; + case "datetime": + $regexp = "/^[0-9a-zA-Z -:]*$/"; + break; + default: + $regexp = $type; + break; + } + if (!$regexp) + continue; + + if (!preg_match ($regexp, $value)) { + $this->errors[] = Array ($this->dbTable . "." . $key => "$type validation failed"); + continue; + } + } + return !count ($this->errors) > 0; + } + + private function prepareData () { + $this->errors = Array (); + $sqlData = Array(); + if (count ($this->data) == 0) + return Array(); + + if (method_exists ($this, "preLoad")) + $this->preLoad ($data); + + foreach ($this->data as $key => &$value) { + if ($value instanceof dbObject && $value->isNew == true) { + $id = $value->save(); + if ($id) + $value = $id; + else + $this->errors = array_merge ($this->errors, $value->errors); + } + + if (!in_array ($key, array_keys ($this->dbFields))) + continue; + + if (!is_array($value)) { + $sqlData[$key] = $value; + continue; + } + + if (isset ($this->jsonFields) && in_array ($key, $this->jsonFields)) + $sqlData[$key] = json_encode($value); + else if (isset ($this->arrayFields) && in_array ($key, $this->arrayFields)) + $sqlData[$key] = implode ("|", $value); + else + $sqlData[$key] = $value; + } + return $sqlData; + } + + private static function dbObjectAutoload ($classname) { + $filename = "models/". $classname .".php"; + include ($filename); + } + + /* + * Enable models autoload from a specified path + * + * Calling autoload() without path will set path to dbObjectPath/models/ directory + * + * @param string $path + */ + public static function autoload ($path = null) { + if ($path) + static::$modelPath = $path . "/"; + else + static::$modelPath = __DIR__ . "/models/"; + spl_autoload_register ("dbObject::dbObjectAutoload"); + } +} +?> diff --git a/tests/dbObjectTests.php b/tests/dbObjectTests.php new file mode 100644 index 00000000..fbf257cc --- /dev/null +++ b/tests/dbObjectTests.php @@ -0,0 +1,258 @@ + Array ( + 'login' => 'char(10) not null', + 'active' => 'bool default 0', + 'customerId' => 'int(10) not null', + 'firstName' => 'char(10) not null', + 'lastName' => 'char(10)', + 'password' => 'text not null', + 'createdAt' => 'datetime', + 'updatedAt' => 'datetime', + 'expires' => 'datetime', + 'loginCount' => 'int(10) default 0' + ), + 'products' => Array ( + 'customerId' => 'int(10) not null', + 'userId' => 'int(10) not null', + 'productName' => 'char(50)' + ) +); + +$data = Array ( + 'user' => Array ( + Array ('login' => 'user1', + 'customerId' => 10, + 'firstName' => 'John', + 'lastName' => 'Doe', + 'password' => $db->func('SHA1(?)',Array ("secretpassword+salt")), + 'expires' => $db->now('+1Y'), + 'loginCount' => $db->inc() + ), + Array ('login' => 'user2', + 'customerId' => 10, + 'firstName' => 'Mike', + 'lastName' => NULL, + 'password' => $db->func('SHA1(?)',Array ("secretpassword2+salt")), + 'expires' => $db->now('+1Y'), + 'loginCount' => $db->inc(2) + ), + Array ('login' => 'user3', + 'active' => true, + 'customerId' => 11, + 'firstName' => 'Pete', + 'lastName' => 'D', + 'password' => $db->func('SHA1(?)',Array ("secretpassword2+salt")), + 'expires' => $db->now('+1Y'), + 'loginCount' => $db->inc(3) + ) + ), + 'product' => Array ( + Array ('customerId' => 1, + 'userId' => 1, + 'productName' => 'product1', + ), + Array ('customerId' => 1, + 'userId' => 1, + 'productName' => 'product2', + ), + Array ('customerId' => 1, + 'userId' => 1, + 'productName' => 'product3', + ), + Array ('customerId' => 1, + 'userId' => 2, + 'productName' => 'product4', + ), + Array ('customerId' => 1, + 'userId' => 2, + 'productName' => 'product5', + ), + + ) +); +function createTable ($name, $data) { + global $db; + //$q = "CREATE TABLE $name (id INT(9) UNSIGNED PRIMARY KEY NOT NULL"; + $q = "CREATE TABLE $name (id INT(9) UNSIGNED PRIMARY KEY AUTO_INCREMENT"; + foreach ($data as $k => $v) { + $q .= ", $k $v"; + } + $q .= ")"; + $db->rawQuery($q); +} + +// rawQuery test +foreach ($tables as $name => $fields) { + $db->rawQuery("DROP TABLE " . $name); + createTable ($name, $fields); +} + +foreach ($data as $name => $datas) { + foreach ($data[$name] as $userData) { + $obj = new $name ($userData); + $id = $obj->save(); + if ($obj->errors) { + echo "errors:"; + print_r ($obj->errors); + exit; + } + } +} + +$products = product::ArrayBuilder()->get(2); +foreach ($products as $p) { + if (!is_array ($p)) { + echo "ArrayBuilder do not return an array\n"; + exit; + } +} + +$product = product::ArrayBuilder()->with('userId')->byId(5); +if (!is_array ($product['userId'])) { + echo "Error in with processing in getOne"; + exit; +} + +$products = product::ArrayBuilder()->with('userId')->get(2); +if (!is_array ($products[0]['userId'])) { + echo "Error in with processing in get"; + exit; +} + +$depts = product::join('user')->orderBy('products.id', 'desc')->get(5); +foreach ($depts as $d) { + if (!is_object($d)) { + echo "Return should be an object\n"; + exit; + } +} + +$dept = product::join('user')->byId(5); +if (count ($dept->data) != 13) { + echo "wrong props count " .count ($dept->data). "\n"; + exit; +} +if ($db->count != 1) { + echo "wrong count after byId\n"; + exit; +} + +// hasOne +$products = product::get (); +$cnt = 0; +foreach ($products as $p) { + if (get_class ($d) != 'product') { + echo "wrong class returned\n"; + exit; + } + + if (!($p->userId instanceof user)) { + echo "wrong return class of hasOne result\n"; + exit; + } + + $cnt++; +} + +if (($cnt != $db->count) && ($cnt != 5)) { + echo "wrong count after get\n"; + exit; +} + +// hasMany +$user = user::where('id',1)->getOne(); +if (!is_array ($user->products) || (count ($user->products) != 3)) { + echo "wrong count in hasMany\n"; + exit; +} + +foreach ($user->products as $p) { + if (!($p instanceof product)) { + echo "wrong return class of hasMany result\n"; + exit; + } +} + +// multi save +$client = new user; +$client->login = 'testuser'; +$client->firstName = 'john'; +$client->lastName = 'Doe Jr'; + +$obj = new product; +$obj->customerId = 2; +$obj->userId = 2; +$obj->productName = "product6"; +$obj->save(); + +$obj->userId = 5; +$obj->save(); + +$obj->userId = $client; +$obj->save(); +if ($client->errors) { + echo "errors:"; + print_r ($client->errors); + exit; +} + +$expected = '{"customerId":2,"userId":{"id":4,"login":"testuser","active":0,"customerId":0,"firstName":"john","lastName":"Doe Jr","password":"","createdAt":"' .$client->createdAt. '","updatedAt":null,"expires":null,"loginCount":0},"productName":"product6","id":6}'; + +if ($obj->with('userId')->toJson() != $expected) { + echo "Multisave problem\n"; + echo $obj->with('userId')->toJson(); + exit; +} + +$u= new user; +$u->active='test'; +$u->customerId = 'test'; +$u->expires = 'test;'; +$u->firstName = 'test'; + +$obj = new product; +$obj->userId = $u; +$obj->save(); +if ($obj->save()) { + echo "validation 1 failed\n"; + exit; +} +if (count ($obj->errors) != 7) { + print_r ($obj->errors); + echo "validation 2 failed\n"; + exit; +} + +if (!user::byId(1) instanceof user) + echo "wrong return type1"; + +if (!is_array (user::ArrayBuilder()->byId(1))) + echo "wrong return type2"; + +if (!is_array (product::join('user')->orderBy('products.id', 'desc')->get(2))) + echo "wrong return type2"; + +if (!is_array (product::orderBy('products.id', 'desc')->join('user')->get(2))) + echo "wrong return type2"; + +$u = new user; +if (!$u->byId(1) instanceof user) + echo "wrong return type2"; + +$p = new product; +if (!is_array ($p->join('user')->orderBy('products.id', 'desc')->get(2))) + echo "wrong return type2"; + +if (!is_array ($p->orderBy('products.id', 'desc')->join('user')->get(2))) + echo "wrong return type2"; + +echo "All done"; +?> diff --git a/tests/models/product.php b/tests/models/product.php new file mode 100644 index 00000000..7e0033c9 --- /dev/null +++ b/tests/models/product.php @@ -0,0 +1,29 @@ + Array('int', 'required'), + 'customerId' => Array ('int', 'required'), + 'productName' => Array ('text','required') + ); + protected $relations = Array ( + 'userId' => Array ("hasOne", "user") + ); + + public function last () { + $this->where ("id" , 130, '>'); + return $this; + } +} + + +?> diff --git a/tests/models/user.php b/tests/models/user.php new file mode 100644 index 00000000..6929e2fa --- /dev/null +++ b/tests/models/user.php @@ -0,0 +1,40 @@ + Array ('text', 'required'), + 'active' => Array ('bool'), + 'customerId' => Array ('int'), + 'firstName' => Array ('/[a-zA-Z0-9 ]+/'), + 'lastName' => Array ('text'), + 'password' => Array ('text'), + 'createdAt' => Array ('datetime'), + 'updatedAt' => Array ('datetime'), + 'expires' => Array ('datetime'), + 'loginCount' => Array ('int') + ); + + protected $timestamps = Array ('createdAt', 'updatedAt'); + protected $relations = Array ( + 'products' => Array ("hasMany", "product", 'userid') + ); +} + + +?> diff --git a/tests.php b/tests/mysqliDbTests.php similarity index 99% rename from tests.php rename to tests/mysqliDbTests.php index d6ba3c94..852877b2 100644 --- a/tests.php +++ b/tests/mysqliDbTests.php @@ -1,5 +1,5 @@ delete("products"); -echo "All done"; //print_r($db->rawQuery("CALL simpleproc(?)",Array("test"))); print_r ($db->trace); +echo "All done"; ?>