6

I'm trying to create an auto-complete function into a textbox but the result should come from my SQL database.

Here's the code that i'm trying to configure:
index.php:

<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>jQuery UI Autocomplete - Default functionality</title>
        <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
        <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
        <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
        <link rel="stylesheet" href="/resources/demos/style.css">
        <script>
            $(function() {
                var availableTags = [
                    "autocomplete.php"; ];
                $( "#tags" ).autocomplete({
                    source: availableTags
                });
            });
        </script>
    </head>
    <body>
        <div class="ui-widget">
            <label for="tags">Tags: </label>
            <input id="tags">
        </div>
    </body>
</html>

EDIT: I changed the content of variable availableTags and made it into var availableTags = <?php include('autocomplete.php') ?>;

Variable availableTags is the source of words, so I try to change it and instead put a file name where fetching of words from my database is happening.

Here's my autocomplete.php file:

 <?php

 include('conn.php');
 $sql="SELECT * FROM oldemp";
 $result = mysqli_query($mysqli,$sql) or die(mysqli_error());

 while($row=mysqli_fetch_array($result))
 {
 echo "'".$row['name']."', ";
 }
 ?>

EDIT: Also changed the content of the while loop and made it into

$name=mysqli_real_escape_string($con,$row['name']);
$json[]=$name;

How can I insert the fetched words from autocomplete.php into availableTags variable?

EDIT/UPDATE: There's a list showing up whenever I type something on the textbox, but it has no text in it. I know it's fetching, but the word itself is not showing on the list.

Logan Wayne
  • 6,001
  • 16
  • 31
  • 49

5 Answers5

4

The jQuery UI autocomplete can take 3 different types of values of the source option:

  1. An array containing the list of things to fill in the auto complete with
  2. A string containing the URL of a script that filters a list and sends us the results. The plugin will take text typed into it and send it as a term parameter in a query-string appended to the URL we provided.
  3. A function that retrieves the data and then calls a callback with that data.

Your original code uses the first, an array.

var availableTags = [
  "autocomplete.php";
];

What that tells the autocomplete is that the string "autocomplete.php" is the only thing in the list of things to autocomplete with.

I think what you were trying to do is embed it with something like this:

$(function() {

  var availableTags = [
    <?php include("autocomplete.php"); /* include the output of autocomplete as array data */ ?>;
  ];

  $( "#tags" ).autocomplete({
    source: availableTags
  });

});

That would probably work okay assuming that the list of things that are being returned from the database will always remain short. Doing it this way is kind of fragile though since you are just shoving raw output from PHP into your JS. If the returned data contains " you might have to use addSlashes to escape it correctly. You should however change the query to return a single field rather than *, you probably only want one field as the label in the autocomplete not the entire row.

A better approach, especially if the list could potentially grow very large, would be to use the second method:

$(function() {

  var availableTags = "autocomplete.php";

  $( "#tags" ).autocomplete({
    source: availableTags
  });

});

This will require you to make a change to the back-end script that is grabbing the list so that it does the filtering. This example uses a prepared statement to ensure the user provided data in $term doesn't open you up to SQL injection:

<?php

include('conn.php');

// when it calls autocomplete.php, jQuery will add a term parameter
// for us to use in filtering the data we return. The % is appended
// because we will be using the LIKE operator.
$term = $_GET['term'] . '%';
$output = array();

// the ? will be replaced with the value that was passed via the
// term parameter in the query string
$sql="SELECT name FROM oldemp WHERE name LIKE ?";

$stmt = mysqli_stmt_init($mysqli);

if (mysqli_stmt_prepare($stmt, $sql)) {

  // bind the value of $term to ? in the query as a string
  mysqli_stmt_bind_param($stmt, 's', $term);

  mysqli_stmt_execute($stmt);

  // binds $somefield to the single field returned by the query
  mysqli_stmt_bind_result($stmt, $somefield);

  // loop through the results and build an array.
  while (mysqli_stmt_fetch($stmt)) {
      // because it is bound to the result
      // $somefield will change on every loop
      // and have the content of that field from
      // the current row.
      $output[] = $somefield;
  }

  mysqli_stmt_close($stmt);
}

mysqli_close($mysqli);

// output our results as JSON as jQuery expects
echo json_encode($output);

?>

It's been a while since I've worked with mysqli, so that code might need some tweaking as it hasn't been tested.

It would be good to get into the habit of using prepared statements since when properly used, they make SQL injection impossible. You can instead use a normal non-prepared statement, escaping every user-provided item with mysqli_real_escape_string before you insert it into your SQL statement. However, doing this is very error-prone. It only takes forgetting to escape one thing to open yourself up to attacks. Most of the major "hacks" in recent history are due to sloppy coding introducing SQL injection vulnerabilities.

If you really want to stick with the non-prepared statement, the code would look something like this:

<?php
  include('conn.php');

  $term = $_GET['term'];
  $term = mysqli_real_escape_string($mysqli, $term);
  $output = array();

  $sql = "SELECT name FROM oldemp WHERE name LIKE '" . $term . "%';";

  $result = mysqli_query($mysqli,$sql) or die(mysqli_error());

  while($row=mysqli_fetch_array($result))
  {
    $output[] = $row['name'];
  }

  mysqli_close($mysqli);

  // output our results as JSON as jQuery expects
  echo json_encode($output);
?>
Useless Code
  • 12,123
  • 5
  • 35
  • 40
  • I think the real problem here is how to get the content of autocomplete.php into index.php. And where does GET[] supposed to get a 'term' variable? And I replaced ? to $term in the SELECT sql. Thanks for the effort – Logan Wayne Jan 24 '14 at 00:57
  • @LoganWayne As [the documentation states](http://api.jqueryui.com/autocomplete/#option-source) `term` is sent by the autocomplete plugin, it gets the text that it sends from the text that is typed in it. It is a **very bad idea** to just put `$term` into the SQL statement without any escaping, it opens you up to [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) attacks. For instance what if someone searched for `';DROP TABLE oldemp`. If you aren't going to use a prepared statement, at least use `mysqli_real_escape_string` on `$term` before you put it into the SQL statement! – Useless Code Jan 24 '14 at 05:08
  • Thanks for that advice. And now I get the point where the 'term' comes from. And yes, I used mysqli_real_escape_string on $term before putting it on the SQL statement. But still isn't working. – Logan Wayne Jan 24 '14 at 05:35
  • What isn't working? Pick the first few letters of something in the DB, if you visit `autocomplete.php?term=%those letters here%` what happens? Does it produce an error? A blank page? Output different from what you were expecting? A proper JSON formatted array? It's hard to troubleshoot "it doesn't work". – Useless Code Jan 24 '14 at 07:25
  • I'm running index.php. Whenever I type something in the text box, nothing shows up. I did all your recommendations, but nothing happened. – Logan Wayne Jan 24 '14 at 08:42
  • When you visit autocomplete.php directly what happens? Do you get any output at all, if so what? Is there an error message on the page? Is there an error message in your php error log? We need to make sure that the error is not on the server side before we can start diagnosing the JS. – Useless Code Jan 24 '14 at 09:00
  • When I direct to autocomplete.php, there's an error of course because of the 'term' variable. But all records in the database can also be seen on that page inside [] and seperated by , with " " on each data. (e.g. ["Useless Code","Logan Wayne", "etc."] – Logan Wayne Jan 27 '14 at 00:12
3

When a string is used, the Autocomplete plugin expects that string to point to a URL resource that will return JSON data.

source: "autocomplete.php"

Therefore you need to return a JSON object.

$json = false;
while($row=mysqli_fetch_array($result))
{
    $json[] = array(
        'name' => $row['name']
    );
}
echo json_encode($json);
Ohgodwhy
  • 49,779
  • 11
  • 80
  • 110
  • I've made the source from availableTags variable to autocomplete.php, just like you said and used the JSON object, but still not working. – Logan Wayne Jan 23 '14 at 03:14
3

Your autocomplete.php file,

include('conn.php');
 $sql="SELECT * FROM oldemp";
 $result = mysqli_query($mysqli,$sql) or die(mysqli_error());

//Create an array
$arr = Array();
while($row=mysqli_fetch_array($result))
{
    array_push($arr,$row['name']);
}
header('Content-Type: application/json');
echo json_encode($arr)
?>

The result of this will be an JSON array which can be directly used in JavaScript. Hence, the script will be something like -

var availableTags = [];
$.ajax({
    url:"autocomplete.php",success:function(result){
    availableTags = result
}});
Kamehameha
  • 5,423
  • 1
  • 23
  • 28
  • Just like the first answer, autocomplete.php rightly fetched the data, but in the index page, autocomplete text box is still not working. Thanks for the answer though – Logan Wayne Jan 23 '14 at 07:42
3

Solved my problem.

Have the script like this:

<!-- WITHOUT THESE THREE BELOW, THE AUTOCOMPLETE WILL LOOK UGLY OR WILL NOT WORK AT ALL -->
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>

<script>

  $(function() {
    $( "#tags" ).autocomplete({
      source: "autocomplete.php"
    });
  });

</script>

And autocomplete.php (where we will get the data to fill the autocomplete input field):

<?php

  include("conn.php"); /* ESTABLISH CONNECTION IN THIS FILE; MAKE SURE THAT IT IS mysqli_* */

  $stmt = $con->prepare("SELECT description FROM table"); /* START PREPARED STATEMENT */
  $stmt->execute(); /* EXECUTE THE QUERY */
  $stmt->bind_result($description); /* BIND THE RESULT TO THIS VARIABLE */
  while($stmt->fetch()){ /* FETCH ALL RESULTS */
    $description_arr[] = $description; /* STORE EACH RESULT TO THIS VARIABLE IN ARRAY */
  } /* END OF WHILE LOOP */

  echo json_encode($description_arr); /* ECHO ALL THE RESULTS */

?>
Logan Wayne
  • 6,001
  • 16
  • 31
  • 49
0

Just a suggestion for the autocomplete file. Sorry, I would have added a comment above, but I don't have enough rep as of writing this.

After successfully implementing Useless Code's suggestion I was noticing my server overhead was going through the roof. It seemed bots were some how initiating the script, even though there was no letters being typed in the input area. I did a little test on the autocomplete file and found it would query my database even if the term was empty.

So, I just encpsulated the whole autocomplete script with an if statement... like so...

<?php

if(!empty($_GET['term']))
{
include('conn.php');

$term = $_GET['term'];
$term = mysqli_real_escape_string($mysqli, $term);
$output = array();

$sql = "SELECT name FROM oldemp WHERE name LIKE '" . $term . "%';";

$result = mysqli_query($mysqli,$sql) or die(mysqli_error());

while($row=mysqli_fetch_array($result))
{
$output[] = $row['name'];
}

mysqli_close($mysqli);

// output our results as JSON as jQuery expects
echo json_encode($output);
}
?>

... and now my server is back to normal.