From 3ec7818eaa12f124313637331ad7e5e0a4420faa Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Mon, 26 May 2014 09:41:12 +0300 Subject: [PATCH 01/22] Add getLastQuery() in readme --- readme.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/readme.md b/readme.md index 96558337..0de4ccd1 100644 --- a/readme.md +++ b/readme.md @@ -227,3 +227,10 @@ Obtain an initialized instance of the class from another class ```php $db = MysqliDb::getInstance(); ``` + +Get last executed SQL query. +Please note that function returns SQL query only for debugging purposes as its execution most likely will fail due missing quotes around char variables. +```php + $db->get('users'); + echo "Last executed query was ". $db->getLastQuery(); +``` From c7709294ee2df76629673f0bc1dc17f5f8c72c03 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 16:39:17 +0300 Subject: [PATCH 02/22] Fix table drop in tests --- tests.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests.php b/tests.php index 2eb65255..df51f3dc 100644 --- a/tests.php +++ b/tests.php @@ -94,7 +94,7 @@ function createTable ($name, $data) { } foreach ($tables as $name => $fields) { - $db->rawQuery("DROP TABLE $name"); + $db->rawQuery("DROP TABLE ".$prefix.$name); createTable ($prefix.$name, $fields); } From 9503e130af47e4fdd80a8d296496a5ea3470583b Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 17:08:29 +0300 Subject: [PATCH 03/22] Add one more test --- tests.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests.php b/tests.php index df51f3dc..275f354c 100644 --- a/tests.php +++ b/tests.php @@ -217,6 +217,13 @@ function createTable ($name, $data) { exit; } +$db->where("id = 1 or id = 2"); +$res = $db->get ("users"); +if ($db->count != 2) { + echo "Invalid users count on select with multiple params"; + exit; +} + $db->delete("users"); $db->get("users"); if ($db->count != 0) { From 3214680b72575c2431b2a66c56d0bbb5c7d388a1 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 17:08:41 +0300 Subject: [PATCH 04/22] Initial subqueries support API --- MysqliDb.php | 103 ++++++++++++++++++++++++++++++++++++++++++++------- readme.md | 10 +++++ 2 files changed, 100 insertions(+), 13 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 32d794bd..ee0a43c5 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -93,6 +93,12 @@ class MysqliDb protected $db; protected $port; + /** + * Is Subquery object + * + */ + protected $isSubQuery = false; + /** * @param string $host * @param string $username @@ -100,7 +106,7 @@ class MysqliDb * @param string $db * @param int $port */ - public function __construct($host, $username, $password, $db, $port = NULL) + public function __construct($host = NULL, $username = NULL, $password = NULL, $db = NULL, $port = NULL) { $this->host = $host; $this->username = $username; @@ -110,9 +116,14 @@ public function __construct($host, $username, $password, $db, $port = NULL) $this->port = ini_get ('mysqli.default_port'); else $this->port = $port; - - $this->connect(); $this->setPrefix(); + + if ($host == null && $username == null && $db == null) { + $this->isSubQuery = true; + return; + } + // for subqueries we do not need database connection and redefine root instance + $this->connect(); self::$_instance = $this; } @@ -122,6 +133,9 @@ public function __construct($host, $username, $password, $db, $port = NULL) */ public function connect() { + if ($this->isSubQuery) + return; + $this->_mysqli = new mysqli ($this->host, $this->username, $this->password, $this->db, $this->port) or die('There was a problem connecting to the database'); @@ -165,6 +179,7 @@ protected function reset() public function setPrefix($prefix = '') { $this->_prefix = $prefix; + return $this; } /** @@ -233,6 +248,10 @@ public function get($tableName, $numRows = null, $columns = '*') $column = is_array($columns) ? implode(', ', $columns) : $columns; $this->_query = "SELECT $column FROM $this->_prefix$tableName"; $stmt = $this->_buildQuery($numRows); + + if ($this->isSubQuery) + return $this; + $stmt->execute(); $this->_stmtError = $stmt->error; $this->reset(); @@ -250,6 +269,10 @@ public function get($tableName, $numRows = null, $columns = '*') public function getOne($tableName, $columns = '*') { $res = $this->get ($tableName, 1, $columns); + + if (is_object($res)) + return $res; + if (isset($res[0])) return $res[0]; @@ -265,6 +288,9 @@ public function getOne($tableName, $columns = '*') */ public function insert($tableName, $insertData) { + if ($this->isSubQuery) + return; + $this->_query = "INSERT into $this->_prefix$tableName"; $stmt = $this->_buildQuery(null, $insertData); $stmt->execute(); @@ -284,6 +310,9 @@ public function insert($tableName, $insertData) */ public function update($tableName, $tableData) { + if ($this->isSubQuery) + return; + $this->_query = "UPDATE $this->_prefix$tableName SET "; $stmt = $this->_buildQuery(null, $tableData); @@ -304,6 +333,9 @@ public function update($tableName, $tableData) */ public function delete($tableName, $numRows = null) { + if ($this->isSubQuery) + return; + $this->_query = "DELETE FROM $this->_prefix$tableName"; $stmt = $this->_buildQuery($numRows); @@ -488,6 +520,21 @@ protected function _bindParam($value) array_push ($this->_bindParams, $value); } + protected function _buildPair ($operator, $value) { + if (!is_object($value)) { + $this->_bindParam ($value); + $comparison = ' ' . $operator. ' ? '; + return $comparison; + } + + $subQuery = $value->getSubQuery(); + $comparison = " " . $operator . " (" . $subQuery['query'] . ")"; + foreach ($subQuery['params'] as $v) + $this->_bindParam ($v); + + return $comparison; + } + /** * Abstraction method that will compile the WHERE statement, * any passed update data, and the desired rows. @@ -582,9 +629,13 @@ protected function _buildQuery($numRows = null, $tableData = null) case 'not in': case 'in': $comparison = ' ' . $key . ' ('; - foreach($val as $v){ - $comparison .= ' ?,'; - $this->_bindParam ($v); + if (is_object ($val)) { + $comparison .= $this->_buildPair ("", $val); + } else { + foreach ($val as $v) { + $comparison .= ' ?,'; + $this->_bindParam ($v); + } } $comparison = rtrim($comparison, ',').' ) '; break; @@ -596,14 +647,12 @@ protected function _buildQuery($numRows = null, $tableData = null) break; default: // We are using a comparison operator with only one parameter after it - $comparison = ' '.$key.' ? '; - $this->_bindParam ($val); + $comparison = $this->_buildPair ($key, $val); } } else if ($value[1] === null) { $comparison = ''; } else { - $comparison = ' = ? '; - $this->_bindParam ($value[1]); + $comparison = $this->_buildPair ("=", $value[1]); } $this->_query .= $column.$comparison; } @@ -637,6 +686,11 @@ protected function _buildQuery($numRows = null, $tableData = null) $this->_query .= ' LIMIT ' . (int)$numRows; } + $this->_lastQuery = $this->replacePlaceHolders($this->_query, $this->_bindParams); + + if ($this->isSubQuery) + return; + // Prepare query $stmt = $this->_prepareQuery(); @@ -644,7 +698,6 @@ protected function _buildQuery($numRows = null, $tableData = null) if (count ($this->_bindParams) > 1) call_user_func_array(array($stmt, 'bind_param'), $this->refValues($this->_bindParams)); - $this->_lastQuery = $this->replacePlaceHolders($this->_query, $this->_bindParams); return $stmt; } @@ -708,7 +761,8 @@ protected function _prepareQuery() */ public function __destruct() { - $this->_mysqli->close(); + if (!$this->isSubQuery) + $this->_mysqli->close(); } /** @@ -741,7 +795,10 @@ protected function replacePlaceHolders ($str, $vals) { $newStr = ""; while ($pos = strpos ($str, "?")) { - $newStr .= substr ($str, 0, $pos) . $vals[$i++]; + $val = $vals[$i++]; + if (is_object ($val)) + $val = '[object]'; + $newStr .= substr ($str, 0, $pos) . $val; $str = substr ($str, $pos + 1); } return $newStr; @@ -765,6 +822,18 @@ public function getLastError () { return $this->_stmtError . " " . $this->_mysqli->error; } + public function getSubQuery () { + if (!$this->isSubQuery) + return null; + + array_shift ($this->_bindParams); + $val = Array ('query' => $this->_query, + 'params' => $this->_bindParams + ); + $this->reset(); + return $val; + } + /* Helper functions */ /** * Method returns generated interval function as a string @@ -841,4 +910,12 @@ public function func ($expr, $bindParams = null) { return Array ("[F]" => Array($expr, $bindParams)); } + /** + * Method creates new mysqlidb object for a subquery generation + */ + public static function subQuery() + { + return new MysqliDb(); + } + } // END class diff --git a/readme.md b/readme.md index 0de4ccd1..5b8aeb5f 100644 --- a/readme.md +++ b/readme.md @@ -216,6 +216,16 @@ $products = $db->get ("products p", null, "u.name, p.productName"); print_r ($products); ``` +### Subqueries +```php +$db->where("id", Array("in" => $db->subQuery() + ->where("qty", Array (">" => 2)); + ->get("products",null,"userId") + )); + +$res = $db->get ("users"); +// Gives SELECT * FROM users WHERE id IN (SELECT userId FROM products WHERE qty > 2) +``` ### Helper commands Reconnect in case mysql connection died ```php From 99909967cb1d952c9633e83d489c5c645d85e5df Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 18:41:07 +0300 Subject: [PATCH 05/22] Minor fixes --- MysqliDb.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index ee0a43c5..d1ba6ff8 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -270,7 +270,7 @@ public function getOne($tableName, $columns = '*') { $res = $this->get ($tableName, 1, $columns); - if (is_object($res)) + if ($this->isSubQuery) return $res; if (isset($res[0])) @@ -795,10 +795,7 @@ protected function replacePlaceHolders ($str, $vals) { $newStr = ""; while ($pos = strpos ($str, "?")) { - $val = $vals[$i++]; - if (is_object ($val)) - $val = '[object]'; - $newStr .= substr ($str, 0, $pos) . $val; + $newStr .= substr ($str, 0, $pos) . $vals[$i++]; $str = substr ($str, $pos + 1); } return $newStr; From c1d40ca6302766b90be9168e675919218ceb2ebd Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 18:49:23 +0300 Subject: [PATCH 06/22] Prefix should be static to be shareble with subqueries --- MysqliDb.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index d1ba6ff8..44c10b92 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -24,7 +24,7 @@ class MysqliDb * * @var string */ - protected $_prefix; + protected static $_prefix; /** * MySQLi instance * @@ -116,14 +116,15 @@ public function __construct($host = NULL, $username = NULL, $password = NULL, $d $this->port = ini_get ('mysqli.default_port'); else $this->port = $port; - $this->setPrefix(); if ($host == null && $username == null && $db == null) { $this->isSubQuery = true; return; } + // for subqueries we do not need database connection and redefine root instance $this->connect(); + $this->setPrefix(); self::$_instance = $this; } @@ -178,7 +179,7 @@ protected function reset() */ public function setPrefix($prefix = '') { - $this->_prefix = $prefix; + self::$_prefix = $prefix; return $this; } @@ -246,7 +247,7 @@ public function get($tableName, $numRows = null, $columns = '*') $columns = '*'; $column = is_array($columns) ? implode(', ', $columns) : $columns; - $this->_query = "SELECT $column FROM $this->_prefix$tableName"; + $this->_query = "SELECT $column FROM " .self::$_prefix . $tableName; $stmt = $this->_buildQuery($numRows); if ($this->isSubQuery) @@ -291,7 +292,7 @@ public function insert($tableName, $insertData) if ($this->isSubQuery) return; - $this->_query = "INSERT into $this->_prefix$tableName"; + $this->_query = "INSERT into " .self::$_prefix . $tableName; $stmt = $this->_buildQuery(null, $insertData); $stmt->execute(); $this->_stmtError = $stmt->error; @@ -313,7 +314,7 @@ public function update($tableName, $tableData) if ($this->isSubQuery) return; - $this->_query = "UPDATE $this->_prefix$tableName SET "; + $this->_query = "UPDATE " . self::$_prefix . $tableName ." SET "; $stmt = $this->_buildQuery(null, $tableData); $stmt->execute(); @@ -336,7 +337,7 @@ public function delete($tableName, $numRows = null) if ($this->isSubQuery) return; - $this->_query = "DELETE FROM $this->_prefix$tableName"; + $this->_query = "DELETE FROM " . self::$_prefix . $tableName; $stmt = $this->_buildQuery($numRows); $stmt->execute(); @@ -397,7 +398,7 @@ public function join($joinTable, $joinCondition, $joinType = '') if ($joinType && !in_array ($joinType, $allowedTypes)) die ('Wrong JOIN type: '.$joinType); - $this->_join[$joinType . " JOIN " . $this->_prefix.$joinTable] = $joinCondition; + $this->_join[$joinType . " JOIN " . self::$_prefix . $joinTable] = $joinCondition; return $this; } From ab6caff51b6705922f5212521c358097005505d6 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 19:27:26 +0300 Subject: [PATCH 07/22] Added nicer where API to define operators --- MysqliDb.php | 10 ++++++++-- readme.md | 20 ++++++++++---------- tests.php | 8 ++++---- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 44c10b92..5304df66 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -357,8 +357,11 @@ public function delete($tableName, $numRows = null) * * @return MysqliDb */ - public function where($whereProp, $whereValue = null) + public function where($whereProp, $whereValue = null, $operator = null) { + if ($operator) + $whereValue = Array ($operator => $whereValue); + $this->_where[$whereProp] = Array ("AND", $whereValue); return $this; } @@ -373,8 +376,11 @@ public function where($whereProp, $whereValue = null) * * @return MysqliDb */ - public function orWhere($whereProp, $whereValue = null) + public function orWhere($whereProp, $whereValue = null, $operator = null) { + if ($operator) + $whereValue = Array ($operator => $whereValue); + $this->_where[$whereProp] = Array ("OR", $whereValue); return $this; } diff --git a/readme.md b/readme.md index 5b8aeb5f..c5bd5364 100644 --- a/readme.md +++ b/readme.md @@ -133,23 +133,23 @@ $results = $db->get('users'); ``` ```php -$db->where('id', Array('>=' => 50)); +$db->where('id', 50, ">="); $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id >= 50; ``` BETWEEN: ```php -$db->where('id', Array('between' => Array(4, 20) ) ); -//$db->where('id', Array('not between' => Array(4, 20) ) ); +$db->where('id', Array(4, 20), 'between'); +//$db->where('id', Array(4, 20), 'not between'); $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id BETWEEN 4 AND 20 ``` IN: ```php -$db->where('id', Array( 'in' => Array(1, 5, 27, -1, 'd') ) ); -//$db->where('id', Array( 'not in' => Array(1, 5, 27, -1, 'd') ) ); +$db->where('id', Array(1, 5, 27, -1, 'd'), 'IN'); +//$db->where('id', Array(1, 5, 27, -1, 'd'), 'NOT IN'); $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id IN (1, 5, 27, -1, 'd'); ``` @@ -164,7 +164,7 @@ $results = $db->get('users'); NULL comparison: ```php -$db->where ("lastName", Array("<=>" => NULL)); +$db->where ("lastName", NULL, '<=>'); $results = $db->get("users"); // Gives: SELECT * FROM users where lastName <=> NULL ``` @@ -218,10 +218,10 @@ print_r ($products); ### Subqueries ```php -$db->where("id", Array("in" => $db->subQuery() - ->where("qty", Array (">" => 2)); - ->get("products",null,"userId") - )); +$db->where("id", $db->subQuery() + ->where("qty", 2, ">") + ->get("products",null,"userId"), + 'in'); $res = $db->get ("users"); // Gives SELECT * FROM users WHERE id IN (SELECT userId FROM products WHERE qty > 2) diff --git a/tests.php b/tests.php index 275f354c..ce2adbf1 100644 --- a/tests.php +++ b/tests.php @@ -138,7 +138,7 @@ function createTable ($name, $data) { //$users = $db->get("users"); //print_r ($users); -$db->where("firstname", Array("LIKE" => '%John%')); +$db->where("firstname", '%John%', 'LIKE'); $users = $db->get("users"); if ($db->count != 1) { echo "Invalid insert count in LIKE: ".$db->count; @@ -172,14 +172,14 @@ function createTable ($name, $data) { exit; } -$db->where ("id", Array('in' => Array('1','2','3'))); +$db->where ("id", Array('1','2','3'), 'IN'); $db->get("users"); if ($db->count != 3) { echo "Invalid users count on where() with in "; exit; } -$db->where ("id", Array('between' => Array('2','3'))); +$db->where ("id", Array('2','3'), 'between'); $db->get("users"); if ($db->count != 2) { echo "Invalid users count on where() with between"; @@ -194,7 +194,7 @@ function createTable ($name, $data) { exit; } -$db->where ("lastName", Array("<=>" => NULL)); +$db->where ("lastName", NULL, '<=>'); $r = $db->get("users"); if ($db->count != 1) { echo "Invalid users count on null where()"; From 8f513b0a56bebc60610570f231af6306c9b9c454 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 20:43:48 +0300 Subject: [PATCH 08/22] Revert "Minor fixes" This reverts commit 99909967cb1d952c9633e83d489c5c645d85e5df. --- MysqliDb.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 5304df66..c06073ed 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -271,7 +271,7 @@ public function getOne($tableName, $columns = '*') { $res = $this->get ($tableName, 1, $columns); - if ($this->isSubQuery) + if (is_object($res)) return $res; if (isset($res[0])) @@ -802,7 +802,10 @@ protected function replacePlaceHolders ($str, $vals) { $newStr = ""; while ($pos = strpos ($str, "?")) { - $newStr .= substr ($str, 0, $pos) . $vals[$i++]; + $val = $vals[$i++]; + if (is_object ($val)) + $val = '[object]'; + $newStr .= substr ($str, 0, $pos) . $val; $str = substr ($str, $pos + 1); } return $newStr; From 7a71c491cc1dfaa3d298ba88bd2eb59772a206aa Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 20:51:15 +0300 Subject: [PATCH 09/22] Subquery support in UPDATE/INSERT --- MysqliDb.php | 4 +++- readme.md | 7 ++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index c06073ed..b798a76c 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -579,7 +579,9 @@ protected function _buildQuery($numRows = null, $tableData = null) if ($isUpdate !== false) $this->_query .= "`" . $column . "` = "; - if (!is_array ($value)) { + if (is_object ($value)) { + $this->_query .= $this->_buildPair ("", $value) . ", "; + } else if (!is_array ($value)) { $this->_bindParam ($value); $this->_query .= '?, '; } else { diff --git a/readme.md b/readme.md index c5bd5364..efd9f142 100644 --- a/readme.md +++ b/readme.md @@ -218,11 +218,8 @@ print_r ($products); ### Subqueries ```php -$db->where("id", $db->subQuery() - ->where("qty", 2, ">") - ->get("products",null,"userId"), - 'in'); - +$ids = $db->subQuery()->where("qty", 2, ">")->get("products", null, "userId"); +$db->where("id", $ids, 'in'); $res = $db->get ("users"); // Gives SELECT * FROM users WHERE id IN (SELECT userId FROM products WHERE qty > 2) ``` From b6d5e9bfa45da32ba346ead4747b20495a3948c1 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 17:08:41 +0300 Subject: [PATCH 10/22] Subqueries support API --- MysqliDb.php | 126 +++++++++++++++++++++++++++++++++++++++++++-------- readme.md | 7 +++ 2 files changed, 113 insertions(+), 20 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 32d794bd..4f99f70b 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -24,7 +24,7 @@ class MysqliDb * * @var string */ - protected $_prefix; + protected static $_prefix; /** * MySQLi instance * @@ -93,6 +93,12 @@ class MysqliDb protected $db; protected $port; + /** + * Is Subquery object + * + */ + protected $isSubQuery = false; + /** * @param string $host * @param string $username @@ -100,7 +106,7 @@ class MysqliDb * @param string $db * @param int $port */ - public function __construct($host, $username, $password, $db, $port = NULL) + public function __construct($host = NULL, $username = NULL, $password = NULL, $db = NULL, $port = NULL) { $this->host = $host; $this->username = $username; @@ -110,7 +116,13 @@ public function __construct($host, $username, $password, $db, $port = NULL) $this->port = ini_get ('mysqli.default_port'); else $this->port = $port; - + + if ($host == null && $username == null && $db == null) { + $this->isSubQuery = true; + return; + } + + // for subqueries we do not need database connection and redefine root instance $this->connect(); $this->setPrefix(); self::$_instance = $this; @@ -122,6 +134,9 @@ public function __construct($host, $username, $password, $db, $port = NULL) */ public function connect() { + if ($this->isSubQuery) + return; + $this->_mysqli = new mysqli ($this->host, $this->username, $this->password, $this->db, $this->port) or die('There was a problem connecting to the database'); @@ -164,7 +179,8 @@ protected function reset() */ public function setPrefix($prefix = '') { - $this->_prefix = $prefix; + self::$_prefix = $prefix; + return $this; } /** @@ -231,8 +247,12 @@ public function get($tableName, $numRows = null, $columns = '*') $columns = '*'; $column = is_array($columns) ? implode(', ', $columns) : $columns; - $this->_query = "SELECT $column FROM $this->_prefix$tableName"; + $this->_query = "SELECT $column FROM " .self::$_prefix . $tableName; $stmt = $this->_buildQuery($numRows); + + if ($this->isSubQuery) + return $this; + $stmt->execute(); $this->_stmtError = $stmt->error; $this->reset(); @@ -250,6 +270,10 @@ public function get($tableName, $numRows = null, $columns = '*') public function getOne($tableName, $columns = '*') { $res = $this->get ($tableName, 1, $columns); + + if (is_object($res)) + return $res; + if (isset($res[0])) return $res[0]; @@ -265,7 +289,10 @@ public function getOne($tableName, $columns = '*') */ public function insert($tableName, $insertData) { - $this->_query = "INSERT into $this->_prefix$tableName"; + if ($this->isSubQuery) + return; + + $this->_query = "INSERT into " .self::$_prefix . $tableName; $stmt = $this->_buildQuery(null, $insertData); $stmt->execute(); $this->_stmtError = $stmt->error; @@ -284,7 +311,10 @@ public function insert($tableName, $insertData) */ public function update($tableName, $tableData) { - $this->_query = "UPDATE $this->_prefix$tableName SET "; + if ($this->isSubQuery) + return; + + $this->_query = "UPDATE " . self::$_prefix . $tableName ." SET "; $stmt = $this->_buildQuery(null, $tableData); $stmt->execute(); @@ -304,7 +334,10 @@ public function update($tableName, $tableData) */ public function delete($tableName, $numRows = null) { - $this->_query = "DELETE FROM $this->_prefix$tableName"; + if ($this->isSubQuery) + return; + + $this->_query = "DELETE FROM " . self::$_prefix . $tableName; $stmt = $this->_buildQuery($numRows); $stmt->execute(); @@ -365,7 +398,7 @@ public function join($joinTable, $joinCondition, $joinType = '') if ($joinType && !in_array ($joinType, $allowedTypes)) die ('Wrong JOIN type: '.$joinType); - $this->_join[$joinType . " JOIN " . $this->_prefix.$joinTable] = $joinCondition; + $this->_join[$joinType . " JOIN " . self::$_prefix . $joinTable] = $joinCondition; return $this; } @@ -488,6 +521,21 @@ protected function _bindParam($value) array_push ($this->_bindParams, $value); } + protected function _buildPair ($operator, $value) { + if (!is_object($value)) { + $this->_bindParam ($value); + $comparison = ' ' . $operator. ' ? '; + return $comparison; + } + + $subQuery = $value->getSubQuery(); + $comparison = " " . $operator . " (" . $subQuery['query'] . ")"; + foreach ($subQuery['params'] as $v) + $this->_bindParam ($v); + + return $comparison; + } + /** * Abstraction method that will compile the WHERE statement, * any passed update data, and the desired rows. @@ -525,7 +573,9 @@ protected function _buildQuery($numRows = null, $tableData = null) if ($isUpdate !== false) $this->_query .= "`" . $column . "` = "; - if (!is_array ($value)) { + if (is_object ($value)) { + $this->_query .= $this->_buildPair ("", $value) . ", "; + } else if (!is_array ($value)) { $this->_bindParam ($value); $this->_query .= '?, '; } else { @@ -582,9 +632,13 @@ protected function _buildQuery($numRows = null, $tableData = null) case 'not in': case 'in': $comparison = ' ' . $key . ' ('; - foreach($val as $v){ - $comparison .= ' ?,'; - $this->_bindParam ($v); + if (is_object ($val)) { + $comparison .= $this->_buildPair ("", $val); + } else { + foreach ($val as $v) { + $comparison .= ' ?,'; + $this->_bindParam ($v); + } } $comparison = rtrim($comparison, ',').' ) '; break; @@ -596,14 +650,12 @@ protected function _buildQuery($numRows = null, $tableData = null) break; default: // We are using a comparison operator with only one parameter after it - $comparison = ' '.$key.' ? '; - $this->_bindParam ($val); + $comparison = $this->_buildPair ($key, $val); } } else if ($value[1] === null) { $comparison = ''; } else { - $comparison = ' = ? '; - $this->_bindParam ($value[1]); + $comparison = $this->_buildPair ("=", $value[1]); } $this->_query .= $column.$comparison; } @@ -637,6 +689,11 @@ protected function _buildQuery($numRows = null, $tableData = null) $this->_query .= ' LIMIT ' . (int)$numRows; } + $this->_lastQuery = $this->replacePlaceHolders($this->_query, $this->_bindParams); + + if ($this->isSubQuery) + return; + // Prepare query $stmt = $this->_prepareQuery(); @@ -644,7 +701,6 @@ protected function _buildQuery($numRows = null, $tableData = null) if (count ($this->_bindParams) > 1) call_user_func_array(array($stmt, 'bind_param'), $this->refValues($this->_bindParams)); - $this->_lastQuery = $this->replacePlaceHolders($this->_query, $this->_bindParams); return $stmt; } @@ -708,7 +764,8 @@ protected function _prepareQuery() */ public function __destruct() { - $this->_mysqli->close(); + if (!$this->isSubQuery) + $this->_mysqli->close(); } /** @@ -741,7 +798,10 @@ protected function replacePlaceHolders ($str, $vals) { $newStr = ""; while ($pos = strpos ($str, "?")) { - $newStr .= substr ($str, 0, $pos) . $vals[$i++]; + $val = $vals[$i++]; + if (is_object ($val)) + $val = '[object]'; + $newStr .= substr ($str, 0, $pos) . $val; $str = substr ($str, $pos + 1); } return $newStr; @@ -765,6 +825,24 @@ public function getLastError () { return $this->_stmtError . " " . $this->_mysqli->error; } + /** + * Mostly internal method to get query and its params out of subquery object + * after get() and getAll() + * + * @return array + */ + public function getSubQuery () { + if (!$this->isSubQuery) + return null; + + array_shift ($this->_bindParams); + $val = Array ('query' => $this->_query, + 'params' => $this->_bindParams + ); + $this->reset(); + return $val; + } + /* Helper functions */ /** * Method returns generated interval function as a string @@ -841,4 +919,12 @@ public function func ($expr, $bindParams = null) { return Array ("[F]" => Array($expr, $bindParams)); } + /** + * Method creates new mysqlidb object for a subquery generation + */ + public static function subQuery() + { + return new MysqliDb(); + } + } // END class diff --git a/readme.md b/readme.md index 0de4ccd1..d8ce4ba3 100644 --- a/readme.md +++ b/readme.md @@ -216,6 +216,13 @@ $products = $db->get ("products p", null, "u.name, p.productName"); print_r ($products); ``` +### Subqueries +```php +$ids = $db->subQuery()->where("qty", 2, ">")->get("products", null, "userId"); +$db->where("id", $ids, 'in'); +$res = $db->get ("users"); +// Gives SELECT * FROM users WHERE id IN (SELECT userId FROM products WHERE qty > 2) +``` ### Helper commands Reconnect in case mysql connection died ```php From 6261af4f9ebb5f577d28aa5e9d20b31e6862925f Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Wed, 28 May 2014 19:27:26 +0300 Subject: [PATCH 11/22] Added nicer where API to define operators --- MysqliDb.php | 10 ++++++++-- readme.md | 12 ++++++------ tests.php | 8 ++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 4f99f70b..0b5a2e14 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -357,8 +357,11 @@ public function delete($tableName, $numRows = null) * * @return MysqliDb */ - public function where($whereProp, $whereValue = null) + public function where($whereProp, $whereValue = null, $operator = null) { + if ($operator) + $whereValue = Array ($operator => $whereValue); + $this->_where[$whereProp] = Array ("AND", $whereValue); return $this; } @@ -373,8 +376,11 @@ public function where($whereProp, $whereValue = null) * * @return MysqliDb */ - public function orWhere($whereProp, $whereValue = null) + public function orWhere($whereProp, $whereValue = null, $operator = null) { + if ($operator) + $whereValue = Array ($operator => $whereValue); + $this->_where[$whereProp] = Array ("OR", $whereValue); return $this; } diff --git a/readme.md b/readme.md index d8ce4ba3..efd9f142 100644 --- a/readme.md +++ b/readme.md @@ -133,23 +133,23 @@ $results = $db->get('users'); ``` ```php -$db->where('id', Array('>=' => 50)); +$db->where('id', 50, ">="); $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id >= 50; ``` BETWEEN: ```php -$db->where('id', Array('between' => Array(4, 20) ) ); -//$db->where('id', Array('not between' => Array(4, 20) ) ); +$db->where('id', Array(4, 20), 'between'); +//$db->where('id', Array(4, 20), 'not between'); $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id BETWEEN 4 AND 20 ``` IN: ```php -$db->where('id', Array( 'in' => Array(1, 5, 27, -1, 'd') ) ); -//$db->where('id', Array( 'not in' => Array(1, 5, 27, -1, 'd') ) ); +$db->where('id', Array(1, 5, 27, -1, 'd'), 'IN'); +//$db->where('id', Array(1, 5, 27, -1, 'd'), 'NOT IN'); $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id IN (1, 5, 27, -1, 'd'); ``` @@ -164,7 +164,7 @@ $results = $db->get('users'); NULL comparison: ```php -$db->where ("lastName", Array("<=>" => NULL)); +$db->where ("lastName", NULL, '<=>'); $results = $db->get("users"); // Gives: SELECT * FROM users where lastName <=> NULL ``` diff --git a/tests.php b/tests.php index 275f354c..ce2adbf1 100644 --- a/tests.php +++ b/tests.php @@ -138,7 +138,7 @@ function createTable ($name, $data) { //$users = $db->get("users"); //print_r ($users); -$db->where("firstname", Array("LIKE" => '%John%')); +$db->where("firstname", '%John%', 'LIKE'); $users = $db->get("users"); if ($db->count != 1) { echo "Invalid insert count in LIKE: ".$db->count; @@ -172,14 +172,14 @@ function createTable ($name, $data) { exit; } -$db->where ("id", Array('in' => Array('1','2','3'))); +$db->where ("id", Array('1','2','3'), 'IN'); $db->get("users"); if ($db->count != 3) { echo "Invalid users count on where() with in "; exit; } -$db->where ("id", Array('between' => Array('2','3'))); +$db->where ("id", Array('2','3'), 'between'); $db->get("users"); if ($db->count != 2) { echo "Invalid users count on where() with between"; @@ -194,7 +194,7 @@ function createTable ($name, $data) { exit; } -$db->where ("lastName", Array("<=>" => NULL)); +$db->where ("lastName", NULL, '<=>'); $r = $db->get("users"); if ($db->count != 1) { echo "Invalid users count on null where()"; From ad19105baf40a50a8cc889e10efc3266c39f3c2a Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Sat, 7 Jun 2014 14:34:34 +0300 Subject: [PATCH 12/22] Added insert subquery doc and a test --- readme.md | 48 ++++++++++++++++++++++++++++++++++++------------ tests.php | 11 +++++++++++ 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/readme.md b/readme.md index efd9f142..08463feb 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,7 @@ After that, create a new instance of the class. $db = new Mysqlidb('host', 'username', 'password', 'databaseName'); ``` -It's also possible to set a table prefix: +Its also possible to set a table prefix: ```php $db->setPrefix('tablePrefix'); ``` @@ -134,31 +134,35 @@ $results = $db->get('users'); ```php $db->where('id', 50, ">="); +// or $db->where('id', Array('>=' => 50)); + $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id >= 50; ``` -BETWEEN: +BETWEEN / NOT BETWEEN: ```php $db->where('id', Array(4, 20), 'between'); -//$db->where('id', Array(4, 20), 'not between'); +// or $db->where('id', Array('between' => Array(4, 20) ) ); + $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id BETWEEN 4 AND 20 ``` -IN: +IN / NOT IN: ```php $db->where('id', Array(1, 5, 27, -1, 'd'), 'IN'); -//$db->where('id', Array(1, 5, 27, -1, 'd'), 'NOT IN'); +// or $db->where('id', Array( 'in' => Array(1, 5, 27, -1, 'd') ) ); + $results = $db->get('users'); // Gives: SELECT * FROM users WHERE id IN (1, 5, 27, -1, 'd'); ``` OR CASE ```php -$db->where('firstName','John'); -$db->orWhere('firstName','Peter'); -$results = $db->get('users'); +$db->where ('firstName', 'John'); +$db->orWhere ('firstName', 'Peter'); +$results = $db->get ('users'); // Gives: SELECT * FROM users WHERE firstName='John' OR firstName='peter' ``` @@ -202,8 +206,8 @@ $results = $db->get('users'); ### Grouping method ```php -$db->groupBy("name"); -$results = $db->get('users'); +$db->groupBy ("name"); +$results = $db->get ('users'); // Gives: SELECT * FROM users GROUP BY name; ``` @@ -217,12 +221,32 @@ print_r ($products); ``` ### Subqueries +Subquery in selects: ```php -$ids = $db->subQuery()->where("qty", 2, ">")->get("products", null, "userId"); -$db->where("id", $ids, 'in'); +$ids = $db->subQuery (); +$ids->where ("qty", 2, ">"); +$ids->get ("products", null, "userId"); + +$db->where ("id", $ids, 'in'); $res = $db->get ("users"); // Gives SELECT * FROM users WHERE id IN (SELECT userId FROM products WHERE qty > 2) ``` + +Subquery in inserts: +```php +$userIdQ = $db->subQuery (); +$userIdQ->where ("id", 6); +$userIdQ->getOne ("users", "name"), + +$data = Array ( + "productName" => "test product", + "userId" => $userIdQ, + "lastUpdated" => $db->now() +); +$id = $db->insert ("products", $data); +// Gives INSERT INTO PRODUCTS (productName, userId, lastUpdated) values ("test product", (SELECT name FROM users WHERE id = 6), NOW()); +``` + ### Helper commands Reconnect in case mysql connection died ```php diff --git a/tests.php b/tests.php index ce2adbf1..5235174a 100644 --- a/tests.php +++ b/tests.php @@ -224,6 +224,17 @@ function createTable ($name, $data) { exit; } +$usersQ = $db->subQuery(); +$usersQ->where ("login", "user2"); +$usersQ->getOne ("users", "id"); + +$db->where ("userId", $usersQ); +$res = $db->getOne ("products", "count(id) as cnt"); +if ($res['cnt'] != 2) { + echo "Invalid select result with subquery"; + exit; +} +//TODO: insert test $db->delete("users"); $db->get("users"); if ($db->count != 0) { From f01c82ee72db22c3fa34e00d8c20b6b134a95086 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Sat, 7 Jun 2014 15:01:03 +0300 Subject: [PATCH 13/22] Bugfix: multiple conditions on the same key shouldnt overwrite previous condition --- MysqliDb.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 0b5a2e14..9b6f50de 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -362,7 +362,7 @@ public function where($whereProp, $whereValue = null, $operator = null) if ($operator) $whereValue = Array ($operator => $whereValue); - $this->_where[$whereProp] = Array ("AND", $whereValue); + $this->_where[] = Array ("AND", $whereValue, $whereProp); return $this; } @@ -381,7 +381,7 @@ public function orWhere($whereProp, $whereValue = null, $operator = null) if ($operator) $whereValue = Array ($operator => $whereValue); - $this->_where[$whereProp] = Array ("OR", $whereValue); + $this->_where[] = Array ("OR", $whereValue, $whereProp); return $this; } /** @@ -618,10 +618,11 @@ protected function _buildQuery($numRows = null, $tableData = null) if ($hasConditional) { //Prepair the where portion of the query $this->_query .= ' WHERE '; - foreach ($this->_where as $column => $value) { - //value[0] -- AND/OR, value[1] -- condition array + $i = 0; + foreach ($this->_where as $value) { + //value[0] -- AND/OR, value[1] -- condition array, value[2] -- key name // if its not a first condition insert its concatenator (AND or OR) - if (array_search ($column, array_keys ($this->_where)) != 0) + if ($i != 0) $this->_query .= ' ' . $value[0]. ' '; if (is_array ($value[1])) { @@ -663,7 +664,8 @@ protected function _buildQuery($numRows = null, $tableData = null) } else { $comparison = $this->_buildPair ("=", $value[1]); } - $this->_query .= $column.$comparison; + $this->_query .= $value[2].$comparison; + $i++; } } From e82a79619e0147fbb13b5fe7f48b2f53e13a2cdd Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Sun, 8 Jun 2014 01:51:38 +0300 Subject: [PATCH 14/22] Rename variable for better understanding --- MysqliDb.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 9b6f50de..6aed6239 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -619,21 +619,21 @@ protected function _buildQuery($numRows = null, $tableData = null) //Prepair the where portion of the query $this->_query .= ' WHERE '; $i = 0; - foreach ($this->_where as $value) { - //value[0] -- AND/OR, value[1] -- condition array, value[2] -- key name + foreach ($this->_where as $cond) { + //cond[0] -- AND/OR, cond[1] -- condition array, cond[2] -- key name // if its not a first condition insert its concatenator (AND or OR) if ($i != 0) - $this->_query .= ' ' . $value[0]. ' '; + $this->_query .= ' ' . $cond[0]. ' '; - if (is_array ($value[1])) { - //value[0] -- AND/OR, value[1] -- condition array + if (is_array ($cond[1])) { + //cond[0] -- AND/OR, cond[1] -- condition array // if the value is an array, then this isn't a basic = comparison - $key = key($value[1]); - $val = $value[1][$key]; + $key = key($cond[1]); + $val = $cond[1][$key]; switch( strtolower($key) ) { case '0': $comparison = ''; - foreach ($value[1] as $v) + foreach ($cond[1] as $v) $this->_bindParam ($v); break; case 'not in': @@ -659,12 +659,12 @@ protected function _buildQuery($numRows = null, $tableData = null) // We are using a comparison operator with only one parameter after it $comparison = $this->_buildPair ($key, $val); } - } else if ($value[1] === null) { + } else if ($cond[1] === null) { $comparison = ''; } else { - $comparison = $this->_buildPair ("=", $value[1]); + $comparison = $this->_buildPair ("=", $cond[1]); } - $this->_query .= $value[2].$comparison; + $this->_query .= $cond[2].$comparison; $i++; } } From b8c5d3c584a5e06ea245f3ab93d8cd91effa0594 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Sun, 8 Jun 2014 01:51:38 +0300 Subject: [PATCH 15/22] Rename variable for better understanding --- MysqliDb.php | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 9b6f50de..3baaee89 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -619,21 +619,19 @@ protected function _buildQuery($numRows = null, $tableData = null) //Prepair the where portion of the query $this->_query .= ' WHERE '; $i = 0; - foreach ($this->_where as $value) { - //value[0] -- AND/OR, value[1] -- condition array, value[2] -- key name + foreach ($this->_where as list($concat, $wValue, $wKey)) { // if its not a first condition insert its concatenator (AND or OR) - if ($i != 0) - $this->_query .= ' ' . $value[0]. ' '; + if ($i++ != 0) + $this->_query .= " $concat "; + $this->_query .= $wKey; - if (is_array ($value[1])) { - //value[0] -- AND/OR, value[1] -- condition array + if (is_array ($wValue)) { // if the value is an array, then this isn't a basic = comparison - $key = key($value[1]); - $val = $value[1][$key]; + $key = key($wValue); + $val = $wValue[$key]; switch( strtolower($key) ) { case '0': - $comparison = ''; - foreach ($value[1] as $v) + foreach ($wValue as $v) $this->_bindParam ($v); break; case 'not in': @@ -647,25 +645,23 @@ protected function _buildQuery($numRows = null, $tableData = null) $this->_bindParam ($v); } } - $comparison = rtrim($comparison, ',').' ) '; + $this->_query .= rtrim($comparison, ',').' ) '; break; case 'not between': case 'between': - $comparison = ' ' . $key . ' ? AND ? '; + $this->_query .= " $key ? AND ? "; $this->_bindParam ($val[0]); $this->_bindParam ($val[1]); break; default: // We are using a comparison operator with only one parameter after it - $comparison = $this->_buildPair ($key, $val); + $this->_query .= $this->_buildPair ($key, $val); } - } else if ($value[1] === null) { - $comparison = ''; + } else if ($wValue === null) { + // } else { - $comparison = $this->_buildPair ("=", $value[1]); + $this->_query .= $this->_buildPair ("=", $wValue); } - $this->_query .= $value[2].$comparison; - $i++; } } From 5e193ce050c100960a55a7442d2e1144892a01f8 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Thu, 12 Jun 2014 14:26:47 +0300 Subject: [PATCH 16/22] Added copy() method for properties sharing --- MysqliDb.php | 12 ++++++++++++ readme.md | 15 ++++++++++++++- tests.php | 5 +++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index 3baaee89..00d0dd6f 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -769,6 +769,8 @@ protected function _prepareQuery() public function __destruct() { if (!$this->isSubQuery) + return; + if ($this->_mysqli) $this->_mysqli->close(); } @@ -931,4 +933,14 @@ public static function subQuery() return new MysqliDb(); } + /** + * Method returns a copy of a mysqlidb subquery object + * + * @param object new mysqlidb object + */ + public function copy () + { + return clone $this; + } + } // END class diff --git a/readme.md b/readme.md index 08463feb..ffc27c6b 100644 --- a/readme.md +++ b/readme.md @@ -220,6 +220,20 @@ $products = $db->get ("products p", null, "u.name, p.productName"); print_r ($products); ``` +### Properties sharing +Its is also possible to copy properties +```php +$db->where ("agentId", 10); + +$customers = $common->copy (); +$res = $customers->get ("customers"); +// SELECT * FROM customers where agentId = 10 + +$db->orWhere ("agentId", 20); +$res = $db->get ("users"); +// SELECT * FROM users where agentId = 10 or agentId = 20 +``` + ### Subqueries Subquery in selects: ```php @@ -246,7 +260,6 @@ $data = Array ( $id = $db->insert ("products", $data); // Gives INSERT INTO PRODUCTS (productName, userId, lastUpdated) values ("test product", (SELECT name FROM users WHERE id = 6), NOW()); ``` - ### Helper commands Reconnect in case mysql connection died ```php diff --git a/tests.php b/tests.php index 5235174a..f6590186 100644 --- a/tests.php +++ b/tests.php @@ -228,8 +228,9 @@ function createTable ($name, $data) { $usersQ->where ("login", "user2"); $usersQ->getOne ("users", "id"); -$db->where ("userId", $usersQ); -$res = $db->getOne ("products", "count(id) as cnt"); +$db2 = $db->copy(); +$db2->where ("userId", $usersQ); +$res = $db2->getOne ("products", "count(id) as cnt"); if ($res['cnt'] != 2) { echo "Invalid select result with subquery"; exit; From a6f3e0516b38cb12d852cba2f78a8dd65d6ead63 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Thu, 12 Jun 2014 18:36:45 +0300 Subject: [PATCH 17/22] ups --- MysqliDb.php | 42 ++++++++++++++++++++++++++++++++++++++++++ readme.md | 15 +++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/MysqliDb.php b/MysqliDb.php index 00d0dd6f..e5383d10 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -215,6 +215,48 @@ public function rawQuery($query, $bindParams = null) return $this->_dynamicBindResults($stmt); } + /** + * Pass in an array of subqueries for union query construction. + * + * @param array $objects Contains a user-provided array of subqueries + * @param $type 'ALL', 'DISTINCT', null. + * + * @return array Contains the returned rows from the query. + */ + public function union ($objects, $type = null) + { + $allowedTypes = array('ALL', 'DISTINCT'); + $type = strtoupper (trim ($type)); + + if ($type && !in_array ($type, $allowedTypes)) + die ('Wrong UNION type: '.$type); + + if (!is_array ($objects)) + return; + + $this->_query = ""; + $i = 0; + foreach ($objects as $obj) { + if (!is_object ($obj)) + continue; + + if ($i++ != 0) + $this->_query .= " UNION {$type} "; + + $subQuery = $obj->getSubQuery(); + $this->_query .= "(" . $subQuery['query'] . ")"; + foreach ($subQuery['params'] as $v) + $this->_bindParam ($v); + } + $stmt = $this->_buildQuery (null); + $stmt->execute(); + $this->_stmtError = $stmt->error; + $this->reset(); + + return $this->_dynamicBindResults($stmt); + } + + /** * * @param string $query Contains a user-provided select query. diff --git a/readme.md b/readme.md index ffc27c6b..fce150f3 100644 --- a/readme.md +++ b/readme.md @@ -260,6 +260,21 @@ $data = Array ( $id = $db->insert ("products", $data); // Gives INSERT INTO PRODUCTS (productName, userId, lastUpdated) values ("test product", (SELECT name FROM users WHERE id = 6), NOW()); ``` +UNION queries +```php +$common = $db->subQuery(); +$common->where ("agentId", 10); + +$customers = $common->copy(); +$customers->get ("customers"); + +$users = $common->copy(); +$users->get ("users"); + +$db->orderBy ("lastModified"); +$res = $db->union (Array ($customers, $users, $companies), "ALL"); +GIVES (SELECT * FROM customers WHERE agentId = 10 ) UNION ALL (SELECT * FROM users WHERE agentId = 10 ) ORDER BY lastModified DESC +``` ### Helper commands Reconnect in case mysql connection died ```php From c9b6198fedcd60a6406f17ce98deb0d31c4afee7 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Thu, 12 Jun 2014 18:37:24 +0300 Subject: [PATCH 18/22] Revert "ups" This reverts commit a6f3e0516b38cb12d852cba2f78a8dd65d6ead63. --- MysqliDb.php | 42 ------------------------------------------ readme.md | 15 --------------- 2 files changed, 57 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index e5383d10..00d0dd6f 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -215,48 +215,6 @@ public function rawQuery($query, $bindParams = null) return $this->_dynamicBindResults($stmt); } - /** - * Pass in an array of subqueries for union query construction. - * - * @param array $objects Contains a user-provided array of subqueries - * @param $type 'ALL', 'DISTINCT', null. - * - * @return array Contains the returned rows from the query. - */ - public function union ($objects, $type = null) - { - $allowedTypes = array('ALL', 'DISTINCT'); - $type = strtoupper (trim ($type)); - - if ($type && !in_array ($type, $allowedTypes)) - die ('Wrong UNION type: '.$type); - - if (!is_array ($objects)) - return; - - $this->_query = ""; - $i = 0; - foreach ($objects as $obj) { - if (!is_object ($obj)) - continue; - - if ($i++ != 0) - $this->_query .= " UNION {$type} "; - - $subQuery = $obj->getSubQuery(); - $this->_query .= "(" . $subQuery['query'] . ")"; - foreach ($subQuery['params'] as $v) - $this->_bindParam ($v); - } - $stmt = $this->_buildQuery (null); - $stmt->execute(); - $this->_stmtError = $stmt->error; - $this->reset(); - - return $this->_dynamicBindResults($stmt); - } - - /** * * @param string $query Contains a user-provided select query. diff --git a/readme.md b/readme.md index fce150f3..ffc27c6b 100644 --- a/readme.md +++ b/readme.md @@ -260,21 +260,6 @@ $data = Array ( $id = $db->insert ("products", $data); // Gives INSERT INTO PRODUCTS (productName, userId, lastUpdated) values ("test product", (SELECT name FROM users WHERE id = 6), NOW()); ``` -UNION queries -```php -$common = $db->subQuery(); -$common->where ("agentId", 10); - -$customers = $common->copy(); -$customers->get ("customers"); - -$users = $common->copy(); -$users->get ("users"); - -$db->orderBy ("lastModified"); -$res = $db->union (Array ($customers, $users, $companies), "ALL"); -GIVES (SELECT * FROM customers WHERE agentId = 10 ) UNION ALL (SELECT * FROM users WHERE agentId = 10 ) ORDER BY lastModified DESC -``` ### Helper commands Reconnect in case mysql connection died ```php From 4f93e79346f0270b7d8d7c7c533c45b24138d74e Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Tue, 17 Jun 2014 03:13:04 +0300 Subject: [PATCH 19/22] Fix typo --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index ffc27c6b..f4676963 100644 --- a/readme.md +++ b/readme.md @@ -225,7 +225,7 @@ Its is also possible to copy properties ```php $db->where ("agentId", 10); -$customers = $common->copy (); +$customers = $db->copy (); $res = $customers->get ("customers"); // SELECT * FROM customers where agentId = 10 From 9e7856399c7f04df72472aa50c2191c8d7cb287f Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Thu, 19 Jun 2014 13:18:13 +0300 Subject: [PATCH 20/22] Added transaction helpers --- MysqliDb.php | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 15 +++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/MysqliDb.php b/MysqliDb.php index 00d0dd6f..cafe2a3e 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -943,4 +943,53 @@ public function copy () return clone $this; } + /** + * Begin a transaction + * + * @uses mysqli->autocommit(false) + * @uses register_shutdown_function(array($this, "_transaction_shutdown_check")) + */ + public function startTransaction () { + $this->_mysqli->autocommit (false); + $this->_transaction_in_progress = true; + register_shutdown_function (array ($this, "_transaction_status_check")); + } + + /** + * Transaction commit + * + * @uses mysqli->commit(); + * @uses mysqli->autocommit(true); + */ + public function commit () { + $this->_mysqli->commit (); + $this->_transaction_in_progress = false; + $this->_mysqli->autocommit (true); + } + + /** + * Transaction rollback function + * + * @uses mysqli->rollback(); + * @uses mysqli->autocommit(true); + */ + public function rollback () { + $this->_mysqli->rollback (); + $this->_transaction_in_progress = false; + $this->_mysqli->autocommit (true); + } + + /** + * Shutdown handler to rollback uncommited operations in order to keep + * atomic operations sane. + * + * @uses mysqli->rollback(); + */ + public function _transaction_status_check () { + if (!$this->_transaction_in_progress) + return; + + echo "rolling all back"; + $this->rollback (); + } } // END class diff --git a/readme.md b/readme.md index f4676963..64749db8 100644 --- a/readme.md +++ b/readme.md @@ -278,3 +278,18 @@ Please note that function returns SQL query only for debugging purposes as its e $db->get('users'); echo "Last executed query was ". $db->getLastQuery(); ``` + +### Transaction helpers +Please keep in mind that transactions are working on innoDB tables. +Rollback transaction if insert fails: +```php +$db->startTransaction(); +... +if (!$db->insert ('myTable', $insertData)) { + //Error while saving, cancel new record + $db->rollback(); +} else { + //OK + $db->commit(); +} +``` From fd69e015fd1da6835c36e123d9ef312c4a2b3684 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Thu, 19 Jun 2014 13:19:50 +0300 Subject: [PATCH 21/22] removed debug junk --- MysqliDb.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/MysqliDb.php b/MysqliDb.php index cafe2a3e..3be57369 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -988,8 +988,6 @@ public function rollback () { public function _transaction_status_check () { if (!$this->_transaction_in_progress) return; - - echo "rolling all back"; $this->rollback (); } } // END class From ce7d9e8bee9288d3c8456fe58107e583386c6713 Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Thu, 26 Jun 2014 17:56:46 +0300 Subject: [PATCH 22/22] rawQuery: do not escape quotes --- MysqliDb.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MysqliDb.php b/MysqliDb.php index 3be57369..fc188bd5 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -193,7 +193,7 @@ public function setPrefix($prefix = '') */ public function rawQuery($query, $bindParams = null) { - $this->_query = filter_var ($query, FILTER_SANITIZE_MAGIC_QUOTES, + $this->_query = filter_var ($query, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES); $stmt = $this->_prepareQuery();