From aab34844eee4829a070cc7a9e84fe43c4a70153b Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Wed, 6 May 2015 21:25:57 +0200 Subject: [PATCH 1/2] Implemented renewed querybuilder and a better databasemodel. Database utilities can from now on be added to this module. Fixes #46 --- Application/Config/config.database.php | 1 + Application/Models/model.example.php | 3 +- Core/System/Models/model.interpret.php | 10 +- Core/System/class.core.php | 4 + Modules/databasemodel/class.model.php | 376 -------------- Modules/databasemodel/moduleInfo.php | 21 - Modules/databaseutils/class.model.php | 163 ++++++ Modules/databaseutils/class.query.php | 675 +++++++++++++++++++++++++ Modules/databaseutils/moduleInfo.php | 23 + 9 files changed, 868 insertions(+), 408 deletions(-) delete mode 100644 Modules/databasemodel/class.model.php delete mode 100644 Modules/databasemodel/moduleInfo.php create mode 100644 Modules/databaseutils/class.model.php create mode 100644 Modules/databaseutils/class.query.php create mode 100644 Modules/databaseutils/moduleInfo.php diff --git a/Application/Config/config.database.php b/Application/Config/config.database.php index a39e564..8cf8c3c 100644 --- a/Application/Config/config.database.php +++ b/Application/Config/config.database.php @@ -5,4 +5,5 @@ 'username' => '', 'password' => '', 'prefix' => '', + 'debug' => false, ) ; \ No newline at end of file diff --git a/Application/Models/model.example.php b/Application/Models/model.example.php index 3b40f0d..0a2112c 100644 --- a/Application/Models/model.example.php +++ b/Application/Models/model.example.php @@ -8,8 +8,7 @@ class Example extends Model{ public function __construct(&$core){ parent::__construct($core); - $this->setType('techfuze/databasemodel', 'DatabaseModel'); - $this->fields = array('id', 'key', 'value'); + $this->setType('techfuze/databaseutils', 'Model'); $this->table = 'example'; } } diff --git a/Core/System/Models/model.interpret.php b/Core/System/Models/model.interpret.php index dfa41da..14b79fd 100644 --- a/Core/System/Models/model.interpret.php +++ b/Core/System/Models/model.interpret.php @@ -7,21 +7,13 @@ class Interpret extends Model { public function __construct(&$core){ parent::__construct($core); - $this->setType('techfuze/databasemodel', 'DatabaseModel'); - $this->fields = array(); + $this->setType('techfuze/databaseutils', 'Model'); $this->table = ''; } public function table($name) { $this->table = $name; - // Get tables - $sth = $this->mods->database->prepare("DESCRIBE " . $this->table); - $sth->execute(); - $table_fields = $sth->fetchAll(\PDO::FETCH_COLUMN); - - // Append to model - $this->fields = $table_fields; } } diff --git a/Core/System/class.core.php b/Core/System/class.core.php index e8aed64..1226c83 100644 --- a/Core/System/class.core.php +++ b/Core/System/class.core.php @@ -166,7 +166,11 @@ class Core { // Return a reference return $this->mods->{strtolower($cfg->module_name)} = &$CLASS; } + } else { + throw new CoreException("Could not load mod '".$name."'. Invalid Configuration", 1); } + } else { + throw new CoreException("Could not load mod '".$name."'. Not found", 1); } } diff --git a/Modules/databasemodel/class.model.php b/Modules/databasemodel/class.model.php deleted file mode 100644 index d58941d..0000000 --- a/Modules/databasemodel/class.model.php +++ /dev/null @@ -1,376 +0,0 @@ -core); - } - - public function select(){ - - $select = ''; - $where = ''; - $order = ''; - $limit = ''; - $other = ''; - - $args = func_get_args(); - $binds = array(); - - // Only one argument given - if(func_num_args() >= 1){ - // Null: select all fields - if($args[0] === null){ - - $fields = $this->fields; - }else{ - - $fields = $args[0]; - } - - // Convert array to string - if(is_array($fields)){ - - // Slightly faster as implode() - foreach($fields as $index => $field){ - - $select .= ' `'.$field.($index == (count($fields)-1) ? '`' : '`,'); - } - }elseif(is_numeric($fields)){ - - // Build where based on primary key - $select= '*'; - $where = 'WHERE `'.$this->primary.'` = '.$fields; - }else{ - - // Directly use it as $select - $select = $fields; - } - - if(func_num_args() >= 2){ - - // Convert array to string - if($args[1] !== null) - if(is_array($fields)){ - - $where = 'WHERE'; - - // Implode the fields into a string - foreach($args[1] as $field => $value){ - - $token1 = 'AND'; - $token2 = '='; - - // Operator feautures - if(strpos($field, '!') !== false){ - - $token2 = '<>'; - $field = str_replace('!', '', $field); - } - - if(strpos($field, '/') !== false){ - - $token1 = 'OR'; - $field = str_replace('/', '', $field); - } - - if (strpos($field, '>') !== false) { - - $token2 = '>'; - $field = str_replace('>', '', $field); - } - - if (strpos($field, '<') !== false) { - - $token2 = '<'; - $field = str_replace('<', '', $field); - } - - if (strpos($field, '=>') !== false) { - - $token2 = '=>'; - $field = str_replace('=>', '', $field); - } - - if (strpos($field, '<=') !== false) { - - $token2 = '<='; - $field = str_replace('<=', '', $field); - } - - $where .= ($where === 'WHERE' ? '' : $token1).' `'.$field.'` '.$token2.' :'.$field.' '; - $binds[$field] = $value; - } - }else{ - // Directly use it as $select - $where = $args[1]; - } - - if(func_num_args() >= 3){ - - // Order - if($args[2] !== null) - if(is_array($args[2])){ - - $order = 'ORDER BY'; - foreach($args[2] as $index => $field){ - - $mode = 'ASC'; - if(substr($field, 0, 1) === '-'){ - - $field = substr($field, 1); - $mode = 'DESC'; - } - - $order .= ' `'.$field.'` '.$mode.($index == (count($args[2])-1) ? '' : ','); - } - }else{ - - $order = 'ORDER BY '.$args[2]; - } - - if(func_num_args() >= 4){ - - // Limit - if($args[3] !== null) - if(is_array($args[3])){ - - $limit = 'LIMIT '.$args[3][0].','.$args[3][1]; - }elseif($args[3]){ - - $limit = 'LIMIT '.$args[3]; - } - - if(func_num_args() >= 5){ - - // Other - $other = $args[4]; - } - } - } - } - }else{ - - $select = '*'; - } - - $query = 'SELECT '.$select.' FROM `'.$this->table.'` '.$where.' '.$order.' '.$limit.' '.$other; - - try{ - $sth = $this->mods->database->prepare($query); - $sth->execute($binds); - }catch (\PDOException $e){ - - throw new \Exception('Could not execute SQL-query due PDO-exception '.$e->getMessage()); - } - - // Fetch results - $result = $sth->fetchAll(\PDO::FETCH_ASSOC); - - return $result; - } - - public function insert($arr){ - $query = ''; - $fields= ''; - $values= ''; - - $i = 0; - foreach($arr as $index => $value){ - - $i++; - $fields .= '`'.$index.'`'.($i == count($arr) ? '' : ', '); - $values .= ':'.$index.($i == count($arr) ? '' : ', '); - } - - $query = 'INSERT INTO `'.$this->table.'` ('.$fields.')VALUES('.$values.')'; - - try{ - - $sth = $this->mods->database->prepare($query); - $sth->execute($arr); - }catch (\PDOException $e){ - - throw new \Exception('Could not execute SQL-query due PDO-exception '.$e->getMessage()); - } - } - - public function delete(){ - $query = ''; - $where = ''; - $args = func_get_args(); - - // Convert array to string - if(func_num_args() >= 1){ - - // Where - if($args[0] !== null) - if(is_array($args[0])){ - - $where = 'WHERE'; - - // Implode the fields into a string - foreach($args[0] as $field => $value){ - - $token1 = 'AND'; - $token2 = '='; - - if(strpos($field, '!') !== false){ - - $token2 = '<>'; - $field = str_replace('!', '', $field); - } - - if(strpos($field, '/') !== false){ - - $token1 = 'OR'; - $field = str_replace('/', '', $field); - } - - $where .= ($where === 'WHERE' ? '' : $token1).' `'.$field.'` '.$token2.' :'.$field.' '; - $binds[$field] = $value; - } - }elseif(is_numeric($args[0])){ - - // Build where based on primary key - $where = 'WHERE `'.$this->primary.'` = '.$args[0]; - }else{ - - // Directly use it as $select - $where = $args[1]; - } - - if(func_num_args() >= 2){ - - // Limit - if($args[1] !== null) - if(is_array($args[1])){ - - $limit = 'LIMIT '.$args[1][0].','.$args[1][1]; - }elseif($args[1]){ - - $limit = 'LIMIT '.$args[1]; - } - } - } - - $query = 'DELETE FROM `'.$this->table.'` '.$where.' '.$limit; - - try{ - - $sth = $this->mods->database->prepare($query); - $sth->execute($binds); - }catch (\PDOException $e){ - - throw new \Exception('Could not execute SQL-query due PDO-exception '.$e->getMessage()); - } - } - - public function update(){ - $query = ''; - $fields= ''; - $where = ''; - $args = func_get_args(); - - // Convert array to string - if(func_num_args() >= 1){ - - // Update - if($args[0] !== null) - if(is_array($args[0])){ - - // Slightly faster as implode() - $i = 0; - foreach($args[0] as $field => $value){ - - $i ++; - $fields .= ' `'.$field.'` = :insert_'.$field.($i == (count($args[0])) ? '' : ','); - $binds['insert_'.$field] = $value; - } - }else{ - // Directly use it as $select - $fields = $args[0]; - } - - if(func_num_args() >= 2){ - // Where - if($args[1] !== null) - if(is_array($args[1])){ - $where = 'WHERE'; - - // Implode the fields into a string - foreach($args[1] as $field => $value){ - - $token1 = 'AND'; - $token2 = '='; - - if(strpos($field, '!') !== false){ - - $token2 = '<>'; - $field = str_replace('!', '', $field); - } - - if(strpos($field, '/') !== false){ - - $token1 = 'OR'; - $field = str_replace('/', '', $field); - } - - $where .= ($where === 'WHERE' ? '' : $token1).' `'.$field.'` '.$token2.' :'.$field.' '; - $binds[$field] = $value; - } - }elseif(is_numeric($args[1])){ - - // Build where based on primary key - $where = 'WHERE `'.$this->primary.'` = '.$args[1]; - }else{ - - // Directly use it as $select - $where = $args[1]; - } - - if(func_num_args() >= 3){ - - // Limit - if($args[2] !== null) - if(is_array($args[2])){ - - $limit = 'LIMIT '.$args[2][0].','.$args[2][1]; - }elseif($args[2]){ - - $limit = 'LIMIT '.$args[2]; - } - } else { - $limit = ""; - } - } - } - - $query = 'UPDATE `'.$this->table.'` SET '.$fields.' '.$where.' '.$limit; - - try{ - $sth = $this->mods->database->prepare($query); - $sth->execute($binds); - }catch (\PDOException $e){ - throw new \Exception('Could not execute SQL-query due PDO-exception '.$e->getMessage()); - } - } - - public function __call($name, $params) { - return call_user_func_array(array($this->mods->database, $name), $params); - } -} \ No newline at end of file diff --git a/Modules/databasemodel/moduleInfo.php b/Modules/databasemodel/moduleInfo.php deleted file mode 100644 index 81b6c58..0000000 --- a/Modules/databasemodel/moduleInfo.php +++ /dev/null @@ -1,21 +0,0 @@ - 'Module\DatabaseModel', - 'module_file' => 'class.model.php', - 'module_name' => 'databasemodel', - - 'abstract' => false, - 'dependencies' => array('techfuze/database'), - - 'name' => 'DatabaseModel', - 'description' => 'Abstract type for easy database queries', - 'author' => 'TechFuze', - 'version' => '1.0.0', - 'website' => 'http://fuzeworks.techfuze.net/', - - 'date_created' => '26-02-2015', - 'date_updated' => '26-02-2015', - - 'enabled' => true, -); diff --git a/Modules/databaseutils/class.model.php b/Modules/databaseutils/class.model.php new file mode 100644 index 0000000..56fafa5 --- /dev/null +++ b/Modules/databaseutils/class.model.php @@ -0,0 +1,163 @@ +getModulePath() . '/class.query.php'); + } + + public function giveModel($type) { + return new Model($this->core); + } +} + +/** + * Class Model + * + * Models provide an easy connection to database tables. Each table requires its own model. You don't need to worry about your queries or syntax anymore, because + * models will handle queries and error handling. Because of this, you can freely change your database infrastructure without fear of needing to change your + * table names at thousands of places. The model is the only place where you have to change your database names or fields. + * + * Models also allow custom methods to be created on them. You can use those methods to create specific operations or joins and then use the newly created method + * everywhere in your project. The code is at one place, the usages all over your project. Isn't that great? + * + * @package Module\DatabaseUtils + */ +class Model extends Bus { + + /** + * @var string The name of the database table + */ + public $table = ''; + + /** + * Traditional query interface + * + * @param string $query + * @param null $binds + * @return mixed returns fetched rows if available, otherwise returns number of affected rows + * @throws DatabaseException + */ + public function query($query, $binds = null){ + + if($this->config->database->debug) + $this->logger->log("Query: ".$query, "Database Model"); + + try{ + + $sth = $this->mods->database->prepare($query); + if($binds === null){ + + $sth->execute(); + }else{ + + $sth->execute($binds); + } + }catch (\PDOException $e){ + + throw new DatabaseException('Could not execute SQL-query due PDO-exception '.$e->getMessage()); + } + + if($sth->columnCount() > 0){ + + // Fetch results + $result = $sth->fetchAll(\PDO::FETCH_ASSOC); + }else{ + + // Fetch number of affected rows + $result = $sth->rowCount(); + } + + return $result; + } + + /** + * The default table will be set to $this->table + * @see Query::select + * @return Query + */ + public function select(){ + + $queryBuilder = new Query($this->core); + $queryBuilder->setTable($this->table); + call_user_func_array(array($queryBuilder, 'select'), func_get_args()); + $queryBuilder->from(); + + return $queryBuilder; + } + + /** + * The default table will be set to $this->table + * @see Query::update + * @return Query + */ + + public function update(){ + + $queryBuilder = new Query($this->core); + $queryBuilder->setTable($this->table); + call_user_func_array(array($queryBuilder, 'update'), func_get_args()); + + return $queryBuilder; + } + + /** + * The default table will be set to $this->table + * @see Query::delete + * @return Query + */ + + public function delete(){ + + $queryBuilder = new Query($this->core); + $queryBuilder->setTable($this->table); + call_user_func_array(array($queryBuilder, 'delete'), func_get_args()); + + return $queryBuilder; + } + + /** + * The default table will be set to $this->table + * @see Query::insert + * @param $array Array with values + * @return Query + * @throws Exception + */ + + public function insert($array){ + + $queryBuilder = new Query($this->core); + $queryBuilder->setTable($this->table); + call_user_func_array(array($queryBuilder, 'insert'), func_get_args()); + + return $queryBuilder; + } + + /** + * Return latest insert id + * + * @return mixed + */ + public function getLastInsertId(){ + + return $this->mods->database->lastInsertId(); + } + + public function __call($name, $params) { + return call_user_func_array(array($this->mods->database, $name), $params); + } +} \ No newline at end of file diff --git a/Modules/databaseutils/class.query.php b/Modules/databaseutils/class.query.php new file mode 100644 index 0000000..c11363b --- /dev/null +++ b/Modules/databaseutils/class.query.php @@ -0,0 +1,675 @@ +query .= 'SELECT ' . (func_num_args() == 0 ? '*' : '`' . implode("`, `", func_get_args()) . '`'); + + return $this; + } + + /** + * From + * + * @param $table null|string The table, if it is null, the table set with setTable() will be used + * @return $this + * @throws Exception + */ + public function from($table = '') + { + + if($table === ''){ + + if($this->table == '') + throw new Exception("No table given"); + + $table = $this->table; + } + + $this->query .= ' FROM `' . $table . '`'; + + return $this; + } + + /** + * Where + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + public function where($field, $arg2, $arg3 = null){ + + //The value is the second paramter, unless the second parameter is an operator, in which case it's the third + $value = ($arg3 == null ? $arg2 : $arg3); + //If the third parameter is not given, the default operator should be used + $operator = strtoupper($arg3 == null ? "=" : $arg2); + + switch($operator){ + + case 'IN': + + $this->query .= ' WHERE `' . $field .'` IN ('; + + foreach($value as $k => $v){ + + $this->query .= '?,'; + $this->binds[] = $v; + } + + //Remove the trailing comma and close it + $this->query = rtrim($this->query, ",") . ')'; + + + break; + + case 'BETWEEN': + + $this->query .= ' WHERE `' . $field .'` BETWEEN ? AND ?'; + $this->binds[] = $value[0]; + $this->binds[] = $value[1]; + + break; + + default: + + $this->query .= ' WHERE `' . $field .'` ' . $operator . ' ?'; + $this->binds[] = $value; + + break; + } + return $this; + } + + /** + * Where open. Is the start of WHERE(....). To end this call ->close() + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + public function where_open($field, $arg2, $arg3 = null){ + + $old = $this->query; + $this->where($field, $arg2, $arg3); + //Replace the WHERE with WHERE ( + $this->query = $old . ' WHERE (' . substr($this->query, strlen($old) + 7); + + return $this; + } + + /** + * Or, this function should be called after ->where() or ->having(). Please use ->or as an alias instead of ->_or() + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + public function _or($field, $arg2, $arg3 = null){ + + //The value is the second paramter, unless the second parameter is an operator, in which case it's the third + $value = ($arg3 == null ? $arg2 : $arg3); + //If the third parameter is not given, the default operator should be used + $operator = strtoupper($arg3 == null ? "=" : $arg2); + + switch($operator){ + + case 'IN': + + $this->query .= ' OR `' . $field .'` IN ('; + + foreach($value as $k => $v){ + + $this->query .= '?,'; + $this->binds[] = $v; + } + + //Remove the trailing comma and close it + $this->query = rtrim($this->query, ",") . ')'; + + + break; + + case 'BETWEEN': + + $this->query .= ' OR `' . $field .'` BETWEEN ? AND ?'; + $this->binds[] = $value[0]; + $this->binds[] = $value[1]; + + break; + + default: + + $this->query .= ' OR `' . $field .'` ' . $operator . ' ?'; + $this->binds[] = $value; + + break; + } + return $this; + } + + /** + * Or open. Is the start of OR(....). To end this call ->close() + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + public function or_open($field, $arg2, $arg3 = null){ + + $old = $this->query; + $this->_or($field, $arg2, $arg3); + //Replace the OR with OR ( + $this->query = $old . ' OR (' . substr($this->query, strlen($old) + 4); + + return $this; + } + + /** + * And, this function should be called after ->where() or ->having(). Please use ->and as an alias instead of ->_and() + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + public function _and($field, $arg2, $arg3 = null){ + + //The value is the second paramter, unless the second parameter is an operator, in which case it's the third + $value = ($arg3 == null ? $arg2 : $arg3); + //If the third parameter is not given, the default operator should be used + $operator = strtoupper($arg3 == null ? "=" : $arg2); + + switch($operator){ + + case 'IN': + + $this->query .= ' AND `' . $field .'` IN ('; + + foreach($value as $k => $v){ + + $this->query .= '?,'; + $this->binds[] = $v; + } + + //Remove the trailing comma and close it + $this->query = rtrim($this->query, ",") . ')'; + + + break; + + case 'BETWEEN': + + $this->query .= ' AND `' . $field .'` BETWEEN ? AND ?'; + $this->binds[] = $value[0]; + $this->binds[] = $value[1]; + + break; + + default: + + $this->query .= ' AND `' . $field .'` ' . $operator . ' ?'; + $this->binds[] = $value; + + break; + } + return $this; + } + + /** + * And open. Is the start of AND(....). To end this call ->close() + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + public function and_open($field, $arg2, $arg3 = null){ + + $old = $this->query; + $this->_and($field, $arg2, $arg3); + //Replace the AND with AND ( + $this->query = $old . ' AND (' . substr($this->query, strlen($old) + 5); + + return $this; + } + + /** + * Closes and ->where_open() or ->having_open() + * @return $this + */ + public function close(){ + + $this->query .= ')'; + return $this; + } + + /** + * Order By + * + * Each argument is another order. If you put a minus in front of the name, the order will be DESC instead of ASC + * + * @return $this + */ + public function order(){ + + $this->query .= ' ORDER BY'; + + foreach(func_get_args() as $field){ + + if(substr($this->query, -2) != 'BY') + $this->query .= ","; + + $mode = 'ASC'; + + if(substr($field, 0, 1) == '-'){ + + $field = substr($field, 1); + $mode = 'DESC'; + + } + + $this->query .= ' `' . $field . '` ' . $mode; + } + + return $this; + } + + /** + * limit + * + * @param $limit int Limit + * @param int $offset int Offset + * @return $this + */ + public function limit($limit, $offset = 0){ + + $this->query .= ' LIMIT ' . $offset . ', ' . $limit; + + return $this; + } + + /** + * Having + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + public function having($field, $arg2, $arg3 = null){ + + //The value is the second paramter, unless the second parameter is an operator, in which case it's the third + $value = ($arg3 == null ? $arg2 : $arg3); + //If the third parameter is not given, the default operator should be used + $operator = strtoupper($arg3 == null ? "=" : $arg2); + + switch($operator){ + + case 'IN': + + $this->query .= ' HAVING `' . $field .'` IN ('; + + foreach($value as $k => $v){ + + $this->query .= '?,'; + $this->binds[] = $v; + } + + //Remove the trailing comma and close it + $this->query = rtrim($this->query, ",") . ')'; + + + break; + + case 'BETWEEN': + + $this->query .= ' HAVING `' . $field .'` BETWEEN ? AND ?'; + $this->binds[] = $value[0]; + $this->binds[] = $value[1]; + + break; + + default: + + $this->query .= ' HAVING `' . $field .'` ' . $operator . ' ?'; + $this->binds[] = $value; + + break; + } + return $this; + } + + /** + * Having open. Is the start of HAVING(....). To end this call ->close() + * + * @param $field + * @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3 + * @param null|string $arg3 The value of the field when $arg2 is used for an operator + * @return $this + */ + + public function having_open($field, $arg2, $arg3 = null){ + + $old = $this->query; + $this->having($field, $arg2, $arg3); + //Replace the WHERE with WHERE ( + $this->query = $old . ' HAVING (' . substr($this->query, strlen($old) + 8); + + return $this; + } + + /** + * Update + * + * @param $table null|string Name of the table, if it is null, the table set with setTable() will be used + * @return $this + * @throws Exception + */ + + public function update($table = '') + { + if($table === ''){ + + if($this->table == '') + throw new Exception("No table given"); + + $table = $this->table; + } + + $this->query .= 'UPDATE `' . $table . '`'; + + return $this; + } + + /** + * Set + * + * @param $array array Key value, $field => $value + * @return $this + */ + public function set($array) + { + + $this->query .= ' SET'; + + $first = true; + + foreach($array as $field => $value){ + + if(!$first) + $this->query .= ','; + + $first = false; + $this->query .= ' `' . $field . '`=?'; + + $this->binds[] = $value; + } + + return $this; + } + + /** + * Delete + * + * @return $this + */ + public function delete() + { + + $this->query .= 'DELETE'; + + return $this; + } + + /** + * Insert + * + * @param $array array Key value, $field => $value + * @param $table string|null Table name, if it is null, the table set with setTable() will be used + * @return $this + * @throws Exception + */ + public function insert($array, $table = ''){ + + if($table === ''){ + + if($this->table == '') + throw new DatabaseException("No table given"); + + $table = $this->table; + } + + //Implode the array to get a list with the fields + $this->query .= 'INSERT INTO `' . $table . '` (`' . implode('`,`', array_keys($array)) . '`) VALUES ('; + + //Add all the values as ? and add them to the binds + foreach($array as $field => $value){ + $this->query .= '?,'; + + $this->binds[] = $value; + } + + $this->query = rtrim($this->query, ',') . ')'; + + return $this; + } + + /** + * Returns the query + * + * @return string + */ + public function getQuery(){ + + return $this->query; + } + + /** + * Return the binds that corresponds with the binds + * + * @return array + */ + public function getBinds(){ + + return $this->binds; + } + + /** + * @param $table + * @return $this + */ + public function setTable($table){ + + $this->table = $table; + + return $this; + } + + /** + * @return null|string + */ + public function getTable(){ + + return $this->table; + } + + /** + * @return $this + */ + public function commit(){ + + $this->mods->database->commit(); + + return $this; + } + + /** + * @return $this + */ + public function beginTransaction(){ + + $this->mods->database->beginTransaction(); + + return $this; + } + + /** + * @return $this + */ + public function rollback(){ + + $this->mods->database->rollback(); + + return $this; + } + + /** + * Executes the query generated + * + * @return $this + * @throws DatabaseException + */ + public function execute(){ + + + if($this->config->database->debug) + $this->logger->log("Generated query: ".$this->query, 'QueryBuilder'); + + try{ + + $this->sth = $this->mods->database->prepare($this->query); + if(count($this->binds) === 0){ + + $this->sth->execute(); + }else{ + + $this->sth->execute($this->binds); + } + }catch (\PDOException $e) { + + throw new DatabaseException('Could not execute SQL-query due PDO-exception ' . $e->getMessage()); + } + + return $this; + } + + /** + * Returns the results of the query as an associative array. ->execute() must be called first. + * @return array + * @throws DatabaseException + */ + public function getArray(){ + + if(!isset($this->sth)) + $this->execute(); + + return $this->sth->fetchAll(\PDO::FETCH_ASSOC); + } + + /** + * Returns the results of the query as an array containing objects. ->execute() must be called first. + * @return object[] + * @throws DatabaseException + */ + public function getObject(){ + + if(!isset($this->sth)) + $this->execute(); + + return $this->sth->fetchAll(\PDO::FETCH_OBJ); + } + + /** + * Returns the amount of rows that are affected. ->execute must be called first. + * @return int Amount of rows that are affected + * @throws DatabaseException + */ + public function getRowCount(){ + + if(!isset($this->sth)) + $this->execute(); + + return $this->sth->rowCount(); + } + + /** + * @return string + */ + public function getLastInsertId(){ + + return $this->mods->database->lastInsertId(); + + } + + /** + * PHP does not allow to use "or" and "and" as function name, by using __call we can redirect them to _or and _and + * + * @param $name string + * @param $arguments array + * @return mixed + */ + public function __call($name, $arguments){ + + switch($name){ + + case "or": + return call_user_func_array(array($this, "_or"), $arguments); + + case "and": + return call_user_func_array(array($this, "_and"), $arguments); + } + } +} \ No newline at end of file diff --git a/Modules/databaseutils/moduleInfo.php b/Modules/databaseutils/moduleInfo.php new file mode 100644 index 0000000..111c0cf --- /dev/null +++ b/Modules/databaseutils/moduleInfo.php @@ -0,0 +1,23 @@ + 'Module\DatabaseUtils\Main', + 'module_file' => 'class.model.php', + 'module_name' => 'DatabaseUtils', + + 'abstract' => false, + 'dependencies' => array('techfuze/database'), + 'events' => array(), + 'sections' => array(), + + 'name' => 'FuzeWorks Database Utilities', + 'description' => 'Automatically build SQL queries using methods in this class', + 'author' => 'TechFuze', + 'version' => '1.0.0', + 'website' => 'http://fuzeworks.techfuze.net/', + + 'date_created' => '29-04-2015', + 'date_updated' => '29-04-2015', + + 'enabled' => true, +); From 26d06947ad337abe84a05bc308e828ced67a0610 Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Wed, 6 May 2015 21:38:31 +0200 Subject: [PATCH 2/2] Added new QueryTest for the newly made QueryBuilder --- tests/module_queryTest.php | 335 +++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 tests/module_queryTest.php diff --git a/tests/module_queryTest.php b/tests/module_queryTest.php new file mode 100644 index 0000000..3e03def --- /dev/null +++ b/tests/module_queryTest.php @@ -0,0 +1,335 @@ +createCore(); + $core->loadMod('techfuze/databaseutils'); + $this->query = new Query($core); + } + + /* + * Select + */ + + public function testSelectSimple() + { + $this->query->select()->from('table'); + $this->assertEquals('SELECT * FROM `table`', $this->query->getQuery()); + } + + public function testSelectSimpleDefaultTable(){ + + $this->query->setTable("table")->select()->from('table'); + $this->assertEquals('SELECT * FROM `table`', $this->query->getQuery()); + } + + public function testSelectSimpleOneField(){ + + $this->query->select('field1')->from('table'); + $this->assertEquals('SELECT `field1` FROM `table`', $this->query->getQuery()); + } + + public function testSelectSimpleTwoFields(){ + + $this->query->select('field1', 'field2')->from('table'); + $this->assertEquals('SELECT `field1`, `field2` FROM `table`', $this->query->getQuery()); + } + + /* + * Where + */ + + public function testSelectWhere(){ + + $this->query->select()->from('table')->where("field", "value"); + $this->assertEquals('SELECT * FROM `table` WHERE `field` = ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectWhereLike(){ + + $this->query->select()->from('table')->where("field", "like", '%value%'); + $this->assertEquals('SELECT * FROM `table` WHERE `field` LIKE ?', $this->query->getQuery()); + $this->assertEquals(array('%value%'), $this->query->getBinds()); + } + + public function testSelectWhereBetween(){ + + $this->query->select()->from('table')->where("field", "between", array(2, 4)); + $this->assertEquals('SELECT * FROM `table` WHERE `field` BETWEEN ? AND ?', $this->query->getQuery()); + $this->assertEquals(array(2, 4), $this->query->getBinds()); + } + + public function testSelectWhereIn(){ + + $this->query->select()->from('table')->where("field", "in", array(2, 3, 4)); + $this->assertEquals('SELECT * FROM `table` WHERE `field` IN (?,?,?)', $this->query->getQuery()); + $this->assertEquals(array(2, 3, 4), $this->query->getBinds()); + } + + public function testSelectWhereNot(){ + + $this->query->select()->from('table')->where("field", "<>", "value"); + $this->assertEquals('SELECT * FROM `table` WHERE `field` <> ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectWhereGreater(){ + + $this->query->select()->from('table')->where("field", ">", "value"); + $this->assertEquals('SELECT * FROM `table` WHERE `field` > ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectWhereGreaterEqual(){ + + $this->query->select()->from('table')->where("field", ">=", "value"); + $this->assertEquals('SELECT * FROM `table` WHERE `field` >= ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectWhereSmaller(){ + + $this->query->select()->from('table')->where("field", "<", "value"); + $this->assertEquals('SELECT * FROM `table` WHERE `field` < ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectWhereSmallerEqual(){ + + $this->query->select()->from('table')->where("field", "<=", "value"); + $this->assertEquals('SELECT * FROM `table` WHERE `field` <= ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectWhereAnd(){ + + $this->query->select()->from('table')->where("field1", "value1")->and("field2", "value2"); + $this->assertEquals('SELECT * FROM `table` WHERE `field1` = ? AND `field2` = ?', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2'), $this->query->getBinds()); + } + + public function testSelectWhereAndOpen(){ + + $this->query->select()->from('table')->where("field1", "value1")->and_open("field2", "value2")->or("field3", "value3")->close(); + $this->assertEquals('SELECT * FROM `table` WHERE `field1` = ? AND (`field2` = ? OR `field3` = ?)', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2', 'value3'), $this->query->getBinds()); + } + + public function testSelectWhereOr(){ + + $this->query->select()->from('table')->where("field1", "value1")->or("field2", "value2"); + $this->assertEquals('SELECT * FROM `table` WHERE `field1` = ? OR `field2` = ?', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2'), $this->query->getBinds()); + } + + public function testSelectWhereOrOpen(){ + + $this->query->select()->from('table')->where("field1", "value1")->or_open("field2", "value2")->and("field3", "value3")->close(); + $this->assertEquals('SELECT * FROM `table` WHERE `field1` = ? OR (`field2` = ? AND `field3` = ?)', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2', 'value3'), $this->query->getBinds()); + } + public function testSelectWhereOpen(){ + + $this->query->select()->from('table') + ->where_open("field1", "value1")->and("field2", "value2")->close() + ->or('field3', 'value3'); + $this->assertEquals('SELECT * FROM `table` WHERE (`field1` = ? AND `field2` = ?) OR `field3` = ?', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2', 'value3'), $this->query->getBinds()); + } + + /* + * Order by + */ + + public function testSelectOrderASC(){ + + $this->query->select()->from('table')->order('field'); + $this->assertEquals('SELECT * FROM `table` ORDER BY `field` ASC', $this->query->getQuery()); + } + + public function testSelectOrderDESC(){ + + $this->query->select()->from('table')->order('-field'); + $this->assertEquals('SELECT * FROM `table` ORDER BY `field` DESC', $this->query->getQuery()); + } + + public function testSelectOrderMultiple(){ + + $this->query->select()->from('table')->order('field1', '-field2'); + $this->assertEquals('SELECT * FROM `table` ORDER BY `field1` ASC, `field2` DESC', $this->query->getQuery()); + } + + /* + * Limit + */ + + public function testSelectLimit() + { + $this->query->select()->from('table')->limit(5, 10); + $this->assertEquals('SELECT * FROM `table` LIMIT 10, 5', $this->query->getQuery()); + + } + + /* + * Having + */ + + public function testSelectHaving(){ + + $this->query->select()->from('table')->having("field", "value"); + $this->assertEquals('SELECT * FROM `table` HAVING `field` = ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectHavingLike(){ + + $this->query->select()->from('table')->having("field", "like", '%value%'); + $this->assertEquals('SELECT * FROM `table` HAVING `field` LIKE ?', $this->query->getQuery()); + $this->assertEquals(array('%value%'), $this->query->getBinds()); + } + + public function testSelectHavingBetween(){ + + $this->query->select()->from('table')->having("field", "between", array(2, 4)); + $this->assertEquals('SELECT * FROM `table` HAVING `field` BETWEEN ? AND ?', $this->query->getQuery()); + $this->assertEquals(array(2, 4), $this->query->getBinds()); + } + + public function testSelectHavingIn(){ + + $this->query->select()->from('table')->having("field", "in", array(2, 3, 4)); + $this->assertEquals('SELECT * FROM `table` HAVING `field` IN (?,?,?)', $this->query->getQuery()); + $this->assertEquals(array(2, 3, 4), $this->query->getBinds()); + } + + public function testSelectHavingNot(){ + + $this->query->select()->from('table')->having("field", "<>", "value"); + $this->assertEquals('SELECT * FROM `table` HAVING `field` <> ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectHavingGreater(){ + + $this->query->select()->from('table')->having("field", ">", "value"); + $this->assertEquals('SELECT * FROM `table` HAVING `field` > ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectHavingGreaterEqual(){ + + $this->query->select()->from('table')->having("field", ">=", "value"); + $this->assertEquals('SELECT * FROM `table` HAVING `field` >= ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectHavingSmaller(){ + + $this->query->select()->from('table')->having("field", "<", "value"); + $this->assertEquals('SELECT * FROM `table` HAVING `field` < ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectHavingSmallerEqual(){ + + $this->query->select()->from('table')->having("field", "<=", "value"); + $this->assertEquals('SELECT * FROM `table` HAVING `field` <= ?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testSelectHavingAnd(){ + + $this->query->select()->from('table')->having("field1", "value1")->and("field2", "value2"); + $this->assertEquals('SELECT * FROM `table` HAVING `field1` = ? AND `field2` = ?', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2'), $this->query->getBinds()); + } + + public function testSelectHavingAndOpen(){ + + $this->query->select()->from('table')->having("field1", "value1")->and_open("field2", "value2")->or("field3", "value3")->close(); + $this->assertEquals('SELECT * FROM `table` HAVING `field1` = ? AND (`field2` = ? OR `field3` = ?)', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2', 'value3'), $this->query->getBinds()); + } + + public function testSelectHavingOr(){ + + $this->query->select()->from('table')->having("field1", "value1")->or("field2", "value2"); + $this->assertEquals('SELECT * FROM `table` HAVING `field1` = ? OR `field2` = ?', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2'), $this->query->getBinds()); + } + + public function testSelectHavingOrOpen(){ + + $this->query->select()->from('table')->having("field1", "value1")->or_open("field2", "value2")->and("field3", "value3")->close(); + $this->assertEquals('SELECT * FROM `table` HAVING `field1` = ? OR (`field2` = ? AND `field3` = ?)', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2', 'value3'), $this->query->getBinds()); + } + public function testSelectHavingOpen(){ + + $this->query->select()->from('table') + ->having_open("field1", "value1")->and("field2", "value2")->close() + ->or('field3', 'value3'); + $this->assertEquals('SELECT * FROM `table` HAVING (`field1` = ? AND `field2` = ?) OR `field3` = ?', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2', 'value3'), $this->query->getBinds()); + } + + /* + * Update + */ + + public function testUpdateSimple(){ + + $this->query->update('table')->set(array('field' => 'value')); + $this->assertEquals('UPDATE `table` SET `field`=?', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + } + + public function testUpdateMultiple(){ + + $this->query->update('table')->set(array('field1' => 'value1', "field2" => 'value2')); + $this->assertEquals('UPDATE `table` SET `field1`=?, `field2`=?', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2'), $this->query->getBinds()); + } + + /* + * Delete + */ + + public function testDeleteSimple(){ + + $this->query->delete()->from('table'); + $this->assertEquals('DELETE FROM `table`', $this->query->getQuery()); + } + + /* + * Insert + */ + + public function testInsertSimple(){ + + $this->query->insert(array('field' => 'value'), 'table'); + $this->assertEquals('INSERT INTO `table` (`field`) VALUES (?)', $this->query->getQuery()); + $this->assertEquals(array('value'), $this->query->getBinds()); + + } + + public function testInsertMultiple(){ + + $this->query->insert(array('field1' => 'value1', 'field2' => 'value2'), 'table'); + $this->assertEquals('INSERT INTO `table` (`field1`,`field2`) VALUES (?,?)', $this->query->getQuery()); + $this->assertEquals(array('value1', 'value2'), $this->query->getBinds()); + + } +} \ No newline at end of file