Dynamic MySQL query - php

I'm trying to create a dynamic sql query that compares my cat column to whatever the user entered in a form. The idea is that I will be able to take a dynamic array of values and then compare them to the cat column. This is what I tried to do:
// Loop to get the array of values from form
$get_arr = $_GET;
foreach ($get_arr as $get) {
$var = "AND cat LIKE $get";
}
// SQL query
$sql = "SELECT * FROM items
WHERE title LIKE 'this'
AND description LIKE 'that'
'%$var%'";
It doesn't work -- $var always show up blank. What would the solution be?

You have several problems.
You're not escaping the input, so you're subject to SQL injection or syntax errors.
You need to put quotes around the LIKE parameter.
You're overwriting $var each time through the loop instead of appending to it.
You're not putting any spaces around the expression.
You're putting % around the whole $var, it should be inside the LIKE parameter.
foreach ($get_arr as $get) {
$get = mysqli_real_escape_string($conn, $get);
$var .= " AND cat like '%$get%'";
}
$sql = "SELECT * FROM items
WHERE title LIKE '%this%'
AND description LIKE '%that%'
%var";

Related

How to use more than 1 quotation using getRows method and .implode in sql Query, using PHP

I'm trying to get all records from db using getRows method, to achieve that I need to implode special characters.
In previous function, to get $ids I used:
foreach ($ids as &$id) $id = (int)$id;
$rows = $this->db->getRows('SELECT name, id FROM database WHERE id IN ('.implode(',', $ids).')');
if (count($rows)) {
foreach ($rows as $row)
{
$ret[$row['id']] = $row['name'];
}
}
but in my next function I need to use name to search for records.
Because name is in single quotes 'name' I tried making it like this:
foreach ($names as $name) $id=(int)$name;
$rows = $this->db->getRows('SELECT name, is_active FROM database WHERE name IN ('.implode(',',$names).')');
if(count($rows))
{
foreach ($rows as $row)
{
$ret[$row['name']] = $row['is_active'];
}
}
it doesnt solve the problem, it just crashes. So I tried changing it a bit with separating it with double quotes:
$rows = $this->db->getRows("SELECT name, is_active FROM database WHERE name IN (" .implode(',',$names) .")");
still getting same error database query error.
and I dont know really what to do next. I believe I cant pass that many quotes inside of a implode.
Query should look like this: SELECT name, is_active FROM database WHERE name IN ('name1', 'name2', 'name3')
I tried to follow PHP: implode - Manual with same error results.
Switching between single or double quotes like you did, doesn't change the fact that you did not add any quotes around the individual name values at all. These quotes are the string delimiters the PHP syntax requires, but you have not added any quotes around the names in your implode yet, which the SQL syntax requires.
And implode only inserts the separator between the values - so the quote character before the first, and the quote character after the last item, still need to be added.
You want something like
'SELECT name, is_active FROM database WHERE name IN ("'.implode('","',$names).'")'
which will produce
SELECT name, is_active FROM database WHERE name IN ("a","b","c")

Php search Splitting criteria type

I have a php search form with two fields. One for $code another for '$name'.The user uses one or the other, not both.
The submit sends via $_POST.
In the receiving php file I have:
SELECT * FROM list WHERE code = '$code' OR name = '$name' ORDER BY code"
Everything works fine, however I would like that $code is an exact search while $name is wild.
When I try:
SELECT * FROM list WHERE code = '$code' OR name = '%$name%' ORDER BY code
Only $code works while $name gives nothing. I have tried multiple ways. Changing = to LIKE, putting in parentheses etc. But only one way or the other works.
Is there a way I can do this? Or do I have to take another approach?
Thanks
If you only want to accept one or the other, then only add the one you want to test.
Also, when making wild card searches in MySQL, you use LIKE instead of =. We also don't want to add that condition if the value is empty since it would become LIKE '%%', which would match everything.
You should also use parameterized prepared statements instead of injection data directly into your queries.
I've used PDO in my example since it's the easiest database API to use and you didn't mention which you're using. The same can be done with mysqli with some tweaks.
I'm using $pdo as if it contains the PDO instance (database connection) in the below code:
// This will contain the where condition to use
$condition = '';
// This is where we add the values we're matching against
// (this is specifically so we can use prepared statements)
$params = [];
if (!empty($_POST['code'])) {
// We have a value, let's match with code then
$condition = "code = ?";
$params[] = $_POST['code'];
} else if (!empty($_POST['name'])){
// We have a value, let's match with name then
$condition = "name LIKE ?";
// We need to add the wild cards to the value
$params[] = '%' . $_POST['name'] . '%';
}
// Variable to store the results in, if we get any
$results = [];
if ($condition != '') {
// We have a condition, let's prepare the query
$stmt = $pdo->prepare("SELECT * FROM list WHERE " . $condition);
// Let's execute the prepared statement and send in the value:
$stmt->execute($params);
// Get the results as associative arrays
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
The variable $results will now contain the values based on the conditions, or an empty array if no values were passed.
Notice
I haven't tested this exact code IRL, but the logic should be sound.

Php resolve blocks of text to make query

I am trying to write mysql query for search which is based on the following search.
Search Query string: "(foo and bar) or (blah1 and blah2)"
User will input the query string as mentioned above, it might be a single block or more than one block.
What i want to do is split the string into block of brackets resolve them individually and then combine it with OR or AND statement.
So my end query would look like
Select
*
from
table
where
(field like 'foo%' AND field like 'bar%') OR
(field like 'blah1%' AND field like 'blah2%')
Please help.
There are a couple of issues.
You need to explode out the query string into individual elements, and then explode those out to individual values, then build up the query.
You also need to make the input safe, either by escaping it or using prepared statement.
Using mysqli to use prepared statements something like this:-
$query_string = strtolower("(foo and bar) or (blah1 and blah2)");
$queries_sets = explode(' or ', $query_string);
$query_set = array();
$fields = array();
foreach($queries_sets AS $query_set_key=>$query_set_value)
{
$query_line = explode(' and ', trim($query_set_value, '()'));
if (count($query_line) == 2)
{
$query_set[] = "(field like ? AND field like ?)";
$fields[] = $query_line[0].'%';
$fields[] = $query_line[1].'%';
}
}
$stmt = $conn->prepare("SELECT * FROM table WHERE ".implode(' OR ', $query_set));
call_user_func_array(array($stmt, 'bind_param'), $fields);

SQL full text search with PHP and PDO

I'm trying to write a simple, full text search with PHP and PDO. I'm not quite sure what the best method is to search a DB via SQL and PDO. I found this this script, but it's old MySQL extension. I wrote this function witch should count the search matches, but the SQL is not working. The incoming search string look like this: 23+more+people
function checkSearchResult ($searchterm) {
//globals
global $lang; global $dbh_pdo; global $db_prefix;
$searchterm = trim($searchterm);
$searchterm = explode('+', $searchterm);
foreach ($searchterm as $value) {
$sql = "SELECT COUNT(*), MATCH (article_title_".$lang.", article_text_".$lang.") AGINST (':queryString') AS score FROM ".$db_prefix."_base WHERE MATCH (article_title_".$lang.", article_text_".$lang.") AGAINST ('+:queryString')";
$sth = $dbh_pdo->prepare($sql);
$sql_data = array('queryString' => $value);
$sth->execute($sql_data);
echo $sth->queryString;
$row = $sth->fetchColumn();
if ($row < 1) {
$sql = "SELECT * FROM article_title_".$lang." LIKE :queryString OR aricle_text_".$lang." LIKE :queryString";
$sth = $dbh_pdo->prepare($sql);
$sql_data = array('queryString' => $value);
$sth->execute($sql_data);
$row = $sth->fetchColumn();
}
}
//$row stays empty - no idea what is wrong
if ($row > 1) {
return true;
}
else {
return false;
}
}
When you prepare the $sql_data array, you need to prefix the parameter name with a colon:
array('queryString' => $value);
should be:
array(':queryString' => $value);
In your first SELECT, you have AGINST instead of AGAINST.
Your second SELECT appears to be missing a table name after FROM, and a WHERE clause. The LIKE parameters are also not correctly formatted. It should be something like:
sql = "SELECT * FROM ".$db_prefix."_base WHERE article_title_".$lang." LIKE '%:queryString%' OR aricle_text_".$lang." LIKE '%:queryString%'";
Update 1 >>
For both SELECT statements, you need unique identifiers for each parameter, and the LIKE wildcards should be placed in the value, not the statement. So your second statement should look like this:
sql = "SELECT * FROM ".$db_prefix."_base WHERE article_title_".$lang." LIKE :queryString OR aricle_text_".$lang." LIKE :queryString2";
Note queryString1 and queryString2, without quotes or % wildcards. You then need to update your array too:
$sql_data = array(':queryString1' => "%$value%", ':queryString2' => "%$value%");
See the Parameters section of PDOStatement->execute for details on using multiple parameters with the same value. Because of this, I tend to use question marks as placeholders, instead of named parameters. I find it simpler and neater, but it's a matter of choice. For example:
sql = "SELECT * FROM ".$db_prefix."_base WHERE article_title_".$lang." LIKE ? OR aricle_text_".$lang." LIKE ?";
$sql_data = array("%$value%", "%$value%");
<< End of Update 1
I'm not sure what the second SELECT is for, as I would have thought that if the first SELECT didn't find the query value, the second wouldn't find it either. But I've not done much with MySQL full text searches, so I might be missing something.
Anyway, you really need to check the SQL, and any errors, carefully. You can get error information by printing the results of PDOStatement->errorCode:
$sth->execute($sql_data);
$arr = $sth->errorInfo();
print_r($arr);
Update 2 >>
Another point worth mentioning: make sure that when you interpolate variables into your SQL statement, that you only use trusted data. That is, don't allow user supplied data to be used for table or column names. It's great that you are using prepared statements, but these only protect parameters, not SQL keywords, table names and column names. So:
"SELECT * FROM ".$db_prefix."_base"
...is using a variable as part of the table name. Make very sure that this variable contains trusted data. If it comes from user input, check it against a whitelist first.
<< End of Update 1
You should read the MySQL Full-Text Search Functions, and the String Comparison Functions. You need to learn how to construct basic SQL statements, or else writing even a simple search engine will prove extremely difficult.
There are plenty of PDO examples on the PHP site too. You could start with the documentation for PDOStatement->execute, which contains some examples of how to use the function.
If you have access to the MySQL CLI, or even PHPMyAdmin, you can try out your SQL without all the PHP confusing things. If you are going to be doing any database development work as part of your PHP application, you will find being able to test SQL independently of PHP a great help.

MySQL where clause equals anything (SELECT * WHERE col = ANY_VALUE)

I'd like to create a query in MySQL that has an optional value. When the value is specified the query is filtered by that value, when the value is not all rows are returned. Here's the idea:
public function doQuery($item = 'ANY_VALUE') {
$query = "SELECT * FROM table WHERE item = ?";
db->fetchAll($query,array($item))
...
}
doQuery(); // Returns everything
doQuery($item='item1'); // Returns only rows where item = 'item1'
Is there an easy way to do this without creating two query strings depending on the value of $item?
As far as I know, no such "any" placeholder exists.
If you can use LIKE, you could do
SELECT * FROM table WHERE item LIKE '%'
if you can append a condition, you could nullify the item clause like this:
SELECT * FROM table WHERE item = ? OR 1=1
(won't work in your example though, because you are passing "item" as a parameter)
That's all the options I can see - it's probably easiest to work with two queries, removing the WHERE clause altogether in the second one.
This would probably work, but I*m not sure whether it's a good idea from a database point of view.
public function doQuery($item = 'ANY_VALUE') {
$query = "SELECT * FROM table WHERE item = ? OR 1 = ?";
db->fetchAll($query,array($item, ($item == 'ANY_VALUE' ? 1 : 0))
...
}
Better way to do this is first generate sql query from the parameter you need to bother on, and then execute.
function doQuery($params) {
$query = 'SELECT * FROM mytable ';
if (is_array($params) // or whatever your condition ) {
$query .= 'WHERE item = ' . $params[0];
}
$query .= ' ;';
// execute generated query
execute($query);
}
You cannot get distinct results without giving distinct query strings.
Using $q = "... WHERE item = '$item'" you DO create distinct query strings depending on the value of $item, so it is not that different from using
$q = "..." . ($item=='ANY_VALUE' ? something : s_th_else);.
That said I see two or three options:
use function doQuery($item = "%") { $query = "SELECT ... WHERE item LIKE '$item'"; ...}
But then callers to that function must know that they must escape a '%' or '_' character properly if they want to search for an item having this character literally (e.g. for item = "5% alcoholic solution", giving this as argument would also find "50-50 sunflower and olive oil non alcoholic solution".
use function doQuery($item = NULL) { $query = "SELECT ..."; if ($item !== NULL) $query .= " WHERE item = '$item' "; ...} (where I use NULL to allow any other string or numerical value as a valid "non-empty" argument; in case you also want to allow to search for NULL (without quotes) you must choose another "impossible" default value, e.g., [], and you must anyway use a distinct query without the single quotes which however are very important in the general case), or even:
use function doQuery($item = NULL) { if($item === NULL) $query = "SELECT ..."; else $query = "SELECT ... WHERE item = '$item' "; ...}, which is more to type but probably faster since it will avoid an additional string manipulation (concatenation of the first and second part).
I think the 2nd & 3rd options are better than the first one. You should explain why you want to avoid these better solutions.
PS: always take care of not forgetting the quotes in the SQL, and even to properly escape any special characters (quotes, ...) in arguments which can depend on user input, as to avoid SQL injections. You may be keen on finding shortest possible solutions (as I am), but neglecting such aspects is a no-no: it's not a valid solution, so it's not the shortest solution!

Categories