Error when implementing autocomplete with Cakephp 3

Friends, I’m trying to adapt this tutorial for my project in cakephp 3.8. I have a user table and I want to do a search by name.

After generating the code with the bake and inserting the search field, my index.ctp looks like this:

<html>
<body>
    <?= $this->Form->create('', ['type' => 'get']); ?>
    <?= $this->Form->control('keyword', array('class' => 'auto','name'=> 'search','value' => '','label' => false,'required' => true));?>
    <?= $this->Form->button(__('Search')); ?>
    <?= $this->Form->end();  ?>

    <nav class="large-3 medium-4 columns" id="actions-sidebar">
        <ul class="side-nav">
            <li class="heading"><?= __('Actions') ?></li>
            <li><?= $this->Html->link(__('New User'), ['action' => 'add']) ?></li>
        </ul>
    </nav>
    <div class="large-9 medium-8 columns content">
        <h3><?= __('Users') ?></h3>
        <table cellpadding="0" cellspacing="0">
            <thead>
                <tr>
                    <th scope="col"><?= $this->Paginator->sort('id') ?></th>
                    <th scope="col"><?= $this->Paginator->sort('name') ?></th>
                    <th scope="col"><?= $this->Paginator->sort('created') ?></th>
                    <th scope="col" class="actions"><?= __('Actions') ?></th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($users as $user): ?>
                <tr>
                    <td><?= $this->Number->format($user->id) ?></td>
                    <td><?= h($user->nome) ?></td>
                    <td><?= h($user->created) ?></td>
                    <td class="actions">
                        <?= $this->Html->link(__('View'), ['action' => 'view', $user->id]) ?>
                        <?= $this->Html->link(__('Edit'), ['action' => 'edit', $user->id]) ?>
                        <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $user->id], ['confirm' => __('Are you sure you want to delete # {0}?', $user->id)]) ?>
                    </td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
        <div class="paginator">
            <ul class="pagination">
                <?= $this->Paginator->first('<< ' . __('first')) ?>
                <?= $this->Paginator->prev('< ' . __('previous')) ?>
                <?= $this->Paginator->numbers() ?>
                <?= $this->Paginator->next(__('next') . ' >') ?>
                <?= $this->Paginator->last(__('last') . ' >>') ?>
            </ul>
            <p><?= $this->Paginator->counter(['format' => __('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')]) ?>
            </p>
        </div>
    </div>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="http://code.jquery.com/ui/1.10.1/jquery-ui.min.js"></script>
    <script type="text/javascript">
    $(function() {
        $(".auto").autocomplete({
            source: "search.php",
            minLength: 1
        });
    });
    </script>
</body>
</html>

Here in this part, I don’t know how to adapt.

 //autocomplete
    $(".auto").autocomplete({
        source: "/users",
        minLength: 1
    });  

Here in this part, I don’t know how I could adapt.

My UsersController.php looked like this:

    public function index()
    {
      $users= $this->paginate = [
            'conditions' => [
                'name like' => '%'. $this->request->getQuery('keyword') . '%',            
            ]
        ];
        $keyword = $this->Users->find('all');
        $this->set('users', $this->paginate($keyword));
    }

At the moment, for any search I get “No search results.”, Even if the name exists in the database.
I appreciate if anyone can analyze. Any comment is welcome!

I’m not sure of the detail of the link you should have for your autocomplete js code but here is a technique you can use to find out.

Write some test code in your template use $this->Html->link() to create a link to the call point that will handle your search. Load the page and look at the resulting link in the html. That’s what you need to use as your source in the js.

I made these changes:

.
.
 <?php use Cake\Routing\Router; ?>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="http://code.jquery.com/ui/1.10.1/jquery-ui.min.js"></script>

    <script>
    $(function() {
        $(".auto").autocomplete({
            minLength: 1,
            source: function(request, response) {
                $.ajax({
                    type: "GET",
                    source: "/users",
                    url: "<?php echo Router::url(array('controller' => 'Users', 'action' => 'index')); ?>" +
                        '?keyword=' +
                        request.keyword,
                    dataType: "json",
                    success: function(data) {
                        response(data);
                        console.log(data);
                    }
                });
            }
        });
    });
    </script>

But I still get: No search results.
When I press enter, the search works, but the autocomplete does not.

If I understand correctly, your search method is being properly called and is returning the expected data but the changes you anticipate in the rendered page are not occurring.

I’m not sure what the atuocomplete js function is supposed to do. Based on your code, you are making an assumption that autocomplete will recieve the data from your ajax call and incorporate it into your page.

In a normal ajax call (not embedded inside the autocomplete function, the ajax success function would have to make the DOM changes… In this case, the data would have to be a structure that matched autocomplete's expectations. I wonder what those are.

I guess your next step is to insure you understand what structure autocomplete expects in the data and insure your data matches that structure.

Perfect. I’ll take it easy. In the tutorial he uses this code snippet. That way when we start typing, it already searches the database.

<script type="text/javascript">
$(function() {
    
    //autocomplete
    $(".auto").autocomplete({
        source: "search.php",
        minLength: 1
    });                

});
</script>
````

Yeah. Framework code like this is super useful… once I learn all the context and assumptions in the background. I never seem to be successful out of the box with ‘simple’ calls like this. I always end up on an excursion to the center of the earth. Then I reach some point where I understand and the simple call makes perfect sense… and magically works!

I was able to access the users’ names. But when I type, regardless of the search, it loads all users. It does not filter according to the research.

search.ctp

<?php
use Cake\Routing\Router;
use Cake\View\Helper\UrlHelper;
?>

<div class="ui-widget">
<?php
        echo $this->Form->create('Users', array('action' => 'search'));
        echo $this->Form->input('name',array('id' => 'Autocomplete', 'class' => 'Autocomplete')); 
       
        echo $this->Form->end();
    ?>
</div>

<div class="ui-widget" style="margin-top:2em; font-family:Arial">
  Result:
  <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div>
</div>
        <script src="//code.jquery.com/jquery-1.10.2.js"></script>
  <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
    <script type="text/javascript">
      $(document).ready(function($){
    $('#Autocomplete').autocomplete({
  source:'<?php echo Router::url(array("controller" => "Users", "action" => "search")); ?>',
  minLength: 1
    });  });
</script>

Method.

 public function search() 
    {
     if ($this->request->is('ajax')) {
         
         $this->autoRender = false;            
         $name = $this->request->getData('term');            
         $results = $this->Users->find('all', array(
        'conditions' => array('Users.name LIKE ' => '%' . $name . '%')
        ));
         
         $resultArr = array();
         foreach($results as $result) {
            $resultArr[] = array('label' =>$result['name'] , 'value' => $result['name'] );
         }
         echo json_encode($resultArr);              
}}

I managed to move forward a little, but the filter doesn’t work.

I managed to solve it.

trim($this->request->query['term'])

Thank you for your help!

In case you’re wondering why this change worked, in the request object, “data” is for form contents that are posted, and “query” is for values from the URL. Developer tools in your browser are invaluable for debugging this sort of thing; you can see exactly what is being sent to the server and what’s being received in response, so the difference between a POST and a GET are very obvious there.