I have a form with 3 select boxes: age,room,type.
<form action="results.php" method="get">
<div class="form-group">
<select name="age">
<option value>Any</option>
<option value="1">15</option>
<option value="2">25</option>
<option value="3">30</option>
<option value="4">40</option>
</select>
</div>
<div class="form-group">
<select name="room">
<option value>Any</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
</div>
<div class="form-group">
<select name="type">
<option value>Any</option>
<option value="1">Personal</option>
<option value="2">Business</option>
</select>
</div>
</form>
What i am trying to do with PDO is to make a small search.
If all variables are empty then my condition is:
$search = $db->query("SELECT * FROM table");
If 1 of them (as example the age) is not empty then i have:
if(!empty($_GET['age'])){
$age = $_GET['age'];
$search = $db->query("SELECT * FROM table WHERE age = '$age'");
}
Now, if 2 of them are npt empty i have:
if(!empty($_GET['age']) && !empty($GET['room'])){
$age = $_GET['age'];
$room = $_GET['room'];
$search = $db->query("SELECT * FROM table WHERE age = '$age' AND room = '$room'");
}
In order to avoid all possible search combinations, how can i make a search with the term if is not empty. I had made one in the past:
if(!empty($age)){
$where = "WHERE age = '$age'";
}
if(!empty($room)){
$where .= "and room = '$room'";
}
$query = "SELECT * FROM table $where";
How can i make it happen with PDO?? :/
I'd do something like this:
$param = array();
$query = 'SELECT ... FROM t WHERE 1=1';
if(!empty($_GET['age'])){
$param['age'] = $_GET['age'];
$query .= ' AND t.age = :age';
}
if(!empty($_GET['room'])){
$param['room'] = $_GET['room'];
$query .= ' AND t.room = :room';
}
if(!empty($_GET['type'])){
$param['type'] = $_GET['type'];
$query .= ' AND t.type = :type';
}
$dbh->prepare($query)->execute($param);
You might want to separate out the prepare and the execute. Check the return from the prepare before you try calling execute. Or, configure PDO can throw an exception when an error occurs, e.g.
$dbh->setAttribute(PDO::ERRMODE_EXCEPTION);
You will need to make a query builder of some kind. You will also want to use prepared statements, rather than directly injecting user-provided input into the sql query. That might look something like this:
<?php
$search = [
'age' => 42,
'room' => 'Millyway',
];
$criteria = [];
$params = [];
foreach($search as $field => $value) {
$criteria[] = "$field = :$field";
$params[$field] = $value;
}
$where = ($criteria ? ('WHERE ' . implode(' AND ', $criteria)) : '');
$query = "SELECT * FROM tablename $where";
$stmt = $db->prepare($query);
$stmt->execute($params);
while($obj = $stmt->fetchObject()) {
// iterate over your result set
}
Given search terms as key-values in $search (which can be any column and value in the table, and will need to be populated from wherever those values come from), this code will build $criteria, a set of WHERE clause fragments (using a parameterized sql parameter name, rather than injecting the value directly), and $params, the list of parameters to be passed into the (upcoming) prepared statement.
It then builds the full WHERE clause in $where, by either combining all of the $criteria that were built, or returning an empty string. This is then added directly into the query, and the query is executed using the parameters array that was built up. You then iterate over the result set like any other PDO query.
Among others, the main benefit of using parameterized SQL over injecting variables directly is that it protects you from SQL Injection attacks.
Note that there are many ways this code could be improved. You could easily put it in a function; add complexity to allow for different types of comparisons (e.g. <> or LIKE); even use it as the basis for a more complicated query builder that allows more complicated logic such as ((age = :age AND room = :room1) OR (room = :room2)); and so on. What you do is up to the needs of your application.
Related
I am trying to filter some inputs of the user with select boxes. I am figuring if there is a better way doing this.
if(isset($_POST['action']))
{
$sql = "SELECT * FROM occasions WHERE naam IS NOT NULL";
if(isset($_POST['merk'])){
$merk = $_POST['merk'];
$merkQuery = implode(',', array_fill(0, count($merk), '?'));
$sql .= " AND merk IN(".$merkQuery.")";
}
if(isset($_POST['brandstof'])){
$brandstof = $_POST['brandstof'];
$brandstofQuery = implode(',', array_fill(0, count($brandstof), '?'));
$sql .= " AND brandstof IN(".$brandstofQuery.")";
}
//We prepare our SELECT statement.
$statement = $pdo->prepare($sql);
if(isset($_POST['merk'])){
//Execute statement.
$statement->execute(array_merge(array_values($merk)));
}
if(isset($_POST['brandstof'])){
//Execute statement.
$statement->execute(array_merge(array_values($brandstof)));
}
if(isset($_POST['merk']) && isset($_POST['brandstof']))
{
$statement->execute(array_merge(array_values($merk), array_values($brandstof)));
}
else
{
$statement->execute();
}
}
Cause if there are many select boxes that need filtering, the code would become long. I was wondering if there is a better way of filtering multiple select boxes.
Here is an example: link
I would suggest renaming the post variables; grouping them into a single two dimensional array.
<input type="checkbox" name="data[merk][bmw]" />
<input type="checkbox" name="data[merk][skoda] />
and so forth.
What this does, is it allows you to use a foreach to iterate through whatever values are checked.
$data = $_POST['data'] ?? []; // null coalesce defaults to a blank array if post var is null
foreach($data as $category=>$val) {
settype($val, 'array');
$query = implode(',', array_fill(0, count($val), '?'));
foreach($val as $k=>$v) {
$params[] = $k;
}
// DON'T DO THIS!
$sql .= " AND $category IN(".$query.")";
}
The reason you shouldn’t do it as shown is because you should never build a query with user-supplied data.
What you can do, however, is map user-supplied data with hard-coded data.
$map = [
// form value => db field
'merk' => 'MERK',
'brandstof' => 'BRANDSTOF',
// ... etc
];
and then when building your query,
$sql .= " AND $map[$category] IN($query)";
In the meantime, you have built your parameters in $params.
—-
Bottom line, what we have done is refactor the code since we were noticing things getting repeated. For example, you were having to repeat code for each occasion(?). One solution would be to continue to check each post value and call a function to calculate the ?s. But even then, it would be repetitive to type out all those isset()s.
In retrospect, it probably would have been better to do inputs like this:
<input type="checkbox" name="data[merk][]" value="bmw" />
<input type="checkbox" name="data[merk][]" value="skoda" />
This would no doubt be more intuitive, although you would still have to build the params array.
foreach($val as $v) {
$params[] = $v;
}
I have to following situation: I'm making a search page. The search page is populated with MySQL select query selectboxes. Therefor the name of the checkboxes are like name="name[]".
To show you what I'm doing, I'll include a picture:
So if I were to check 1 Availability - like Week - the query will work perfectly. But if I choose 2 Availabilities, only the models with BOTH availabilites show instead of every model that has one of both availabilities.
Here is my code:
HTML:
$return2 = $tafel->query("SELECT DISTINCT whenpossible FROM models where arttype LIKE 'Model%'");
while($row1 = $return2->fetch(PDO::FETCH_ASSOC)){
$whenp = $row1['whenpossible'];
<input type="checkbox" name="when[]" value="<?= $whenp ?>"><span class="box2"><?= $whenp ?></span>
PHP:
if (!empty($_POST['gender'])) {
$genders = $_POST['gender'];
$gender = implode(",",$genders);
} else {
$gender = "%";
}
$select = $tafel->prepare("SELECT * FROM models
WHERE whenpossible LIKE :when");
$select->bindParam(':when', $when, PDO::PARAM_STR);
$select->execute();
Does anyone have an idea how to fix this so that I can choose either 1 or multiple options?
Thank you all very much!
In code you build incorrect SQL command.
For your example SQL is
SELECT * FROM table WHERE field LIKE '%value1,%value2'
This is wrong code. Correct code can be
SELECT * FROM table WHERE field LIKE '%value1' OR field LIKE '%value2'
You should rewrite your code for build correct SQL
For those who are interested or facing the same problem, here is the answer:
In case IN or FIND_IN_SET aren't working for you, you can try this.
Implode your value like so:
if (!empty($_POST['VALUE'])) {
$value = $_POST['VALUE'];
$valueimplode = implode('|', $value)
} else {
$value = 'somethingelse'
}
| is very important for the SQL function. It acts like OR.
SQL as followed:
$query = $db->prepare("SELECT * FROM table WHERE column REGEXP(:valueimplode)");
$query->bindParam(':value', $valueimplode, PDO::PARAM_STR);
$query->execute();
You will find all the results that you need.
If you have an empty value, you can do this:
if (!empty($_POST['VALUE'])) {
$value = $_POST['VALUE'];
$valueimplode = implode('|', $value)
} else {
$value = '%' <-- Wildcard, means EVERYTHING!!!
}
Then the SQL like this:
$query = $db->prepare("SELECT * FROM table WHERE (column REGEXP(:valueimplode) OR column LIKE :value)");
$query->bindParam(':value', $valueimplode, PDO::PARAM_STR);
$query->execute();
% doesn't work with REGEXP so it will go to LIKE, which will then select everything in that column. Meaning, it will result everything.
I hope someone has good use for this!
I have a multiselect field
<select name="duration[]" id="duration" title="Duration" multiple="multiple" size="3">
<option value="1">1 Months</option>
<option value="2">2 Months</option>
<option value="3">3 Months</option>
</select>
my php code implode multiple values i.e 123 as 1,2,3 and insert it in database. The problem is that the field is not a required field and when i leave it empty it give me error (Invalid arguments passed)
My php code below
$duration = array();
$duration = $_POST['duration'];
if($duration)
{
foreach($duration as $value)
{
$months[] = $value;
}
}
$sql = "SELECT * FROM tbl_courses WHERE duration IN (".implode($months, ',').") ";
thanks in advance
The two problems you have is you try to implode on user input which may not be an array, and your code is vulnerable to SQL Injection.
To address those you should first check if it's an array with is_array(), then check if it has any elements with count(), then finally implode but use array_map() to filter the values to prevent SQL Injection. This will not only prevent SQL Injection but will prevent syntax errors in your query because strings must be quoted in an IN clause.
function getInt($i) {
return (int)$i;
}
$inClause = '';
if(isset($_POST['duration']) && is_array($_POST['duration']) && count($_POST['duration']) > 0)
{
$inClause = 'WHERE ';
$inClause .= implode(', ', array_map('getInt', $_POST['duration']));
}
$sql = "SELECT * FROM tbl_courses $inClause";
$duration = array();
$duration = $_POST['duration'];
$sql = FALSE;
if($duration&&is_array($duration))
{
foreach($duration as $value)
{
$months[] = $value;
}
$sql = "SELECT * FROM tbl_courses WHERE duration IN (".implode($months, ',').") ";
}
if($sql){
//do something with sql
}
use is_array to check if $duration is an array.
Just remove the WHERE condition if it is not required
$sql = "SELECT * FROM tbl_courses";
if (count($months)>0)
$sql .= " WHERE duration IN (".implode($months, ',').") ";
and better use isset($_POST["x"]) instead of just an if.
So let's say $_POST['duration'] is null.
$duration = array();
$duration = $_POST['duration'];
Then you don't have a value to implode on because your foreach is not going to be executed.
One solution would be to put your code into an if( $_POST['duration'] ) respectively if( count($months) )
One of options is typecasting.
$duration = (array)$_POST['duration'];
Since you use contents of $_POST['duration'] in your query be aware of SQL injection techniques.
Check that $_POST['duration'] is set and build your WHERE statement if it is, if not leave it blank:
$where = !$_POST['duration'] ? '' : 'WHERE duration IN ('.implode($_POST['duration'],',').')';
$sql = 'SELECT * FROM tbl_courses '.$where;
I have also removed some of your unnecessary variable declaration and looping around $duration.
Please note you need to sanitise your data to protect against SQL injection. The easiest way in this case would to be to loop through the values and cast them as int:
foreach($_POST['duration'] as $value) {
$months[] = (int)$value;
}
$where = !$months ? '' : 'WHERE duration IN ('.implode($months,',').')';
$sql = 'SELECT * FROM tbl_courses '.$where;
i have a database like this
prod_id prod_name catogory_1 catogory_2 catogary_3
now i want place my filter like this
<slect id="filter for cat 1">
<option vlaue="all">all</option>
<option vlaue="caps">Caps</option>
<option vlaue="shoose">shoose</option>
<option vlaue="cloths">cloths</option>
<option vlaue="bags">bags</option>
</slect>
<slect id="filter for cat 2">
<option vlaue="all">all</option>
<option vlaue="mens">mans</option>
<option vlaue="womens">wonens</option>
<option vlaue="babys">babys</option>
</slect>
<slect id="filter for cat 3">
<option vlaue="all">all</option>
<option vlaue="large">Larg</option>
<option vlaue="midum">midum</option>
<option vlaue="small">small</option>
</slect>
<div id="display_prod_list">
here goes the list
</div>
my function
<?php
function display_product() {
$query = mysql_query("SELECT `pord_id` AS `id`, `prod_name` AS `name`,
`catogory_1` AS `catogory_1`, `catogory_2` AS `catogory_2`,
`catogory_3` AS `catogory_3` FROM `porducts`");
$products = array();
while(($rows = mysql_fetch_assoc($query))!== false) {
$products[] = $rows;
}
return $pruducts;
}
?>
this function returns all products i want to change this function to return only filtered products
how can i do that? please help me. can i do this with php or i have to use jquery or something. please
tell me the best way to filter my products thanks
I don't know if this is about what you asking for, but hope it helps.
Use a WHERE clause on your MySQL query, for example:
SELECT `pord_id` AS `id`, `prod_name` AS `name`,
`catogory_1` AS `catogory_1`, `catogory_2` AS `catogory_2`,
`catogory_3` AS `catogory_3`
FROM `porducts`
WHERE catogory_1 = 'caps' AND catagory_2 = 'babys';
You could build your query condition in this way:
$conditions = array();
$condition_string = '';
if ($catagory_1 != 'all') { $conditions[] = "catagory_1 = $catagory_1 "; }
if ($catagory_2 != 'all') { $conditions[] = "catagory_2 = $catagory_2 "; }
if ($catagory_3 != 'all') { $conditions[] = "catagory_3 = $catagory_3 "; }
if ($conditions) { $condition_string = ' WHERE '. implode($conditions, ' AND '); }
$sql =
"SELECT `pord_id` AS `id`, `prod_name` AS `name`,
`catogory_1` AS `catogory_1`, `catogory_2` AS `catogory_2`,
`catogory_3` AS `catogory_3`
FROM `porducts` $condition_string ";
Just PHP
A standard HTML form that sumbits to the php script which executes query based on the filter values when the submit button is pressed.
The use the SQL WHERE clause to narrow down your results to products that match the criteria.
eg. WHERE catogory_1 = 'caps' AND catagory_2 = 'babys';
You will also need to put some logic in the php to prevent you from querying for a category when the filter value is 'all', as you can effectively ignore those categories.
Then output your results.
PHP & jQuery
You could use jQuery to populate the products part of the page with only the products that match the category filters when a filter is changed rather than reloading the page. A ajax request is made to the a php script that queries based on the current filters.
For example, all products are shown when your page loads at first then the "filter for category 1" is changed to "caps", using jQuery to detect a change event on the filter an ajax call to a php script that would run the following query.
SELECT `pord_id` AS `id`, `prod_name` AS `name`,
`catogory_1` AS `catogory_1`, `catogory_2` AS `catogory_2`,
`catogory_3` AS `catogory_3`
FROM `porducts`
WHERE catogory_1 = 'caps';
Then build your HTML based on the results and this is sent back as the ajax response and you should use it to replace the current HTML for the products.
$test=$_POST['Cities'];
foreach ($test as $t){
$query .= " AND u.bostadsort = \'".$t."\'";
}
I have this for my
<select size="6" name="Cities[]" multiple="multiple">
<option value="">All</option>
<option value="1">C1</option>
<option value="2">C2</option>
<option value="3">S3</option>
<option value="4">S4</option>
<option value="5">S5</option>
</select>
But its not right at all.
What I'm trying to do is when you pick the cities(in the search form), it puts all the values into an array.
Now I would like to have it write like this, if you e.g pick 2, 4, 5
AND u.bostadsort = '2' OR u.bostadsort = '4' OR u.bostadsort = '5'
(maybe you could do this easier to, but this is the only way i know, using "OR")
So if you picked more than 1 then it should be OR, instead of the AND i got.
Maybe I done it in a wrong way, and there is a better method doing this than foreach..
How can I do this?
You could use IN instead, which lets you supply multiple values:
if (is_array($_POST['Cities']))
{
// If multiple values were selected, handle them
$cities = array();
foreach ($_POST['Cities'] as $city)
{
if (!empty($city))
{
$cities[] = mysql_real_escape_string($city);
}
}
}
elseif (!empty($_POST['Cities']))
{
// ... or was a single, non-empty value passed in?
$cities = array(mysql_real_escape_string($_POST['Cities']));
}
if (count($cities))
{
$query .= " AND u.bostadsort IN ('" . join("', '", $cities) . "')";
}
Note that I am passing each value through mysql_real_escape_string before using it in the query; this is done to prevent SQL injection. If you are not using MySQL, the other RDBMSes have similar functions.
See http://www.ideone.com/UVXuu for an example (ideone doesn't seem to have mysql enabled, so the calls to mysql_real_escape_string are removed).
Alternately, you could use PDO's prepare and execute methods:
$cities = $_POST['Cities'];
if (count($cities))
{
$query .= " AND u.bostadsort IN (?" . str_repeat(', ?', count($cities) - 1) . ")";
}
// $db is a PDO object
$stmt = $db->prepare($query);
$stmt->execute($cities);
(Of course, this solution ignores how you build the rest of your query because you don't give it in your question, so you'd want to parameterize the other parts as well.)