How to Add jQuery UI Autocomplete with AJAX in CakePHP 4

jQuery UI library provided different types of user interface widgets that are easy to implement on the page. One of the powerful widget is autocomplete.

It displays the suggestion list while typing in the input box. You can preload its data on the client side while initialization or load it dynamically using AJAX.

In this tutorial, I show how you can add jQuery UI Autocomplete in CakePHP 4 and load MySQL database data using jQuery AJAX.

How to Add jQuery UI Autocomplete with AJAX in CakePHP 4


Contents

  1. Create Table
  2. Database Configuration
  3. Create Model
  4. Create Controller
  5. Include jQuery and CSRF token
  6. Create Template
  7. Output
  8. Conclusion

1. Create Table

In the example, I am using users Tables. It has the following structure and data –

CREATE TABLE `users` (
    `id` int(11) NOT NULL,
    `username` varchar(50) NOT NULL,
    `name` varchar(60) NOT NULL,
    `gender` varchar(10) NOT NULL,
    `email` varchar(60) NOT NULL,
    `city` varchar(80) NOT NULL
);

ALTER TABLE `users`
    ADD PRIMARY KEY (`id`);

ALTER TABLE `users`
    MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

INSERT INTO `users` (`id`, `username`, `name`, `gender`, `email`, `city`) VALUES
(1, 'yssyogesh', 'Yogesh singh', 'male', 'yogesh@makitweb.com', 'Bhopal'),
(2, 'bsonarika', 'Sonarika Bhadoria', 'female', 'bsonarika@makitweb.com', 'Indore'),
(3, 'sunil', 'Sunil singh', 'male', 'sunil@makitweb.com', 'Pune'),
(4, 'vishal', 'Vishal Sahu', 'male', 'vishal@makitweb.com', 'Bhopal'),
(5, 'jiten', 'jitendra singh', 'male', 'jitendra@makitweb.com', 'Delhi'),
(6, 'shreya', 'Shreya joshi', 'female', 'shreya@makitweb.com', 'Indore'),
(7, 'abhilash', 'Abhilash namdev', 'male', 'abhilash@makitweb.com', 'Pune'),
(8, 'ekta', 'Ekta patidar', 'female', 'ekta@makitweb.com', 'Bhopal'),
(9, 'deepak', 'Deepak singh', 'male', 'deepak@makitweb.com', 'Delhi'),
(10, 'rohit', 'Rohit Kumar', 'male', 'rohit@makitweb.com', 'Bhopal'),
(11, 'bhavna', 'Bhavna Mahajan', 'female', 'bhavna@makitweb.com', 'Indore'),
(12, 'ajay', 'Ajay singh', 'male', 'ajay@makitweb.com', 'Delhi'),
(13, 'mohit', 'Mohit', 'male', 'mohit@makitweb.com', 'Pune'),
(14, 'akhilesh', 'Akhilesh Sahu', 'male', 'akhilesh@makitweb.com', 'Indore'),
(15, 'ganesh', 'Ganesh', 'male', 'ganesh@makitweb.com', 'Pune'),
(16, 'vijay', 'Vijay', 'male', 'vijay@makitweb.com', 'Delhi');

2. Database Configuration

  • Open config/app_local.php file.
  • Specify your database configuration details in the Datasources default.
'Datasources' => [
     'default' => [
          'host' => '127.0.0.1',
          /*
          * CakePHP will use the default DB port based on the driver selected
          * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
          * the following line and set the port accordingly
          */
          //'port' => 'non_standard_port_number',

          'username' => 'root',
          'password' => 'root',

          'database' => 'cakephp4',
          /*
          * If not using the default 'public' schema with the PostgreSQL driver
          * set it here.
          */
          //'schema' => 'myapp',

          /*
          * You can use a DSN string to set the entire configuration
          */
          'url' => env('DATABASE_URL', null),
     ],

     /*
     * The test connection is used during the test suite.
     */
     'test' => [
          'host' => 'localhost',
          //'port' => 'non_standard_port_number',
          'username' => 'my_app',
          'password' => 'secret',
          'database' => 'test_myapp',
          //'schema' => 'myapp',
          'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
     ],
],

3. Create Model

  • Create Users Model.
bin/cake bake model Users
  • This will create 2 files –
src/Model/Entity/User.php
src/Model/Table/UsersTable.php

src/Model/Entity/User.php

In this Entity class specify field names that you want to allow insertion and updation. You can either remove the field name or set it to false if you don’t want to allow.

Completed Code

<?php
declare(strict_types=1);

namespace App\Model\Entity;

use Cake\ORM\Entity;
class User extends Entity
{
     protected $_accessible = [
          'username' => true,
          'name' => true,
          'gender' => true,
          'email' => true,
          'city' => true,
     ];
}

src/Model/Table/UsersTable.php

This Table class tells ORM which table needs to use and defines validation rules for the fields.

Completed Code

<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{

      public function initialize(array $config): void
      {
            parent::initialize($config);

            $this->setTable('users');
            $this->setDisplayField('name');
            $this->setPrimaryKey('id');
      }

      public function validationDefault(Validator $validator): Validator
      {
            $validator
                ->scalar('username')
                ->maxLength('username', 50)
                ->requirePresence('username', 'create')
                ->notEmptyString('username');

            $validator
                ->scalar('name')
                ->maxLength('name', 60)
                ->requirePresence('name', 'create')
                ->notEmptyString('name');

            $validator
                ->scalar('gender')
                ->maxLength('gender', 10)
                ->requirePresence('gender', 'create')
                ->notEmptyString('gender');

            $validator
                ->email('email')
                ->requirePresence('email', 'create')
                ->notEmptyString('email');

            $validator
                ->scalar('city')
                ->maxLength('city', 80)
                ->requirePresence('city', 'create')
                ->notEmptyString('city');

            return $validator;
      }

      public function buildRules(RulesChecker $rules): RulesChecker
      {
            $rules->add($rules->isUnique(['username']), ['errorField' => 'username']);
            $rules->add($rules->isUnique(['email']), ['errorField' => 'email']);

            return $rules;
      }
}

4. Create Controller

  • Create a AutcompleteController.php file in src/Controller/ folder.
  • Create AutocompleteController Class that extends AppController.

Create 2 method –

  • index() 
  • getUserSuggestions() – Using this method handle jQuery UI AJAX requests and return autocomplete suggestion list.

Read POST search value and assign it to $search variable. Create an object of Users Table using – $this->getTableLocator()->get('Users'). If $search is not empty then set WHERE to search on name field using LIKE.

I have set limit of 5 you can remove it or adjust it according to your requirement while implementing on your project.

Create $data_arr Array to store return response and loop on the fetched records. Store $user['id'] to value key of $data_arr and $user['name'] to label key of $data_arr Array.

Return $data_arr in JSON format.

Completed Code

<?php
declare(strict_types=1);

namespace App\Controller;

class AutocompleteController extends AppController
{

      public function index(){

      }

      // Get Autocomplete Data
      public function getUserSuggestions(){

           ## POST value
           $search = trim($this->request->getData()['search']);

           ## Fetch users
           $USERS = $this->getTableLocator()->get('Users');

           $query = $USERS->find('all');

           // Search value
           if(!empty($search)){
                $query->where(['name LIKE' => "%".$search."%"]);
           }
           $query->order(['name' => 'ASC']);
           $query->limit(5);
           $usersList = $query->toArray();

           $data_arr = array();
           foreach($usersList as $user){
                $data_arr[] = array(
                      'value' => $user['id'],
                      'label' => $user['name']
                );
           }

           echo json_encode($data_arr);
           die;
      }

}

5. Include jQuery and CSRF token

I am including JS libraries and CSRF token on templates/layout/default.php file.

Stored CSRF token in <meta > tag –

<?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>

Include jQuery and jQuery UI library in <head > section –

<!-- jQuery UI CSS -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.13.2/themes/smoothness/jquery-ui.css">

<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>

<!-- jQuery UI JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"></script>

Completed Code

<?php
$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html>
<head>
     <?= $this->Html->charset() ?>
     <meta name="viewport" content="width=device-width, initial-scale=1">

     <!-- CSRF Token -->
     <?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
     <title>
           <?= $cakeDescription ?>:
           <?= $this->fetch('title') ?>
     </title>
     <?= $this->Html->meta('icon') ?>

     <link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">

     <?= $this->Html->css(['normalize.min', 'milligram.min', 'cake']) ?>
     <!-- jQuery UI CSS -->
     <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.13.2/themes/smoothness/jquery-ui.css">

     <!-- jQuery -->
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>

     <!-- jQuery UI JS -->
     <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"></script>

     <?= $this->fetch('meta') ?>
     <?= $this->fetch('css') ?>
     <?= $this->fetch('script') ?>
</head>
<body>
     <nav class="top-nav">
          <div class="top-nav-title">
               <a href="<?= $this->Url->build('/') ?>"><span>Cake</span>PHP</a>
          </div>
          <div class="top-nav-links">
               <a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
               <a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
          </div>
     </nav>
     <main class="main">
          <div class="container">
               <?= $this->Flash->render() ?>
               <?= $this->fetch('content') ?>
          </div>
     </main>
     <footer>
     </footer>
</body>
</html>

6. Create Template

Create Autocomplete folder in templates/ location. In the Autocomplete folder create index.php file – templates/Autocomplete/index.php.

Create 2 input elements  –

  • The first input element is use to initialize jQuery UI autocomplete.
  • The second input element is use to display the selected item value. I am displaying userid in this element.

jQuery

Read CSRF token from the <meta > tag and assign it to csrfToken variable.

Initialize autocomplete on #searchuser and set 3 options –

  • source – Using this option load data.

Send AJAX POST request to $this->Url->build(['controller' => 'Autocomplete','action' => 'getUserSuggestions']), set dataType: 'json', pass typed value as data – { search: request.term }, also pass CSRF token using headers – { 'X-CSRF-Token': csrfToken }.

On successful callback pass response data to response().

  • select – This event triggers when an item is selected from the suggestion list.

Store selected item label in #searchuser and value in #userid.

  • focus – This event triggers when navigating the list using the keyboard arrow keys.

Store selected item label in #searchuser and value in #userid.

Completed Code

<div class="row">
     <div class="col-6">

          <!-- -->
          <?php 
          echo $this->Form->control('searchuser',['id'=>'searchuser','label'=>'Search User','class' => 'form-control']);
          echo $this->Form->control('userid',['id'=>'userid','label' => 'Selected User ID','class' => 'form-control']);
          ?>
          <!-- -->

     </div>
</div>

<!-- Script -->
<script type="text/javascript">
// Read CSRF Token
var csrfToken = $('meta[name="csrfToken"]').attr('content');
$(document).ready(function(){

     // Initialize
     $( "#searchuser" ).autocomplete({

          source: function( request, response ) {

               // Fetch data
               $.ajax({
                    url: "<?= $this->Url->build(['controller' => 'Autocomplete','action' => 'getUserSuggestions']) ?>",
                    type: 'post',
                    dataType: "json",
                    data: {
                         search: request.term
                    },
                    headers:{
                         'X-CSRF-Token': csrfToken
                    },
                    success: function( data ) {
                         response( data );
                    }
               });
          },
          select: function (event, ui) {
               // Set selection
               $('#searchuser').val(ui.item.label); // display the selected text
               $('#userid').val(ui.item.value); // save selected id to input
               return false;
          },
          focus: function(event, ui){
               $( "#searchuser" ).val( ui.item.label );
               $( "#userid" ).val( ui.item.value );
               return false;
          },
     });

});
</script>

7. Output

View Output


8. Conclusion

jQuery UI autocomplete can greatly enhance the search functionality of your web pages it makes them more user-friendly and efficient. Users can easily find what they are looking for by simply typing a few characters, and the real-time search suggestions can significantly improve their experience.

I hope this tutorial has been helpful in getting you started.

You can also checkout this tutorial if you want to know auto-populate dropdown using jQuery AJAX in CakePHP 4.

If you found this tutorial helpful then don't forget to share.

Leave a Comment