I am making a site that has an autocomplete product search bar. When a user types in a few letters, I compare them to the database with the LIKE statement and get products back that look similar. A user can see a max of 4 products that the user was thinking of in a drop down under the search bar. Now once the user clicks on a result, I pass that back to the PHP PDO statement.
If the product doesn't contain a %, it works fine. But I have products such as '100% Product A', and I can't seem to get around it. I know % is used in the LIKE statement. I used like, but the results weren't great. I was getting a different variation of the product I wanted. How do I get passed this?
$pdo = new PDO($dsn, $user, $passwd);
//Retrieving Product Name
$prodName = $_GET['name'];
$stm = $pdo->prepare("SELECT * FROM products WHERE productName = ?");
$stm->bindValue(1, $prodName);
$stm->execute();
$row = $stm->fetch(PDO::FETCH_ASSOC);
$results[0] = $row["productName"];
$results[1] = $row["price"];
$results[2] = $row["quantity"];
$result=implode("','", $results);
echo $result;
Since % has special meaning, you simply need to escape it to make it used literally, using usual \:
\% A % character; see note following the table
So you just need to str_replace() all % with \%.
See: https://dev.mysql.com/doc/refman/8.0/en/string-literals.html
There is not a single problem with retrieving a row containing % character with LIKE operator, let alone with a comparison operator used in your code.
Even with like it could only give you extra results, but never would make a problem with getting the existing result.
To claim such a problem exists, you you must prove your words by posting a Minimal Complete Verifiable example so everyone would be able to run your code and confirm the problem exists indeed.
However, in such a simple case, instead of a proof, you will just discover some simple mistake, completely irrelevant to % sign. For example, your search string gets severely encoded/escaped by some cargo cult code and naturally becomes a different string that doesn't have any matches in the database
Well, i would just remove % from the search string using str_replace
//Retrieving Product Name
$prodName = str_replace('%', '', $_GET['name']);
You could also escape the percentage by putting it in brackets or using backslash:
//Retrieving Product Name
$prodName = str_replace('%', '[%]', $_GET['name']); // \% would work too instead of [%]
Related
I'm trying to get all values from table products, that contains for example shirt.
In this case $thin is getting values from searach box "stxt".
It means that if $thin = shirt, I want to get shirt, t-shirt etc. Right now, only thing that I get is only shirt, despite if I will use "LIKE" or "=" as operator in $sql statement.
$thin = $_POST['stxt'];
$thing = strtoupper($thin);
$sql = "select * from products where upper(productName) LIKE '$thing'";
In your query, put in your LIKE % before and after your variable.
Like this "%$variable%".
If you want there to be just a number of n characters before or after your variable, put the _ symbol n times.
And for security reasons, try to use prepared statements.
Link sql like: https://sql.sh/cours/where/like
can you use a regexp operator ?
$sql = "select * from products where productName regexp $thing";
$thin = $_POST['stxt'];
$thing = strtoupper($thin);
$sql = "select * from products where upper(productName) LIKE '%$thing%'";
MSSQL needs % wildcards, other SQL dialects may differ.
Alternatively you could do a REPLACE(productName,'$thing','') and compare the length of that to the original length.
Either way it is going to be a full table scan unless you have full text indexes set up.
I have text correctly saved into a mariahDB 10.2 database. The text, to complicate matters, is in fact a combination of Regular Expressions and a hybrid code invented by someone else.It can be used unchanged in another application as a text file - not PHP. But it just text at the end of the day. I want to grab data from this table, change it a small amount, and save it in a new table.
The problem is less so about changing the original data much, but more about SELECTING and saving data that is full of backslashes, single quotes, and double quotes to a new table without it being changed when it is saved. Is there a simple way in PHP and MySQL to take text from a table and resave it exactly as it is so the second table is not different from the the first?
For example the first table has the following in it.
add list to list(%section,$plugin function("XpathPlugin.dll", "$Generic Xpath Parser", $document text, "//p[1]/a[#class=\'result-title hdrlnk\' and 1]", "outerhtml", "False"),"Don\'t Delete","Global")
But if I put this into a variable and then INSERT or UPDATE that to another table, MySQL seems to strip out the backslashes, or add backslashes and throw errors for incorrectly formatted SQL.
For instance Don\'t Delete becomes Don't Delete and in other examples \\ become \
In another case ?=\")" loses the a backslash and becomes ?=")"
I have tried dozens of combinations of PHP function to leave the text alone, such as addslashes(), htmlentities(), preg_replace(), various string substitution and nothing get the data back into the table the same way as it came out.
Does anyone have the trick to do this? I would call the function leave_my_text_alone(); !!
EDIT
To add a few things that did not do the trick to get a variable I could update into the database I tried
$omnibotScript = addcslashes($omnibotScript,"'");
I then found I need to do this twice to consider the backslash being removed from before the apostrophe in Don't Delete....or it would throw a MySQL parsing error..Doing it again fixed that. So then I had to put two backslashes back to have one removed. I then added this to consider a double backslash being reduced to single backslash.
$pattern = '/\\\\"/';
$replacement = '\\\\\\\"';
$omnibotScript = preg_replace($pattern, $replacement, $omnibotScript);
But the list went on.
Use prepared statements.
If you use a prepared statement, MySQL will take care of all the escaping you need to get the string back into the table exactly as it came out of it. For example, using MySQLi:
$query = "SELECT s1 FROM t1";
if (!$result = $con->query($query)) {
exit($con->error);
}
$row = $result->fetch_assoc();
$value = $row['s1'];
$query = "INSERT INTO t2(s1) VALUES (?)";
$stmt = $con->prepare($query);
$stmt->bind_param('s', $value);
$stmt->execute();
The value of s1 in t2 will be exactly the same as the value in t1.
I have the following PDO with LIKE using wildcard '%' to match partial results:
$value = $_GET["searchValue"];
$dbh = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );
$query = $dbh->prepare('SELECT * FROM table WHERE column1 LIKE ? OR column2 LIKE ?');
$query->execute(array("%$value1%","%$value2%"));
My issue is that when the user inputs '%' or '%%%%%', it returns all values on the database, even if don't have anything with saved with % symbol con that row.
Thanks in advance.
I have the following PDO with LIKE using wildcard '%' to match partial results
when the user inputs '%' it returns all values on the database, even if don't have anything with saved with % symbol con that row.
These two paragraphs above, quite contradicting they are?
You when you're using % yourself, you want it to match any character, but then user is using it, you want it to match only % literaly. Alas, mysql cannot read minds yet, and cannot tell one % from another.
Frankly, there is no issue at all. Returning all the rows for LIKE '%%%' statement is the case % wildcard is designed for. If you want to let users to search for the % character literally, you have to escape it with backslash.
Lets consider only the $value1 variable.
When the user insert %, PDO makes your query
SELECT * FROM table WHERE column1 LIKE '%%%'
that's the right behavior.
But, if you want to search (and that's what you want) strings with this pattern
[anything]%[anithing] you have to escape the user input, making the query looking like
SELECT * FROM table WHERE column1 LIKE '%\%%'
To do that, you have to replace the % with \%.
Basically, the only thing you have to do is to change this line
$value = $_GET["searchValue"];
in that way
$value = str_replace('%', '\%', $_GET["searchValue"]);
I hope it helps.
I have a PHP script that is generating a MySQL select statement:
select * from words where word = 'Classic'
There is exactly one word in the words table with the variable word equal to Classic.
When my PHP page executes, I get no results from the query. If I echo the string that is being used to execute the query, cut and paste that into the SQL window in PHPMyAdmin in the database, I also get no results. However, if I re-type that EXACT string into the SQL window in PHPMyAdmin (with the same quote characters), I get the proper result of one row.
The word Classic from the select statement is gotten from a PHP GET (see code below). I can echo the $word variable, and get the correct result of 'Classic'. What am I doing wrong?
Here is my code:
<?php
require ('dbconnect.php');
$word = $_GET["word"];
$selectStr = "SELECT * FROM words WHERE word = '" . $word . "'";
if ($results = MySQL($dbName, $selectStr))
{
$rowCount = MySQL_NUMROWS($results);
}
$resultRow = MYSQL_FETCH_ROW($results);
$wordID = $resultRow[0];
?>
Please, please, please sanitize that word. mysql_real_escape_string() should do the trick.
$selectStr = "SELECT * FROM words WHERE word LIKE '" . $sanitized_word_i_promise . "'"; should work :)
Just to explain: "=" should work for exact matches. This includes uppercase / lowercase, spaces etc. You should probably trim that result first too, before using it in the query.
If you have foo stored in the database (note the space at the end) - it won't match foo, without a space. You'll want to use LIKE 'foo%' - probably.
Either way, Sourabh is right, although performance wise, this isn't a big hit when trying to match exact strings, you should look for the problem in other places first (such as, is the item in the database an exact match?).
First off you should not take any user input and directly input it into a query without sanitizing it, or using a prepared statement.
Now that we've gotten that out of the way: have you tried doing a strcmp() with the variable and your string written in? Such as
echo strcmp($_GET['word'], "Classic")
If you get a result other than 0 it means they are not the same, most likely there will be a whitespace of some sort in the $_GET variable. use trim() on it to take out whitespace. Also could be a case sensitivity issue as well.
I have a function below which works perfectly, but now the client came back and asked that the number only be taken to do the search because most of his clients won't type in the suffix "h" or whatever it may be as per my example below:
38039 or 38039h
However he also said he only has one group of product codes which begin with "T" so they could be typing in "T760" in which case we would need the prefix.
My code below does a search on the exact product currently, can anyone help me work in these examples?
<?php
//Find Stock Value
function checkstock($prodCode) {
$prodCode = strtoupper($prodCode);
require '../../../../config.php';
$dbh = new PDO(DB_DSN, DB_USER, DB_PASS);
$sql = "SELECT * FROM isproducts WHERE prodCode = '".
$prodCode."' AND AllowSalesOrder = '1'";
$stmt = $dbh->query($sql);
$obj = $stmt->fetch(PDO::FETCH_OBJ);
$count = $stmt->rowCount();
echo ($count == 1 ?
ROUND($obj->FreeStockQuantity, 0) : 'Invalid product code '.$prodCode.'');
}
//Call Stock Function
checkstock($_POST['productcode']);
?>
Change the query to like below ?
SELECT * FROM isproducts
WHERE
(
prodCode='{$prodCode}' // for product with prefix or suffix
OR prodCode LIKE '{$prodCode}%' // without suffix
OR prodCode='T{$prodCode}' // without prefix
)
AND AllowSalesOrder = ''";
Wild-card by single character
OR prodCode LIKE '{$prodCode}_' // single character wild-card
It seems that you may have products with the same number but not the same suffix? like 8512n and 8512h ?
You could use LIKE '%$code%'
$sql = "SELECT * FROM isproducts WHERE prodCode LIKE '%".$prodCode."%' AND AllowSalesOrder = ''";
and I think its the more secure way in your case, so that all products containing the number will appear, regardless of suffix or prefix.
The above may return more than one product, so the user still has to choose which one it is he is actually looking for.
You can use % wild card for this kind of problem.
check this out....
http://www.w3schools.com/SQL/sql_wildcards.asp
it might be useful... :-)
In some of my code I use the following strategy:
# psuedo-code ... NOT intended for real use:
SELECT COUNT(*) FROM someTable WHERE someColumn = "{XXX}"
# If that returns exactly one than use the corresponding query
SELECT COUNT(*) FROM someTable WHERE someColumn LIKE "{XXX}"
# If that returns exactly one then use it
SELECT COUNT(*) FROM someTable WHERE someColumn LIKE "{XXX}%"
# If that returns exactly one then use it
SELECT COUNT(*) FROM someTable WHERE someColumn LIKE "%{XXX}%"
# If that returns exactly one then use it
... (where {XXX} is the placeholder for the user supplied search term).
The idea here is that I first try a precise match, then I try it under the assumption that the term already may contain SQL wildcards, then I try suffixing the % wild card and finally I try wrapping it with % wild cards.
At any point if I've found an unambiguous match then I use it. If I find more than one match at any point (not shown in the psuedo-code here) then I might throw an exception or I might return them or a subset of them based on the specifics of what I'm doing.
(In reality I'm using the parameter interpolation features of Python or Perl or sanitizing my inputs to allow wild cards while preventing SQL injections; so the code doesn't look like what I'm showing here. This is just to convey the general idea).
My goal is to allow my scripts to be called with the minimum unambiguous arguments supplied which sounds roughly similar to what your clients are requesting here.
From a usability perspective most users will get the first characters of any input right. So exact match following by suffixed wildcard match is most likely to succeed most of the time. In my case my users are likely to be familiar with SQL wildcards and may prefer to use them to construct their own unambiguous match; and logically that attempt has to be inserted before I start suffixing or wrapping it with my own wildcards.
This is why I use this specific sequence of matching attempts.