I am trying to make SQL in php to return all the entries that matches a keyword that is entered by the user (from search bar).
I want to return all the entries that their name "partial" matches with the keyword.
I want at least to match the keyword, if an entry name in database before has space and after maybe another letter/space.
For example I have three entries with names "Milk", "Semi skimmed Milk" and "Full Milk 2". If the keyword is "Milk" or "milk" or "MiLK", I want to get all these three entries.
The only case I am thinking it might be the problem is case sensitive.
I tried with a keyword that exists exactly in database, but my app (on android) stops .
Based on user3783243 answer.
PHP FILE
<?php
$servername = "";
$username = "";
$password = "";
$dbname = "";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT name FROM items WHERE name LIKE CONCAT ('%', ?, '%')";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $keyword);
$res = $stmt->get_result();
while($row = $res->fetch_assoc()) {
echo $row["name"] . ",";
}
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo $row["name"] . ",";
}
} else {
echo "0";
}
$conn->close();
?>
Your query should be:
$sql = "SELECT * FROM items WHERE name LIKE CONCAT ('%', ?, '%')";
and then $keyword should be bound with whatever syntax the driver you are using supports.
As is your query would have been:
SELECT * FROM items WHERE name LIKE CONCAT ('%', Milk, '%')
and you wanted Milk to be a string so it needed to be quoted. As is mysql would have thought that was a column.
Alternatively you could do:
$keyword = '%' . $_POST['keyword'] . '%';
$sql = "SELECT * FROM items WHERE name LIKE CONCAT ?";
that is the same and still requires the binding though.
The binding also takes away the SQL injection. See How can I prevent SQL injection in PHP? and/or https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet#Defense_Option_1:_Prepared_Statements_.28with_Parameterized_Queries.29
Per update.. replace:
$keyword =$_POST['keyword'];
$sql = "SELECT * FROM items WHERE name LIKE '%$keyword%)";
$result = $conn->query($sql);
with:
$sql = "SELECT name FROM items WHERE name LIKE CONCAT ('%', ?, '%')";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $keyword);
$stmt->execute();
$res = $stmt->get_result();
if(empty($conn->errno) && !empty($res)) {
while($row = $res->fetch_assoc()) {
echo $row["name"] . ",";
}
} else {
echo '0';
//print_r($conn->errno);
}
$conn->close();
...
also remove
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo $row["name"] . ",";
}
} else {
echo "0";
}
$conn->close();
In this case you can convert the input in search bar to either upper or lower case by default then apply query in db like
For Upper case:
$keyword =strtoupper($_POST['keyword']);
$sql = "SELECT * FROM items WHERE upper(name) LIKE '%$keyword%)";
Or for lower case:
$keyword =strtolower($_POST['keyword']);
$sql = "SELECT * FROM items WHERE lower(name) LIKE '%$keyword%)";
Related
i try to make a search system which has multiple inputs. I tried to to select the data in query and that it supose to add the aditional data if it exist but it dont do that. i messed it up and dont know how to fix it. The data from my other file gets there. I checked it. Did anyone know what i did wrong?
<?php
$connect= mysqli_connect("localhost", "root", "root", "website");
$query = "SELECT * FROM data";
$sort = " date DESC"
if(isset($_GET["search"])){
$keywordsearch = $connect->escape_string($_GET['search']);
$query .= " WHERE name like '%$keywordsearch%'";
if(isset($_GET['tag'])){
$keywordtag = $connect->escape_string($_GET['tag']);
$query .= " AND tag like '%$keywordtag%'";
$query .= " ORDER BY $sort";
}
}
$result = mysqli_query($connect, $query);
while($row = mysqli_fetch_array($result)){
echo $row['name']."<br>";
}
?>
This can be done by creating an array of the data passed in and using that array you can control all the other requirements to build a query and pass parameters using a Parameterised and bound query
$_GET = ['search'=> 'fluff', 'tag'=>'easy'];
$connect= mysqli_connect("localhost", "root", "root", "website");
$query = "SELECT * FROM data";
$sort = " ORDER BY date DESC";
if(isset($_GET["search"])){
$query .= " WHERE name like ?";
$vals[] = '%' . $_GET["search"] . '%';
}
if(isset($_GET['tag'])){
$query .= " OR tag like ?";
$vals[] = '%' . $_GET["tag"] . '%';
}
$query .= $sort;
$types = str_repeat('s', count($vals));
$stmt = $connect->prepare($query);
$stmt->bind_param($types, ...$vals);
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_array(MYSQLI_ASSOC)){
echo $row['name']."<br>";
}
RESULT:
easy peasy<br>
fluff fluff<br>
From your comment: problem is that i can input the tag variable and if the tag variable is something that is not included in the database it shows nothing
That is because you are using an AND rather than an OR in the query. I corrected that also
I want to search a certain string in all the columns of different tables, so I am looping the query through every column name. but if i give it as dynamic value it does not seem to work.
what is wrong?
<?php
$search = $_POST['search'];
$columns = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'feedback'";
$columns_result = $conn->query($columns);
$columns_array = array();
if (!$columns_result) {
echo $conn->error;
} else {
while ($row = $columns_result->fetch_assoc()) {
//var_dump($row);
//echo $row['COLUMN_NAME']."</br>";
array_push($columns_array, $row['COLUMN_NAME']);
}
}
var_dump($columns_array);
$row_result = array();
for ($i = 0; $i < count($columns_array); $i++) {
echo $columns_array[$i] . "</br>";
$name = "name";
// $sql = 'SELECT * FROM feedback WHERE "'.$search.'" in ("'.$columns_array[$i].'")';
$sql = 'SELECT * FROM feedback WHERE ' . $name . ' like "' . $search . '"';
$result = $conn->query($sql);
if (!$result) {
echo "hi";
echo $conn->error;
} else {
foreach ($result as $row) {
array_push($row_result, $row);
echo "hey";
}
}
}
var_dump($row_result);
I am getting the column names of the table and looping through them because I have so many other tables which I need to search that given string. I don't know if it is optimal I did not have any other solution in my mind. If someone can tell a good way I will try that.
It looks to me that you want to generate a where clause that looks at any available nvarchar column of your table for a possible match. Maybe something like the following is helpful to you?
I wrote the following with SQL-Server in mind since at the beginning the question wasn't clearly tagged as MySql. However, it turns out that with a few minor changes the query work for MySql too (nvarchar needs to become varchar):
$search='%';$tbl='feedback';
if (isset($_POST['search'])) $search = $_POST['search'];
$columns = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '$tbl' AND DATA_TYPE ='nvarchar'";
$columns_result = $conn->query($columns);
$columns_array = array();
if(!$columns_result) print_r($conn->errorInfo());
else while ($row = $columns_result->fetch(PDO::FETCH_ASSOC))
array_push($columns_array, "$row[COLUMN_NAME] LIKE ?");
$where = join("\n OR ",$columns_array);
$sth = $conn->prepare("SELECT * FROM $tbl WHERE $where");
for ($i=count($columns_array); $i;$i--) $sth->bindParam($i, $search);
$sth->execute();
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
print_r($result);
The above is a revised version using prepared statements. I have now tested this latest version using PHP 7.2.12 and SQL-Server. It turned out that I had to rewrite my parameter binding part. Matching so many columns is not a very elegant way of doing queries anyway. But it has been a nice exercise.
It looks like you are using mysqli, so I wanted to give another way of doing it via mysqli.
It does more or less the same as cars10m solution.
$search = $_POST['search'];
$columns = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'feedback'";
$columns_result = $conn->query($columns)->fetch_all(MYSQLI_ASSOC);
// Here dynamically prepare WHERE with all the columns joined with OR
$sql = 'SELECT * FROM feedback WHERE ';
$arrayOfWHERE = [];
foreach($columns_result as $col){
$arrayOfWHERE[] = '`'.$col['COLUMN_NAME'].'` LIKE ?';
}
$sql .= implode(' OR ', $arrayOfWHERE);
// prepare/bind/execute
$stmt = $conn->prepare($sql);
$stmt->bind_param(str_repeat("s", count($arrayOfWHERE)), ...array_fill(0, count($arrayOfWHERE), $search));
$stmt->execute();
$result = $stmt->get_result();
$row_result = $result->fetch_all(MYSQLI_ASSOC);
var_dump($row_result);
Of course this will search for this value in every column of the table. It doesn't consider data type. And as always I have to point out the using PDO is better than mysqli. If you can switch to PDO.
I have one straight forward script that returns user data if access code is correct.
$sql = "SELECT * FROM users WHERE access_codes = '$CODE' ";
$rs = $mysqli->query($sql);
$rows = mysqli_num_rows($rs);
if ($rows == 1) {
//CODE IS FOUND, RETURN USERNAME AND PASSWORD
$row = mysqli_fetch_array($rs);
$username = $row['username'];
$password = $row['password'];
mysqli_close($mysqli); // Closing Connection
header("Location: http://GOTONEXTPAGE.COM");
die();
} else {
//CODE IS NOT FOUND
mysqli_close($mysqli); // Closing Connection
exit;
}
It works fine, if access_codes contains only 1 value.
But what i want to achive is that access_codes can contain several codes, separated with space ex: 123456 654321 0101020304.
And i could still retrive userdata if ANY of these codes is found.
Can i do this with query, or i need to work around with php?
Regards
M
It would be best if you normalized the schema. Make a table of access codes where there's one row for each combination of userID and access_code. Then you can use a JOIN between this table and the users table to get the users with a particular access code.
If you can't redesign, you can use a regular expression:
$sql = "SELECT * FROM users WHERE access_code RLIKE CONCAT('[[:<:]]', ?, '[[:>:]]')";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('s', $CODE);
$stmt->execute();
[[:<:]] and [[:>:]] match word boundaries, so this will only match when the code is a whole word in the column.
$codes = "123456 654321 0101020304";
$expCodes = explode(" ", $codes);
$code = implode("', '", $expCodes);
$sql = "SELECT * FROM users WHERE access_codes IN ('{$code}') ";
// do your query
I'm trying to make some prepared statements in PHP using postgres.
It's a bit difficult to explaing so i'll just show you:
$stmt = "SELECT * FROM customer WHERE zip = '$1'";
if(isset($_POST["CITY"])){
$stmt .= "AND city = '$2'";
}
if(isset($_POST["COUNTRY"])){
$stmt .= "AND country = '$3'";
}
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute("myconnection","my query", array("0000","someCity","someCountry"));
Sorry if some of the code is wrong but it's a freehand example. What I need is to be able to make the prepared statement dynamic depending on if some variables isset/not-null.
It doesn't seem to work when posting 3 variables in the array when the statement only expects 1 or if i only need to add $1 and $3 but not $2. I hope you understand.
I need to use it this weekend, so I hope someone knows!
Thank you in advance!
In a prepared statement, the SQL is static on purpose. The number of parameters cannot vary once the statement is prepared.
But it'd be easy for your code to submit the right number of parameters depending on the statement. You could add a variable for the counter of parameters, and a dynamic php array to pass to pg_execute instead of hard-coded literals. And they would be incremented/populated inside the if (isset(...)) branches.
There is nothing wrong in having 3 different statements (one for each case) and execute the one that applies depending on the number of parameters passed.
Example:
EDIT: I modified the code to match all cases:
Only the zip specified
Zip + city
Zip + country
Zip + city + country
(even if there are some other cases, you'll understand the idea)
$stmt = "SELECT * FROM customer WHERE zip = '$1'";
if(isset($_POST["CITY"]) && isset($_POST["COUNTRY"])) {
$stmt3 = $stmt . " AND city = '$2'" . " AND country = '$3'";
} elseif(isset($_POST["CITY"])) {
$stmt1 = $stmt . " AND city = '$2'";
} elseif(isset($_POST["COUNTRY"])) {
$stmt2 = $stmt . " AND country = '$2'";
}
if(isset($stmt3)) {
$result = pg_prepare("myconnection", "my query", $stmt3);
$result1 = pg_execute("myconnection","my query", array("0000","someCity","someCountry"));
} elseif(isset($stmt2)) {
$result = pg_prepare("myconnection", "my query", $stmt2);
$result1 = pg_execute("myconnection","my query", array("0000","someCountry"));
} elseif(isset($stmt1)) {
$result = pg_prepare("myconnection", "my query", $stmt1);
$result1 = pg_execute("myconnection","my query", array("0000","someCity"));
} else {
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute("myconnection","my query", array("0000"));
}
I omitted (just as you did) all the error checks for brevity.
Although both Daniel and aymeric are correct - no sense in testing twice, nor using numbers. See below:
$some_vars = array();
$some_vars[":zip"] = $_POST["ZIP"];
$stmt = "SELECT * FROM customer WHERE zip = :zip";
if(isset($_POST["CITY"])){
$some_vars[":city"] = $_POST["CITY"]);
$stmt .= " AND city = :city";
}
if(isset($_POST["COUNTRY"])){
$some_vars[":country"] = $_POST["COUNTRY"]);
$stmt .= " AND country = :country";
}
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute("myconnection","my query", $some_vars);
Don't forget to sanitize and such.
Don't do string concatenation. Check if the parameters are set. If not set them to empty. Use a single query string:
$zip = $_POST["zip"];
$city = $_POST["city"];
$country = $_POST["country"];
if (!isset($zip)) $zip = '';
if (!isset($city)) $city = '';
if (!isset($country)) $country = '';
$stmt = "
select *
from customer
where
(zip = '$1' or '$1' = '')
and
(city = '$2' or '$2' = '')
and
(country = '$3' or '$3' = '')
";
$result = pg_prepare("myconnection", "my query", $stmt);
$result1 = pg_execute(
"myconnection",
"my query",
array($zip, $city, $country)
);
Each condition will only be enforced if the respective parameter is not the empty string.
The same logic could use the null value in stead of empty those columns contain empty strings that should be selected.
I'm using a multiple select option form to get a table of venues. Each venue has an ID and this is what I used:
<?php
require("db_access.php");
if(isset($_POST['select3']))
{
$aVenues = $_POST['select3'];
if(!isset($aVenues))
{
echo("<p>You didn't select any venues!</p>\n");
}
else
{
$nVenues = count($aVenues);
echo("<p>You selected $nVenues venues: ");
for($i=0; $i < $nVenues; $i++)
{
echo($aVenues[$i] . " ");
}
echo("</p>");
$sql = "SELECT * FROM venues WHERE id IN (" . implode(",",$aVenues) . ")";
$comma_separated = implode(",", $aVenues);
echo $comma_separated;
}
}
?>
It results in this:
However I thought that the code would use those two numbers below and draw out a table with those id's I used :/ ? Am I missing something?
$array is used in implode(",", $array); but is not defined anywhere else that we can see. It is perhaps intended to be:
implode(",", $aVenues);
UPDATE
Per comments, it does not draw a table because you never actually query your database.
You build your SQL statement, but you need to execute it and fetch the result set.
// Make sure you actually have a database connection
$conn = mysql_connect('localhost', $username, $password);
mysql_select_db($database);
$sql = "SELECT * FROM venues WHERE id IN (" . implode(",",$aVenues) . ")";
$comma_separated = implode(",", $array);
echo $comma_separated;
// Execute query and fetch result rowset
$result = mysql_query($sql);
if ($result) {
$rowset = array();
while ($row = mysql_fetch_array($result)) {
$rowset[] = $row;
}
var_dump($rowset);
}
else echo mysql_error();