Suppose my URL is http://something.com/products.php?brand=samsung&condition=new
For the above query I am using isset() and $_GET[]) functions along with lots of if-else statements in PHP to generate a sql query for displaying the products which satisfy the search criteria.
For example: if I am dealing with only brand and condition parameters then this is how I will generate the query:
$sql = "select * from products where 1=1 ";
if(isset($_GET['brand']))
{
if(isset($_GET['condition']))
{
$sql = $sql + "and brand=".$_GET['brand']." and condition=".$_GET['condition'];
}
}
else
{
if(isset($_GET['condition']))
{
$sql = $sql + "and condition=".$_GET['condition'];
}
else
{
$sql = $sql + ";";
}
}
Now suppose my URL is having 10 parameters (or more). In this case, using if-else is not at all good. How can I generate the query without using so many if-else statements? Is there any better method/script/library available for doing this thing?
There are a number of ways to do this, but the easiest way would be to loop through the acceptable columns and then append appropriately.
// I generally use array and implode to do list concatenations. It avoids
// the need for a test condition and concatenation. It is debatable as to
// whether this is a faster design, but it is easier and chances are you
// won't really need to optimize that much over a database table (a table
// with over 10 columns generally needs to be re-thought)
$search = array();
// you want to white-list here. It is safer and it is more likely to prevent
// destructive user error.
$valid = array( 'condition', 'brand' /* and so on */ );
foreach( $valid as $column )
{
// does the key exist?
if( isset( $_GET[ $column ] ) )
{
// add it to the search array.
$search[] = $column . ' = ' . mysql_real_escape_string( $_GET[ $column ] );
}
}
$sql = 'SELECT * FROM TABLE_NAME WHERE ' . implode( ' AND ', $search );
// run your search.
If you really are trying to get rid of the 'if' statements, you could use this:
$columns = array_intersect( $valid, array_keys( $_GET ) );
foreach( $columns as $column )
{
$search[] = $column . ' = ' . mysql_real_escape_string( $_GET[ $column ] );
}
$sql = 'SELECT * FROM TABLE_NAME WHERE ' . implode( ' AND ', $search );
But you may want to run actual benchmarks to determine whether that is a substantially better option.
Related
Im a new and just learning php. I have a data table with search boxes with this code.
$condition = '';
if(isset($_REQUEST['username']) and $_REQUEST['username']!="") {
$condition .= ' AND username LIKE "%'.$_REQUEST['username'].'%" ';
}
if(isset($_REQUEST['useremail']) and $_REQUEST['useremail']!=""){
$condition .= ' AND useremail LIKE "%'.$_REQUEST['useremail'].'%" ';
}
What I need is to search with both username AND useremail. I have attempted everything I know and spent a few hours searching for a solution but with no success.
You could write a complicated set of IF's with equals and not equals tests all over the place, but as the list of test gets bigger the IF's get almost impossible to maintain or understand. So it might be simpler to just build and array of things to AND in the query
$condition = '';
$and = []; #init the array
if(isset($_REQUEST['username']) and $_REQUEST['username']!="") {
$and[] = ['name' => 'username', 'value' => $_REQUEST['username'] ];
}
if(isset($_REQUEST['useremail']) and $_REQUEST['useremail']!=""){
$and[] = [''name' => 'useremail', 'value' => $_REQUEST['useremail'] ];
}
#now build the condition string
foreach ($and as $i => $andMe) {
if ( $i != 0 ){
// AND required here
$condition .= ' AND ';
}
$condition .= $andMe['name'] . ' = ' . $andMe['value'];
}
Also I have replaced the LIKE with an = as it seems more appropriate, I assume you dont ask people to enter something a bit like there user name and email, but in fact ask for the actual username or email
Of course that would still be susceptible to SQL Injection Attack So a better solution would be
#now build the condition string
foreach ($and as $i => $andMe) {
if ( $i != 0 ){
// AND required here
$condition .= ' AND ';
}
$condition .= $andMe['name'] . ' = ?';
}
And then prepare the query and use the value part to bind to the parameters.
Issue is you have leading AND in your query.
push condition to array then join conditions.
like that
$condition_array = [];
if(isset($_REQUEST['username']) and $_REQUEST['username']!="") {
$condition_array[] = 'username LIKE "%'.$_REQUEST['username'].'%" ';
}
if(isset($_REQUEST['useremail']) and $_REQUEST['useremail']!=""){
$condition_array[] = 'useremail LIKE "%'.$_REQUEST['useremail'].'%" ';
}
$condition = implode(" AND ",$condition_array);
You can create an array of all the keys that are to be searched. Then, create a new array and collect all conditions. Implode them in the end with AND as the glue. This way, query is made correctly without needing to add 100 different if conditions.
Use PDO objects to avoid SQL injection attacks. In the below snippet, if you ever need to add 1 more column for search, just add it in the below $keys array and rest works as usual without needing any further refactoring.
Snippet:
<?php
$keys = ['username', 'useremail'];
$conditions = [];
$placeholders = [];
foreach($keys as $key){
if(!empty($_REQUEST[ $key ])){
$conditions = " $key LIKE ?";
$placeholders[] = '%' . $_REQUEST[ $key ] . '%';
}
}
// you need to create $mysqli object here
if(count($conditions) === 0){
$stmt = $mysqli->prepare('select * from table');
$stmt->execute();
// rest of your code
}else{
$stmt = $mysqli->prepare('select * from table where '. implode(" AND ", $conditions));
$stmt->bind_param(str_repeat('s', count($placeholders)), ...$placeholders);
$stmt->execute();
// rest of your code
}
The outcome I seek is to loop through items taken from a form that isset or true and then have a sql statement query the database. The complication comes from I have 9 fields that the user could choose one or more or all fields (It is a search database but every time you choose an extra field it refines the search). The part of the code to return true I can get my head around by using a foreach loop but how would you link it to SQL query if the outcome is varied?
You generate a different query depending on which fields are entered.
Luckily this isn't too hard in SQL: all those fields are in the WHERE clause:
$where = [ 'foo' => 'bar', 'baz' => 0 ];
$sth = $db->prepare( "SELECT * FROM $table WHERE " .
implode( " AND ",
array_map( function($i) { return "$i=?"; }, array_keys( $where ) ) )
);
$sth->execute( array_values( $where ) );
Of course, if there are relationships between the fields, the query may become more complicated, but this is the gist of it.
Learning this takes time and patience I have cut past the variables from the form
if(isset($_POST['Search'])){
$packID = $_POST['packID'];
$supplier_name = $_POST['supplier_name'];
$timber_species = $_POST['timber_species'];
$timber_product = $_POST['timber_product'];
$timber_grade = $_POST['timber_grade'];
$timber_finish = $_POST['timber_finish'];
$timber_treatment = $_POST['timber_treatment'];
$width = $_POST['width'];
$thickness = $_POST['thickness'];
$length = $_POST['length'];
$markup = $_POST['markup'];
} else{
$packID="";
$supplier_name="";
$timber_species="";
$timber_product="";
$timber_grade="";
$timber_finish="";
$timber_treatment="";
$width= "";
$thickness= "";
$length="";
}
How would you write this Kenney when the variables may or may not be set. I must admit I am a novice and keen to learn. It takes time and patience.
I'm writing a query that uses input from a search form where Brand, Type and Price are optional input fields:
SELECT * FROM `database` WHERE `brand` LIKE "%' . $brand . '%" AND `type` LIKE "%' . $type. '%" AND `price` LIKE "%' . $price . '%"
I am wondering if there is a way to say 'all' if nothing is entered into one of the fields. For example if they do not enter a value in the price field is there a way to tell SQL to just say ignore that section, eg:
AND `price` LIKE "*";
So the reuslts are still filtered by Brand and Type but can have any Price.
Any advice on this is appreciated! Thanks
As Ariel mentioned, it would be better to have PHP do the filtering as you build the query. Here's a code sample for doing it that way:
<?php
$sql = 'SELECT * FROM `database`';
$where = array();
if ($brand !== '') $where[] = '`brand` LIKE "%'.$brand.'%"';
if ($type !== '') $where[] = '`type` LIKE "%'.$type.'%"';
if ($price !== '') $where[] = '`price` LIKE "%'.$price.'%"';
if (count($where) > 0) {
$sql .= ' WHERE '.implode(' AND ', $where);
} else {
// Error out; must specify at least one!
}
// Run $sql
NOTE: Please, please, please make sure that the $brand, $type, and $price variable contents are sanitized before you use them this way or you make yourself vulnerable to SQL injection attacks (ideally you should be using the PHP PDO database connector with prepared statements to sanitize the input).
Normally you do that in the front end language, not SQL.
But price LIKE '%' does, in fact, mean all (except for NULLs). So you are probably fine.
If you have your form fields organized, you can do something like:
<?php
$fields = array(
// Form // SQL
'brand' => 'brand',
'type' => 'type',
'price' => 'price',
);
$sql = 'SELECT * FROM `database`';
$comb = ' WHERE ';
foreach($fields as $form => $sqlfield)
{
if (!isset($_POST[$form]))
continue;
if (empty($_POST[$form]))
continue;
// You can complicate your $fields structure and e.g. use an array
// with both sql field name and "acceptable regexp" to check input
// ...
// This uses the obsolete form for mysql_*
$sql .= $comb . $sqlfield . ' LIKE "%'
. mysql_real_escape_string($_POST[$form])
. '"';
/* To use PDO, you would do something like
$sql .= $comb . $sqlfield . 'LIKE ?';
$par[] = $_POST[$form];
*/
$comb = ' AND ';
}
// Other SQL to go here
$sql .= " ORDER BY brand;";
/* In PDO, after preparing query, you would bind parameters
- $par[0] is value for parameter 1 and so on.
foreach($par as $n => $value)
bindParam($n+1, '%'.$value.'%');
*/
ok so I've been trying for a while now to get this to work but there has to be a better solution than what im thinking about. I'm fairly new to php/mysql so not sure how to do the following:
I have a search box that contains dropdowns for country, state, city
Now if the user only selects country and clicks on search it needs to filter the select by just country and show everything else.
if(!empty($_REQUEST['city']))
$city = $_REQUEST['city'];
else
$city= "%";
if(!empty($_REQUEST['state']))
$state= $_REQUEST['state'];
else
$state= "%";
if(!empty($_REQUEST['country']))
$country= $_REQUEST['country'];
select * from table where country = $country and state = $state and city = $city
problem with this is that those columns are ints so I can't use the "%" to filter it. I hope I was able to explain it any help is more than welcome. Thanks in advance
If you don't want to constrain a column, simply omit it from your query
never insert a string from $_REQUEST directly into a query string -- classic SQL injection flaw.
you probably want to enforce some sort of limit, lest the query return every single result in your database.
example:
<?php
$conditions = array();
if(!empty($_REQUEST['city']))
$conditions[] = "city = " . mysql_real_escape_string($_REQUEST['city']);
if(!empty($_REQUEST['state']))
$conditions[] = "state = " . mysql_real_escape_string($_REQUEST['state']);
if(!empty($_REQUEST['country']))
$conditions[] = "country = " . mysql_real_escape_string($_REQUEST['country']);
$sql = 'select * from table ';
if(!empty($conditions))
$sql .= ' where '. implode(' AND ', $conditions);
$sql .= ' LIMIT 1000';
$where = array();
if(!empty($_REQUEST['city'])) $where[] = "city = '".(int)$_REQUEST['city']."'";
if(!empty($_REQUEST['state'])) $where[] = "state = '".(int)$_REQUEST['state']."'";
if(!empty($_REQUEST['country'])) $where[] = "country = '".(int)$_REQUEST['country']."'";
$wherestring = if(count($where) != 0) ? " WHERE ".implode(' AND ', $where) : "" ;
$query = "SELECT * FROM table".$wherestring;
You may want to consider writing several query strings, one for just country, one for state and country and one for city, state and country. Alternatively you can assemble the query string based upon the different parameters you have to work with.
Example:
if(isset() || isset() || isset() ) //make sure at least one is set
{
$query_string = "SELECT * FROM table WHERE ";
if(isset($_REQUEST['country']))
{
$country = $_REQUEST['country'];
$query_string .= " country = $country";
}
if(isset($_REQUEST['state']))
{
$state = $_REQUEST['state'];
$query_string .= " state = $state";
}
if(isset($_REQUEST['city']))
{
$city = $_REQUEST['city'];
$query_string .= " city = $city";
}
}
else
{
//Else, if none are set, just select all the entries if no specifications were made
$query_string = "SELECT * FROM table";
}
//Then run your query...
So in english, the first thing you do is check your parameters, making sure you have something to work with before you try and concatenate empty variables together.
Then you make the base query string (as long as we have parameters) and leave it open ended so that we can add whatever parameters you need.
Next check each parameter, and if it is set, then concatenate that parameter onto the end of the query string.
Finally process the query by sending it to the SQL server.
Good luck!
h
Here're my suggestions.
I'm giving you an answer, even though you have three already. I'm thinking mine may be easier on the code-eyes.
Do not use the raw $_REQUEST value, as it's likely that the user can poison your database by feeding it fake $_REQUEST data. Though there may be better ways to do it, keep in mind the command "mysql_real_escape_string($string)".
A common method I've seen for solving this problem is written below. (The implode idea, basically. Frank Farmer does it as well in his.)
-
$__searchWheres = array(); //Where we'll store each requirement used later
foreach( array('city','state','country') as $_searchOption) {
if ( ! empty( $_REQUEST[$_searchOption] ) ) {
$__searchWheres[] = $_searchOption . '= "' . mysql_real_escape_string( $_REQUEST[$_searchOption] ) . '"';
}
}
$__query = 'select * from table' . (count($__searchWheres) > 0 ? ' WHERE ' . implode(' AND ',$__searchWheres) : ''); //Implode idea also used by Frank Farmer
//Select from the table, but only add the 'WHERE' key and where data if we have it.
mysql_query($__query);
I have a form that is going to be used to search through a table of support tickets.
the user can search from a few difficult optional fields.
Date (to/from)
Ticket Status
Engineer
Ticket Contact
I'm wondering what is the best way to deal with optional search filters. So I have a query that takes in parameters from the user. So if the user searches using both the from and to dates then the query would want to include BETWEEN. So do I have to write a different query for if the user searches for only from. or another query when the user has not added any date parameters? Then what if the status dropdown is blank? Is that another query?
Any help to clear this up would be great!
Jonesy
Build your query in parts. Start with whatever is constant in your query, and add on more SQL depending on what extra conditions:
$query = "SELECT ...
FROM ...
WHERE [where conditions that are always going to be present]";
if (isset($_POST['date_from']) && isset($_POST['date_to']))
{
$query .= ... // query code for dealing with dates
}
if (isset($_POST['status']))
{
$query .= ... // deal with status
}
// etc.
// Once you have your query fully built, execute it
$result_set = mysql_query($query);
This code is obviously just a skeleton, but that's how I would construct my query.
Hard to say without knowing what sort of DB abstraction you're using, but assuming you're hand-writing the SQL, it's fairly simple, just build up sections of your where clause individually for each variable. (Assuming here that your vars are already escaped/quoted.)
$where_clause = array();
if (!empty($date_from)) {
$where_clause[] = "table.date >= $date_from";
}
if (!empty($date_to)) {
$where_clause[] = "table.date <= $date_to";
}
if (!empty($status)) {
$where_clause[] = "status = $status";
}
$query = 'select * from table where ' . join(' and ', $where_clause);
This is an elegant way that I use alot and wish will help you too:
$q = 'SELECT * FROM Users';
$buildQ = array();
if (empty($idOrName) === false) {
$buildQ[] = '(userid = "' . $idOrName . '" OR username LIKE "%' . $idOrName. '%")';
}
if (empty($nickname) === false) {
$buildQ[] = 'nickname="' . $nickname . '"';
}
if (empty($salary) === false) {
$buildQ[] = 'salary="' . $salary . '"';
}
// ... any other criterias like above if statements
if (count($buildQ) === 1) {
$q .= ' WHERE ' . $buildQ[0];
} else if (count($buildQ) > 1) {
$count = 0;
foreach ($buildQ as $query) {
if ($count === 0) {
$q .= ' WHERE ' . $query;
} else {
$q .= ' AND ' . $query;
}
$count++;
}
}
I think it would be better if You generate query dynamically at runtime based on which fields are filled. So You could make some helper which appends specific query fragments if only one date is passed and the other one is null, or when both are passed and so on.