Commit 5cf47539 authored by Alexander Butenko's avatar Alexander Butenko

buildWhere() refactoring

parent 2cffff2d
......@@ -522,25 +522,38 @@ class MysqliDb
*
* @param string Variable value
*/
protected function _bindParam($value)
{
protected function _bindParam($value) {
$this->_bindParams[0] .= $this->_determineType ($value);
array_push ($this->_bindParams, $value);
}
/**
* Helper function to add variables into bind parameters array in bulk
*
* @param Array Variable with values
*/
protected function _bindParams ($values) {
foreach ($values as $value)
$this->_bindParam ($value);
}
/**
* Helper function to add variables into bind parameters array and will return
* its SQL part of the query according to operator in ' $operator ?' or
* ' $operator ($subquery) ' formats
*
* @param Array Variable with values
*/
protected function _buildPair ($operator, $value) {
if (!is_object($value)) {
$this->_bindParam ($value);
$comparison = ' ' . $operator. ' ? ';
return $comparison;
return ' ' . $operator. ' ? ';
}
$subQuery = $value->getSubQuery();
$comparison = " " . $operator . " (" . $subQuery['query'] . ")";
foreach ($subQuery['params'] as $v)
$this->_bindParam ($v);
$subQuery = $value->getSubQuery ();
$this->_bindParams ($subQuery['params']);
return $comparison;
return " " . $operator . " (" . $subQuery['query'] . ")";
}
/**
......@@ -555,23 +568,97 @@ class MysqliDb
*/
protected function _buildQuery($numRows = null, $tableData = null)
{
$hasTableData = is_array($tableData);
$hasConditional = !empty($this->_where);
$this->_buildJoin();
$this->_buildTableData ($tableData);
$this->_buildWhere();
$this->_buildGroupBy();
$this->_buildOrderBy();
$this->_buildLimit ($numRows);
// Did the user call the "join" method?
if (!empty($this->_join)) {
foreach ($this->_join as $prop => $value) {
$this->_query .= " " . $prop . " on " . $value;
$this->_lastQuery = $this->replacePlaceHolders ($this->_query, $this->_bindParams);
if ($this->isSubQuery)
return;
// Prepare query
$stmt = $this->_prepareQuery();
// Bind parameters to statement if any
if (count ($this->_bindParams) > 1)
call_user_func_array(array($stmt, 'bind_param'), $this->refValues($this->_bindParams));
return $stmt;
}
/**
* This helper method takes care of prepared statements' "bind_result method
* , when the number of variables to pass is unknown.
*
* @param mysqli_stmt $stmt Equal to the prepared statement object.
*
* @return array The results of the SQL fetch.
*/
protected function _dynamicBindResults(mysqli_stmt $stmt)
{
$parameters = array();
$results = array();
$meta = $stmt->result_metadata();
// if $meta is false yet sqlstate is true, there's no sql error but the query is
// most likely an update/insert/delete which doesn't produce any results
if(!$meta && $stmt->sqlstate) {
return array();
}
$row = array();
while ($field = $meta->fetch_field()) {
$row[$field->name] = null;
$parameters[] = & $row[$field->name];
}
// Determine INSERT or UPDATE query
if ($hasTableData) {
// avoid out of memory bug in php 5.2 and 5.3
// https://github.com/joshcam/PHP-MySQLi-Database-Class/pull/119
if (version_compare (phpversion(), '5.4', '<'))
$stmt->store_result();
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
$x = array();
foreach ($row as $key => $val) {
$x[$key] = $val;
}
$this->count++;
array_push($results, $x);
}
return $results;
}
/**
* Abstraction method that will build an JOIN part of the query
*/
protected function _buildJoin () {
if (empty ($this->_join))
return;
foreach ($this->_join as $prop => $value)
$this->_query .= " " . $prop . " on " . $value;
}
/**
* Abstraction method that will build an INSERT or UPDATE part of the query
*/
protected function _buildTableData ($tableData) {
if (!is_array ($tableData))
return;
$isInsert = strpos ($this->_query, 'INSERT');
$isUpdate = strpos ($this->_query, 'UPDATE');
if ($isInsert !== false) {
//is insert statement
$this->_query .= '(`' . implode(array_keys($tableData), '`, `') . '`)';
$this->_query .= ' VALUES(';
}
......@@ -580,12 +667,20 @@ class MysqliDb
if ($isUpdate !== false)
$this->_query .= "`" . $column . "` = ";
// Subquery value
if (is_object ($value)) {
$this->_query .= $this->_buildPair ("", $value) . ", ";
} else if (!is_array ($value)) {
continue;
}
// Simple value
if (!is_array ($value)) {
$this->_bindParam ($value);
$this->_query .= '?, ';
} else {
continue;
}
// Function value
$key = key ($value);
$val = $value[$key];
switch ($key) {
......@@ -594,10 +689,8 @@ class MysqliDb
break;
case '[F]':
$this->_query .= $val[0] . ", ";
if (!empty ($val[1])) {
foreach ($val[1] as $v)
$this->_bindParam ($v);
}
if (!empty ($val[1]))
$this->_bindParams ($val[1]);
break;
case '[N]':
if ($val == null)
......@@ -609,33 +702,41 @@ class MysqliDb
die ("Wrong operation");
}
}
}
$this->_query = rtrim($this->_query, ', ');
if ($isInsert !== false)
$this->_query .= ')';
}
// Did the user call the "where" method?
if ($hasConditional) {
/**
* Abstraction method that will build the part of the WHERE conditions
*/
protected function _buildWhere () {
if (empty ($this->_where))
return;
//Prepair the where portion of the query
$this->_query .= ' WHERE ';
$i = 0;
// Remove first AND/OR concatenator
$this->_where[0][0] = '';
foreach ($this->_where as $cond) {
list ($concat, $wValue, $wKey) = $cond;
// if its not a first condition insert its concatenator (AND or OR)
if ($i++ != 0)
$this->_query .= " $concat ";
$this->_query .= $wKey;
$this->_query .= " " . $concat ." " . $wKey;
// Empty value (raw where condition in wKey)
if ($wValue === null)
continue;
// Simple = comparison
if (!is_array ($wValue))
$wValue = Array ('=' => $wValue);
if (is_array ($wValue)) {
// if the value is an array, then this isn't a basic = comparison
$key = key($wValue);
$key = key ($wValue);
$val = $wValue[$key];
switch( strtolower($key) ) {
switch (strtolower ($key)) {
case '0':
foreach ($wValue as $v)
$this->_bindParam ($v);
$this->_bindParams ($wValue);
break;
case 'not in':
case 'in':
......@@ -653,108 +754,58 @@ class MysqliDb
case 'not between':
case 'between':
$this->_query .= " $key ? AND ? ";
$this->_bindParam ($val[0]);
$this->_bindParam ($val[1]);
$this->_bindParams ($val);
break;
default:
// We are using a comparison operator with only one parameter after it
$this->_query .= $this->_buildPair ($key, $val);
}
} else if ($wValue === null) {
//
} else {
$this->_query .= $this->_buildPair ("=", $wValue);
}
}
}
// Did the user call the "groupBy" method?
if (!empty($this->_groupBy)) {
/**
* Abstraction method that will build the GROUP BY part of the WHERE statement
*
*/
protected function _buildGroupBy () {
if (empty ($this->_groupBy))
return;
$this->_query .= " GROUP BY ";
foreach ($this->_groupBy as $key => $value) {
// prepares the reset of the SQL query.
foreach ($this->_groupBy as $key => $value)
$this->_query .= $value . ", ";
}
$this->_query = rtrim($this->_query, ', ') . " ";
}
// Did the user call the "orderBy" method?
if (!empty ($this->_orderBy)) {
$this->_query .= " ORDER BY ";
foreach ($this->_orderBy as $prop => $value) {
// prepares the reset of the SQL query.
$this->_query .= $prop . " " . $value . ", ";
}
$this->_query = rtrim ($this->_query, ', ') . " ";
}
// Did the user set a limit
if (isset($numRows)) {
if (is_array ($numRows))
$this->_query .= ' LIMIT ' . (int)$numRows[0] . ', ' . (int)$numRows[1];
else
$this->_query .= ' LIMIT ' . (int)$numRows;
$this->_query = rtrim($this->_query, ', ') . " ";
}
$this->_lastQuery = $this->replacePlaceHolders($this->_query, $this->_bindParams);
if ($this->isSubQuery)
/**
* Abstraction method that will build the LIMIT part of the WHERE statement
*
* @param int $numRows The number of rows total to return.
*/
protected function _buildOrderBy () {
if (empty ($this->_orderBy))
return;
// Prepare query
$stmt = $this->_prepareQuery();
// Bind parameters to statement if any
if (count ($this->_bindParams) > 1)
call_user_func_array(array($stmt, 'bind_param'), $this->refValues($this->_bindParams));
$this->_query .= " ORDER BY ";
foreach ($this->_orderBy as $prop => $value)
$this->_query .= $prop . " " . $value . ", ";
return $stmt;
$this->_query = rtrim ($this->_query, ', ') . " ";
}
/**
* This helper method takes care of prepared statements' "bind_result method
* , when the number of variables to pass is unknown.
*
* @param mysqli_stmt $stmt Equal to the prepared statement object.
* Abstraction method that will build the LIMIT part of the WHERE statement
*
* @return array The results of the SQL fetch.
* @param int $numRows The number of rows total to return.
*/
protected function _dynamicBindResults(mysqli_stmt $stmt)
{
$parameters = array();
$results = array();
$meta = $stmt->result_metadata();
// if $meta is false yet sqlstate is true, there's no sql error but the query is
// most likely an update/insert/delete which doesn't produce any results
if(!$meta && $stmt->sqlstate) {
return array();
}
$row = array();
while ($field = $meta->fetch_field()) {
$row[$field->name] = null;
$parameters[] = & $row[$field->name];
}
// avoid out of memory bug in php 5.2 and 5.3
// https://github.com/joshcam/PHP-MySQLi-Database-Class/pull/119
if (version_compare (phpversion(), '5.4', '<'))
$stmt->store_result();
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
$x = array();
foreach ($row as $key => $val) {
$x[$key] = $val;
}
$this->count++;
array_push($results, $x);
}
protected function _buildLimit ($numRows) {
if (!isset ($numRows))
return;
return $results;
if (is_array ($numRows))
$this->_query .= ' LIMIT ' . (int)$numRows[0] . ', ' . (int)$numRows[1];
else
$this->_query .= ' LIMIT ' . (int)$numRows;
}
/**
......
......@@ -137,12 +137,19 @@ if ($db->count != 3) {
exit;
}
$db->where ("active", true);
$users = $db->get("users", 2);
if ($db->count != 2) {
echo "Invalid total insert count with boolean";
exit;
}
// TODO
//$db->where("createdAt", Array (">" => $db->interval("-1h")));
//$users = $db->get("users");
//print_r ($users);
$db->where("firstname", '%John%', 'LIKE');
$db->where("firstname", Array ('LIKE' => '%John%'));
$users = $db->get("users");
if ($db->count != 1) {
echo "Invalid insert count in LIKE: ".$db->count;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment