I am trying to make a query having multiple select boxes, User can select one or multiple values from the drop down menu, But its not working it works only if user select one select box if user tries to select two or more select box values it is not able to display the where clause properly. I saw this code in another web site but not able to pick it properly, If some one can help in understanding or simplifying...
if ($office != '') {
$where = ($where == '') ? 'WHERE ' : 'AND ';
$where .= "adpno = '$office'";
}
if ($sector!= '') {
$where = ($where == '') ? 'WHERE ' : 'AND ';
$where .= "sector= '$sector'";
}
if ($subsector!= '') {
$where = ($where == '') ? 'WHERE ' : 'AND ';
$where .= "subsector= '$subsector'";
}
mysql_query('SELECT * FROM sometable ' . $where);
Make sure to add a space before and after AND and WHERE!
In your code when more than one condition is used, the query is probably built as: SELECT * FROM sometable WHERE subsector=3AND sector=5 (note the lack of space before AND).
In your code sample, there is a space only after AND/WHERE. Note that MySQL ignores whitespaces in the query, so don't worry if it ends up with a double space sometimes. Just make sure to have at least one space, separating all elements of the query.
EDIT: Also, make sure that each if concatenates to the previous WHERE clause instead of overwriting it. So:
if ($office != '') {
$where .= ($where == '') ? ' WHERE ' : ' AND ';
$where .= "adpno = '$office'";
}
if ($sector!= '') {
$where .= ($where == '') ? ' WHERE ' : ' AND ';
$where .= "sector= '$sector'";
}
if ($subsector!= '') {
$where .= ($where == '') ? ' WHERE ' : ' AND ';
$where .= "subsector= '$subsector'";
}
mysql_query('SELECT * FROM sometable ' . $where);
I'd try something like this:
$conditions = '';
if ($tmp = #$_GET['office'])
$conditions .= ($conditions != '' ? ' AND ' : '') . 'office = \'' . mysql_escape_string($tmp) . '\'';
if ($tmp = #$_GET['sector'])
$conditions .= ($conditions != '' ? ' AND ' : '') . 'sector = \'' . mysql_escape_string($tmp) . '\'';
// ...
mysql_query('SELECT * FROM sometable' . ($conditions != '' ? ' WHERE ' . $conditions : '') . 'M');
If you've got more than just two or three fields, a foreach loop might be better suited:
$conditions = '';
$fields = array('office', 'sector', 'subsector', /*...*/);
foreach ($fields as $field)
if ($tmp = #$_GET[$field])
$conditions .= ($conditions != '' ? ' AND ' : '') . $field . ' = \'' . mysql_escape_string($tmp) . '\'';
mysql_query('SELECT * FROM sometable' . ($conditions != '' ? ' WHERE ' . $conditions : '') . 'M');
You could even simplify code a bit by always having at least one condition (untested, but should work):
$conditions = 'TRUE';
$fields = array('office', 'sector', 'subsector', /*...*/);
foreach ($fields as $field)
if ($tmp = #$_GET[$field])
$conditions .= ' AND ' . $field . ' = \'' . mysql_escape_string($tmp) . '\'';
mysql_query('SELECT * FROM sometable WHERE ' . $conditions . ';'));
$fields = array('sector' , 'subsector' , 'office');
$query = '';
foreach($fields as $field)
{
if($query != '')
$query .= ' AND ';
if( $input[$field] != '')
$query .= $field ." = '". $input[$field] ."'";
}
I would recommend you to use mysqli or pdo instead of mysql.
$input is an array of the input values after you've made "security checks",
for instance: mysql_escape_string and htmlspecialchars
Related
I am trying to limit my db output -- which currently gives me from some categories every child and grandchild -- to only the child. So that no grandchildren are displayed.
I tried to limit the recursion to only happen once, but it doesn't work. I think the answer lies in the db query, but I am not familiar with dbs.
public function getChildCategories(&$result, &$categories, $level = 1)
{
$db = EB::db();
$ordering = $this->params->get('order', 'popular');
$sort = 'desc';
$total = (int) $this->params->get('count', 0);
$hideEmptyPost = $this->params->get('hideemptypost', false);
$language = EB::getCurrentLanguage();
foreach ($result as $row) {
// Initialize default structure
$category = EB::table('Category');
$category->bind($row);
$category->cnt = $row->cnt;
$categories[$row->id] = $category;
$categories[$row->id]->childs = array();
// Find child categories
$query = array();
$query[] = 'SELECT a.*, COUNT(' . $db->qn('b.id') . ') AS ' . $db->qn('cnt') . ',' . $db->Quote($level) . ' AS ' . $db->qn('level');
$query[] = 'FROM ' . $db->qn('#__easyblog_category') . ' AS a';
$query[] = 'LEFT JOIN ' . $db->qn('#__easyblog_post_category') . ' AS pc';
$query[] = 'ON ' . $db->qn('a.id') . '=' . $db->qn('pc.category_id');
$query[] = 'LEFT JOIN ' . $db->qn('#__easyblog_post') . ' AS b';
$query[] = 'ON ' . $db->qn('b.id') . '=' . $db->qn('pc.post_id');
$query[] = 'AND ' . $db->qn('b.published') . '=' . $db->Quote(EASYBLOG_POST_PUBLISHED);
$query[] = 'AND ' . $db->qn('b.state') . '=' . $db->Quote(EASYBLOG_POST_NORMAL);
$query[] = 'WHERE ' . $db->qn('a.published') . '=' . $db->Quote(1);
$query[] = 'AND ' . $db->qn('parent_id') . '=' . $db->Quote($row->id);
if ($language) {
$query[] = 'AND(';
$query[] = $db->qn('a.language') . '=' . $db->Quote($language);
$query[] = 'OR';
$query[] = $db->qn('a.language') . '=' . $db->Quote('');
$query[] = 'OR';
$query[] = $db->qn('a.language') . '=' . $db->Quote('*');
$query[] = ')';
}
if (!$hideEmptyPost) {
$query[] = 'GROUP BY ' . $db->qn('a.id');
} else {
$query[] = 'GROUP BY ' . $db->qn('a.id') . ' HAVING (COUNT(' . $db->qn('b.id') . ') > 0)';
}
if ($ordering == 'ordering') {
$query[] = ' ORDER BY `lft` desc';
}
if ($ordering == 'popular') {
$query[] = ' ORDER BY `cnt` desc';
}
if ($ordering == 'alphabet') {
$query[] = ' ORDER BY a.`title` asc';
}
if ($ordering == 'latest') {
$query[] = ' ORDER BY a.`created` desc';
}
$query = implode(' ', $query);
$db->setQuery($query);
$children = $db->loadObjectList();
$ccounter = 0;
// Recursion happens here
if ($children) {
$this->getChildCategories($children, $categories[$row->id]->childs, ++$level);
}
}
}
Can someone explain to me how to modify the db query? The output now is:
Category
-Child Cat
--Grandchild Cat
-Child Cat
I want:
Category
-Child Cat
-Child Cat
The comment from the user mickmackusa solves the problem. His solution works perfectly!
if ($children && $level < 2) {
$this->getChildCategories($children, $categories[$row->id]->childs, $level + 1);
}
I'm attempting to replace a chunk of code that has hard-coded field names with one that gets the field names dynamically from the query and have it mostly working but only up to a point, then I get an error. This is the code that I am trying to replace with a foreach loop of field names which creates the WHERE clause:
if (strlen($Where) == 0 ) $Where .= " WHERE (";
$Where .= "(" . $StorageArea . ")";
if (strlen($Where) > 0 ) $Where .= " AND ";
$Where .= " (" . $StorageArea . ")";
if (strlen($Location))
$Where .= " OR (". $Location .")";
if (strlen($Size))
$Where .= " OR (". $Size .")";
if (strlen($Winery))
$Where .= " OR (". $Winery .")";
if (strlen($Brand))
$Where .= " OR (". $Brand .")";
if (strlen($Appellation))
$Where .= " OR (". $Appellation .")";
if (strlen($ReleaseYear))
$Where .= " OR (". $ReleaseYear .")";
if (strlen($Varietal))
$Where .= " OR (". $Varietal .")";
if (strlen($Status))
$Where .= " OR (". $Status .")";
if (strlen($CountryName))
$Where .= " OR (". $CountryName .")";
$Where .= " ) ";
Unless there is a typo that I've missed, I don't see the problem here in this (so far untested) foreach loop so can can anyone please advise? TO keep it simply and to avoid lots of if else it uses both single line conditionals and more conventional if elseif statements using : and endif rather than { and }. Is there some limitation of this type of conditional? I had always thought that they were directly interchangeable but apparently not.
$i = 1;
$len = count(array_keys($queryField));
foreach (array_keys($queryField) as $row) :
$i++:
if ($row === 'ID') continue;
if ($i == 1) :
if (strlen($Where) == 1) $Where .= " WHERE (";
$Where .= " (" . $$row . ") ";
if (strlen($Where) > 1) $Where .= " AND ";
elseif ($i > 1 && $i != $len) :
if (strlen($$row)) $Where .= " OR (". $$row .")";
elseif ($i == $len) :
if (strlen($$row)) $Where .= " OR (". $$row .")";
$Where .= ")";
endif;
endforeach;
The error is Parse error: syntax error, unexpected ':' which it is giving on the elseif ($i > 1 && $i != $len) : line. I'm not sure yet if this will even do what I want but I can't try it until I get it working.
$queryField is a query with results as below and the function grabs the field names from it
Array
(
[ID] => 3
[StorageArea] => CAB-1
[Size] =>
[Winery] => Name
[Brand] => Fanciful Name
[Appellation] =>
[ReleaseYear] => 2008
[Varietal] => Cuvée
[Status] => Library
[CountryName] =>
)
Thanks to the comments and answer received to get the above working, I realized it does not do what I had expected so I rethought it and came up with something completely different that does work. Since it's not exactly an answer, I am posting it here as a quasi-answer and the fun part was in trying to know how many times the inner foreach was looped. There are other sections to this too for All Keywords or Exact Phrase but this bit is very similar to those so no need to post all of them. $Where is being declared earlier in the function from which this code was extracted so it's not shown here.
For my basic question about mixing conditional types, apparently mixing if and endif with single line conditionals isn't possible and that { and } are recommended for non-HTML-based programming but I really wanted to keep everything the same so for this version, all are single-line conditionals within the foreach loops so no errors.
$keywords = explode(" ", trim($keyword));
// Get number of passes for inner loop
// Subtract one because ID column is not being used
$len = ((count(array_keys($queryField))-1) * count($keywords));
$i = - (count($keywords)); // Subtract number of keywords
if (strlen($keyword)) :
// Any Keywords
foreach ($keywords as $keyword) :
$keyword = str_replace("'", "''", trim($keyword));
foreach (array_keys($queryField) as $column) :
$i++;
if ($column === 'ID') continue;
if (strlen($Where) == 0) $Where .= " WHERE (";
if ($i != $len) $Where .= "(`$column` LIKE '%" . $keyword . "%') OR ";
if ($i == $len) $Where .= "(`$column` LIKE '%" . $keyword . "%') ";
endforeach;
endforeach;
endif;
There are a few typos there but most of all for this to work you'd have to break up your code like so:
<?php
$i = 1;
$len = count(array_keys($queryField));
?>
<?php
foreach (array_keys($queryField) as $row) :
$i++;
if ($row === 'ID') continue;
?>
<?php
if ($i == 1) :
if (strlen($Where) == 1) $Where .= " WHERE (";
$Where .= " (" . $$row . ") ";
if (strlen($Where) > 1) $Where .= " AND ";
?>
<?php
elseif ($i > 1 && $i != $len) :
if (strlen($$row)) $Where .= " OR (". $$row .")";
?>
<?php
elseif ($i == $len) :
if (strlen($$row)) $Where .= " OR (". $$row .")";
$Where .= ")";
?>
<?php
endif;
endforeach;
?>
Unfortunately it's not possible to mix conditional statements as shown in your code.
If this code isn't supposed to be used in an HTML template i suggest sticking to {} brackets:
$i = 1;
$len = count(array_keys($queryField));
foreach (array_keys($queryField) as $row) {
$i++;
if ($row === 'ID') continue;
if ($i == 1) {
if (strlen($Where) == 1) $Where .= " WHERE (";
$Where .= " (" . $$row . ") ";
if (strlen($Where) > 1) $Where .= " AND ";
}
elseif ($i > 1 && $i != $len)
if (strlen($$row)) $Where .= " OR (". $$row .")";
elseif ($i == $len) {
if (strlen($$row)) $Where .= " OR (". $$row .")";
$Where .= ")";
}
}
I am attempting to dynamically create an INSERT statement based on JSON key/value pairs where the $key is the database field of string or integer data type and $value is an integer or string. I haven't had issues inserting numeric strings into Postgres before but it is failing.
Example:
$json = '{"stringField":"string","intString":"42"}';
$columns = $values = '';
foreach (json_decode($json, true) as $key => $value) {
if ($value != NULL) {
$columns .= $key . ', ';
$values .= "'" . $value . "', ";
}
}
$query = ('INSERT INTO table ('.rtrim($columns, ', ').') VALUES ('.trim($values, ', ').');');
This is cleaner PHP:
$json = '{"stringField":"string","numberField":"42"}';
$columns = $values = '';
foreach (json_decode($json, true) as $key => $value) {
if ($value !== NULL) {
$columns .= $key . ', ';
$values .= is_numeric($value) ? $value : "'" . $value . "', ";
}
}
$query = 'INSERT INTO table ('.rtrim($columns, ', ').') VALUES ('.trim($values, ', ').');';
Please think about escaping your values.
The issue turned out to be that one of the values was actually a float numeric string failing on insert into an integer field, rounding the value if it is a numeric string solves this. The is_numeric check avoids string fields being converted to 0.
Solution:
$json = '{"stringField":"string","floatString":"42.0","intString":"42"}';
$columns = $values = '';
foreach (json_decode($json, true) as $key => $value) {
if ($value != NULL) {
$columns .= $key . ', ';
$values .= is_numeric($value) ? round($value) . "," : "'" . $value . "', ";
}
}
$query = ('INSERT INTO table ('.rtrim($columns, ', ').') VALUES ('.trim($values, ', ').');');
I am trying to create a class to save time on cleaning up my variables before sending them to the database to prevent sql injections. The basic systems is working now but i cant seem to get a where/or statement implemented. Does anyone know how to add this?
<?php
class Database {
private $db = '';
private $database = '';
function __construct($settings) {
$this->db = new mysqli('127.0.0.1', $settings['mysql_user']['username'], $settings['mysql_user']['password']);
$this->database = $settings['mysql_user']['database'];
print_r('Database Loaded!<br/>');
}
public function query($method, $database, $rows, $params, $where = array(), $or = array()) {
$count = 0;
$amount = count($rows);
$final_rows = '';
$final_data = '';
$bind_names = array();
$bind_names[0] = '';
$param_types = array(
"int" => "i",
"string" => "s",
"double" => "d",
"blob" => "b"
);
switch($method) {
case 'INSERT':
foreach ($rows as $row) {
$count = $count + 1;
$final_rows .= '`' . $row . '`' . ($count != $amount ? ', ' : '');
$final_data .= '?' . ($count != $amount ? ', ' : '');
}
$stmt = $this->db->prepare('INSERT INTO `' . $this->database . '`.`' . $database . '` (' . $final_rows . ') VALUES (' . $final_data . ')');
for ($i = 0; $i < count($params); $i++)
{
$bind_name = 'bind'.$i;
$$bind_name = $params[$i][1];
$bind_names[0] .= $param_types[$params[$i][0]];
$bind_names[] = &$$bind_name;
}
call_user_func_array( array ($stmt, 'bind_param'), $bind_names);
return $stmt->execute();
break;
case 'UPDATE':
foreach ($rows as $row) {
$count = $count + 1;
$final_rows .= '`' . $row . '`' . ($count != $amount ? ', ' : '');
$final_data .= '?' . ($count != $amount ? ', ' : '');
}
$stmt = $this->db->prepare('UPDATE `' . $this->database . '`.`' . $database . '` SET ' . $final_rows . '');
for ($i = 0; $i < count($params); $i++)
{
$bind_name = 'bind'.$i;
$$bind_name = $params[$i][1];
$bind_names[0] .= $param_types[$params[$i][0]];
$bind_names[] = &$$bind_name;
}
call_user_func_array( array ($stmt, 'bind_param'), $bind_names);
return $stmt->execute();
break;
case 'REPLACE':
foreach ($rows as $row) {
$count = $count + 1;
$final_rows .= '`' . $row . '`' . ($count != $amount ? ', ' : '');
$final_data .= '?' . ($count != $amount ? ', ' : '');
}
$stmt = $this->db->prepare('REPLACE INTO `' . $this->database . '`.`' . $database . '` (' . $final_rows . ') VALUES (' . $final_data . ')');
for ($i = 0; $i < count($params); $i++)
{
$bind_name = 'bind'.$i;
$$bind_name = $params[$i][1];
$bind_names[0] .= $param_types[$params[$i][0]];
$bind_names[] = &$$bind_name;
}
call_user_func_array( array ($stmt, 'bind_param'), $bind_names);
return $stmt->execute();
break;
}
}
}
?>
Going to make a few assumptions, but first I'll recommend you use an ORM before whipping up your own solution. Here's a good list of PHP libraries (I've linked to the database sections, which includes some very well done stand-alone ORMs https://github.com/ziadoz/awesome-php#database)
That being said I'm going to assume the $where and $or arrays are both for the WHERE construct and the items in $where are combined via AND and the $or is combined via OR.
Because you didn't describe what kind of output you were looking for I'm also assuming your $where and $or are key/value pairs which translates to "key=value AND key=value AND (key=value OR key=value)".
DISCLAIMER: This example is kind of hacky, but is the shortest/simplest way to get the example across.
$whereQuery = '';
foreach ($where as $key => $value) {
$whereQuery .= "$key = $value AND";
}
if ($or !== array()) {
$whereQuery .= '(';
foreach ($or as $key => $value) {
$whereQuery .= "$key = $value OR";
}
}
if ($whereQuery !== '') {
if (($temp = strlen($whereQuery) - strlen('AND')) >= 0 && strpos($whereQuery, 'AND', $temp) !== false) {
$whereQuery = substr($whereQuery, -4);
} else {
$whereQuery = substr($whereQuery, -3) . ')';
}
$whereQuery = "WHERE $whereQuery";
}
You can then stick the $whereQuery at the end of an UPDATE or SELECT. Even if $where and $or are empty it'll still work.
You could move the loops into functions and make it recursive if the $value was another array so you could create more complex WHERE statements.
I was wondering how I could make a general function for SELECT mysql queries from my SelectQuery object in PHP. SelectQuery extends Query, which means it inherits the database connection, a realescape method (which is mysqli_real_escape_string()), and a query method which executes the query. Besides that, it also gets a protected variable called _sql, which is the SQL the query() method passes to the database. And it also gets a protected variable called _table, which contains the (escaped) name of the table it's working on.
My code:
public function select($columns = array('*'), $known = null, $limit = null, $offset = null, $orderby = null, $asc = true) {
if (!is_array($columns)) {
new Error('Parameter is not an array.');
return;
}
$select = '';
foreach($columns as $column) {
$select .= (($select != '')?', ':'') . '`' . $this->realescape($column) . '`';
}
$conditions = '';
if (is_array($known)) {
foreach($known as $column => $value) {
$conditions .= (($conditions != '')?' AND ':'WHERE ') . '`' . $this->realescape($column) . '` = ' . ((is_string($value))?'\'':'') . $this->realescape($value) . ((is_string($value))?'\'':'');
}
}
$domain = '';
if ($limit !== null) {
$domain = 'LIMIT ' . $this->realescape($limit);
if ($offset !== null) {
$domain .= ' OFFSET = ' . $this->realescape($offset);
}
}
$order = '';
if ($orderby !== null) {
$order = 'ORDER BY `' . $this->realescape($orderby) . '` ' . (($asc)?'ASC':'DESC');
}
$this->_sql = 'SELECT ' . $select . ' FROM `' . $this->_table . '`';
if ($conditions != '') {
$this->_sql .= ' ' . $conditions;
}
if ($domain != '') {
$this->_sql .= ' ' . $domain;
}
if ($order != '') {
$this->_sql .= ' ' . $order;
}
return $this->query();
}
The $known variable might be set, if set it should be an array which contains all the 'known' elements of the rows we are selecting.
My question: How can I make this so that conditions such as
age < 18, or date > 5120740154 are easily made?
Also, if you think the way I'm making this work is wrong, please say so.
Thanks in advance
Dynamically build WHERE conditions that can use any operators
Create a $where array that holds your where conditions.
$where = array (
'age <' => 18,
'date >' => 5120740154
);
You can build that into your SQL query using a function like this:
private function buildWhereConditions($where) {
$conditions = 'WHERE 1=1';
/*1=1 is just so you can easily append ANDs */
foreach ($where as $key => $value) {
$conditions .= " AND $key $value ";
}
return $conditions;
}
This will return the where conditions to be appended to your query:
WHERE 1=1 AND age < 18 AND date > 5120740154