Search code examples
javascriptphpajaxsearchprepared-statement

Live search returns "undefined" even though the JSON array contains "objects"


I am facing issues retrieving real-time user data via AJAX request from MySQL database. Currently the PHP script ("live_search.php") only returns the value "object" and hence the variables print "undefined".

This is the result: /Users/Jakob/Desktop/Screenshot

enter image description here

I guess the issue is somewhere in the PHP script.

<input type="text" name="username_search" id="textbox" />
<div id="result"></div>

<script type="text/javascript">

        var textBox = document.getElementById('textbox'),
            resultContainer = document.getElementById('result')

        // keep this global to abort it if already a request is running even textbox is upated
        var ajax = null;
        var loadedUsers = 0; // number of users shown in the results

        textBox.onkeyup = function() {
            // "this" refers to the textbox
            var val = this.value;

            // trim - remove spaces in the begining and the end
            val = val.replace(/^\s|\s+$/, "");

            // check if the value is not empty
            if (val !== "") {
                // search for data
                searchForData(val);
            } else {
                // clear the result content
                clearResult();
            }
        }


        function searchForData(value, isLoadMoreMode) {
            // abort if ajax request is already there
            if (ajax && typeof ajax.abort === 'function') {
                ajax.abort();
            }

            // nocleaning result is set to true on load more mode
            if (isLoadMoreMode !== true) {
                clearResult();
            }

            // create the ajax object
            ajax = new XMLHttpRequest();
            // the function to execute on ready state is changed
            ajax.onreadystatechange = function() {
                if (this.readyState === 4 && this.status === 200) {
                    try {
                        var json = JSON.parse(this.responseText)
                        window.alert(json);
                    } catch (e) {
                        noUsers();
                        return;
                    }

                    if (json.length === 0) {
                        if (isLoadMoreMode) {
                            alert('No more to load');
                        } else {
                            noUsers();
                        }
                    } else {
                        showUsers(json);
                    }

                }
            }
            // open the connection
            ajax.open('GET', 'live_search.php?username_search=' + value + '&startFrom=' + loadedUsers , true);
            // send
            ajax.send();
        }

        function showUsers(data) {
            // the function to create a row
            function createRow(rowData) {
                // creating the wrap
                var wrap = document.createElement("div");
                // add a class name
                wrap.className = 'row'

                // name holder
                var name = document.createElement("span");
                name.innerHTML = rowData.name;

                var link = document.createElement("span");
                link.innerHTML = rowData.link;

                wrap.appendChild(name);
                wrap.appendChild(link);

                // append wrap into result container
                resultContainer.appendChild(wrap);
            }

            // loop through the data
            for (var i = 0, len = data.length; i < len; i++) {
                // get each data
                var userData = data[i];
                // create the row (see above function)
                createRow(userData);
            }

            //  create load more button
            var loadMoreButton = document.createElement("span");
            loadMoreButton.innerHTML = "Load More";
            // add onclick event to it.
            loadMoreButton.onclick = function() {
                // searchForData() function is called in loadMoreMode
                searchForData(textBox.value, true);
                // remove loadmorebutton
                this.parentElement.removeChild(this);
            }
            // append loadMoreButton to result container
            resultContainer.appendChild(loadMoreButton);

            // increase the user count
            loadedUsers += len;
        }

        function clearResult() {
            // clean the result <div>
            resultContainer.innerHTML = "";
            // make loaded users to 0
            loadedUsers = 0;
        }

        function noUsers() {
            resultContainer.innerHTML = "No Users";
        }

    </script>

//php script

<?php
require 'db.php';
session_start();

$username = $_GET['username_search'];
$startFrom = $_GET['startFrom'];

$username = trim(htmlspecialchars($username));
$startFrom = filter_var($startFrom, FILTER_VALIDATE_INT);

// make username search friendly
$like = '%' . strtolower($username) . '%'; // search for the username, case-insensitive
$statement = $mysqli -> prepare('
    SELECT first_name, url_user FROM user 
    WHERE lower(first_name) LIKE ? 
    ORDER BY INSTR(first_name, ?), first_name
    LIMIT 6 OFFSET ?
');

if (
    // $mysqli -> prepare returns false on failure, stmt object on success
    $statement &&
    // bind_param returns false on failure, true on success
    $statement -> bind_param('ssi', $like, $username, $startFrom ) &&
    // execute returns false on failure, true on success
    $statement -> execute() &&
    // same happens in store_result
    $statement -> store_result() &&
    // same happens here
    $statement -> bind_result($first_name, $url_user)
) {
    // I'm in! everything was successful.

    // new array to store data
    $array = [];


    while ($statement -> fetch()) {
        $array[] = [
            'first_name' => $first_name,
            'url_user' => $url_user
        ];
    }

    echo json_encode($array);
    exit();


}

The expected result is to have a live search which returns user data in real time. In addition, I am aiming for making the row a link to the respective user (given the url_link). The load more button is secondary and not too important.


Solution

  • PHP does not return object. When you alert the JSON, the variable json is an object. Your undefined output comes from the lines:

    // name holder
    var name = document.createElement("span");
    name.innerHTML = rowData.name;
    
    var link = document.createElement("span");
    link.innerHTML = rowData.link;
    

    Your PHP returns an array of objects with attributes first_name & url_user. You do not change them anywhere in between, so name & link are undefined.

    Additionally, your PHP is too bloated, and you can make it simpler like this for example:

    <?php
    
    // put this line inside the db.php before making the mysqli connection
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    require 'db.php';
    session_start();
    
    $username = $_GET['username_search'];
    $startFrom = $_GET['startFrom'];
    
    $username = trim(htmlspecialchars($username)); // <- don't do this, it hurts your data
    $startFrom = filter_var($startFrom, FILTER_VALIDATE_INT);
    
    // make username search friendly
    $like = '%' . strtolower($username) . '%'; // search for the username, case-insensitive
    $statement = $mysqli->prepare('
        SELECT first_name, url_user FROM user 
        WHERE lower(first_name) LIKE ? 
        ORDER BY INSTR(first_name, ?), first_name
        LIMIT 6 OFFSET ?
    ');
    
    $statement->bind_param('ssi', $like, $username, $startFrom);
    $statement->execute();
    $array = $statement->get_result()->fetch_all();
    echo json_encode($array);