1

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();
?>
JonAte
  • 31
  • 5
  • 1
    Are you actually doing a POST somewhere in your android code? – Patrick Q Feb 05 '19 at 15:46
  • @user3783243 can you explain more? you mean to have this statement before? bind_param('keyword', ?);. i used post method in another case and it gets the value. – JonAte Feb 05 '19 at 16:06
  • @JonAte Don't link to another question. Include _your code_ in the body of the question. – Patrick Q Feb 05 '19 at 16:13
  • 1
    `$keyword = "%{$_POST['keyword']}%"; $pdoStmt->bindParam(':keyword', $keyword, PDO::PARAM_STR);` or `$pdoStmt->bindParam(1, $keyword,PDO::PARAM_STR);` when using SQL `name LIKE ?` – Pinke Helga Feb 05 '19 at 16:20
  • 1
    or `$mysli_stmt->bind_param('s', $keyword)` – Pinke Helga Feb 05 '19 at 16:22
  • pdo: http://php.net/manual/en/pdo.prepared-statements.php mysqli: http://php.net/manual/en/mysqli-stmt.bind-param.php – Pinke Helga Feb 05 '19 at 16:24
  • @Quasimodo'sclone and in query after LIKE i must have a "?" – JonAte Feb 05 '19 at 16:25
  • @JonAte Yes, positional `LIKE ?` (PDO/mysqli) or named `LIKE :keyword` (PDO only) – Pinke Helga Feb 06 '19 at 04:53
  • There's nothing wrong with your query. It should work. Check that you are actually doing a request from your application to the server like @PatrickQ suggests. – Taha Paksu Feb 06 '19 at 08:42
  • @TahaPaksu it's working but it's not preventing sql injection anyways. I also read that MySQL is case insensitive? so there will be no problem – JonAte Feb 06 '19 at 08:51
  • You asked "why does my android app crash?", not "how can I prevent SQL injection inside this?", so I'm answering your question. People are all focused on that (except the copy paste commenters) – Taha Paksu Feb 06 '19 at 09:00
  • @TahaPaksu Updated now the question based of user3783243 answer. Which it stills crash my app – JonAte Feb 06 '19 at 09:24
  • 1
    You forgot to execute the statement. before get_result. `$stmt->execute();` – Taha Paksu Feb 06 '19 at 10:03
  • @TahaPaksu The query is invalid since it is a string. `SELECT * FROM items WHERE name LIKE CONCAT ('%', Milk, '%')` won't execute unless `milk` is a column. Even the `concat` function requires quotes. A driver with bound values adds the quotes, and escapes as needed. – user3783243 Feb 06 '19 at 21:28
  • @user3783243 the current question (I don't know if it was wrong before) is structured correctly. see this answer: https://stackoverflow.com/a/36593020/916000 the bound parameter will be inserted surrounded by quotes. – Taha Paksu Feb 07 '19 at 06:17
  • @TahaPaksu Yes, there have been 6 revisions. The current version is the code I wrote (see answer below). I forgot the `execute()` which you pointed out. The comments are from revisions 1-4. – user3783243 Feb 07 '19 at 12:43

2 Answers2

1

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();
user3783243
  • 5,368
  • 5
  • 22
  • 41
  • $keyword = '%' . $_POST['keyword'] . '%'; stmt->bind_param("s",$keyword) $sql = "SELECT * FROM items WHERE name LIKE CONCAT ?"; but it my app still stops. – JonAte Feb 05 '19 at 16:59
  • What is `stmt`? You should really use error reporting. Cut the java/android out of the question and just get the PHP working first. Inside the getting the PHP working get the `mysqli` working. These all require different error reporting. – user3783243 Feb 05 '19 at 17:10
  • I didn't understood what I have to do with the binding. I use mysqli – JonAte Feb 05 '19 at 17:25
  • 1. Use http://php.net/manual/en/mysqli.error.php for error reporting. 2. Prepare your query first, 3. bind 4. execute 5. fetch... Without the actual PHP execution there's no way for us to know how you run the query. – user3783243 Feb 05 '19 at 17:31
  • I tried $keyword =$_POST['keyword']; $sql = "SELECT * FROM items WHERE name LIKE '%$keyword%)"; and it's working but how I can make that bind? Can I add this line between these two statements? $keyword = mysql_real_escape_string($keyword); – JonAte Feb 05 '19 at 17:52
  • `mysql_` wont work with `mysqli`. See http://php.net/manual/en/mysqli.quickstart.prepared-statements.php for full documentation on how to prepare, bind, and execute. If you update the question with your code that executes the query we can help with that – user3783243 Feb 05 '19 at 17:56
  • It doesn't work.It makes my app crash. Are you sure I should only replace these 3 lines?. I think I should also change if/else – JonAte Feb 06 '19 at 07:33
  • 1
    @JonAte Yes, only 1 fetch. See update. Also your app wasn't crashing previously? Really you should narrow down the issue. Stop using the app and confirm it is the PHP failing. – user3783243 Feb 06 '19 at 12:56
  • I don't know why but it still stops. I think it's concat. I am using a local server. Did you test this on a small database? – JonAte Feb 07 '19 at 05:12
0

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%)";
  • By applying this you can equalizing the case of query with the db entry like if someone write "MilK" in search box, strtoupper will convert it as "MILK" and store it in variable $keyword, then you have applied query upper(name) in db by which entries like milk,miLK,Milk etc will be converted in "MILK" and you will get a match a perfect one. – Shah Muhammad Talha Tahir Feb 06 '19 at 09:27