How to Add Select2 in CakePHP 4 and Load data using jQuery AJAX

Select2 is a popular jQuery plugin that makes the default HTML select element a lot more functional. With <select > element it is not possible to search on the list and add an image to an option, or customize the UI.

Select2 allows the loading of data dynamically from the server using AJAX when the user clicks on an element or search.

In this tutorial, I show how you can add select2 in CakePHP 4 and load data dynamically from the MySQL database using jQuery AJAX.

How to add Select2 in CakePHP 4 and Load data using jQuery AJAX


Table of Content

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

1. Create a Table and Insert records

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
);

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`) VALUES
(1, 'yssyogesh', 'Yogesh singh', 'male', 'yogesh@makitweb.com'),
(2, 'bsonarika', 'Sonarika Bhadoria', 'female', 'bsonarika@makitweb.com'),
(3, 'sunil', 'Sunil singh', 'male', 'sunil@makitweb.com'),
(4, 'vishal', 'Vishal Sahu', 'male', 'vishal@makitweb.com'),
(5, 'jiten', 'jitendra singh', 'male', 'jitendra@makitweb.com'),
(6, 'shreya', 'Shreya joshi', 'female', 'shreya@makitweb.com'),
(7, 'abhilash', 'Abhilash namdev', 'male', 'abhilash@makitweb.com'),
(8, 'ekta', 'Ekta patidar', 'female', 'ekta@makitweb.com'),
(9, 'deepak', 'Deepak singh', 'male', 'deepak@makitweb.com'),
(10, 'rohit', 'Rohit Kumar', 'male', 'rohit@makitweb.com'),
(11, 'bhavna', 'Bhavna Mahajan', 'female', 'bhavna@makitweb.com'),
(12, 'ajay', 'Ajay singh', 'male', 'ajay@makitweb.com'),
(13, 'mohit', 'Mohit', 'male', 'mohit@makitweb.com'),
(14, 'akhilesh', 'Akhilesh Sahu', 'male', 'akhilesh@makitweb.com'),
(15, 'ganesh', 'Ganesh', 'male', 'ganesh@makitweb.com'),
(16, 'vijay', 'Vijay', 'male', 'vijay@makitweb.com');

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 a 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.

Full 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,
     ];
}

src/Model/Table/UsersTable.php

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

Full 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');

            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 a Controller

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

Create 2 method –

  • index() 
  • getUserList() – Using this handle AJAX request and return select2 data.

If searchTerm is POST then assign its value to $search variable. Fetch records from the users table and set the WHERE clause to search on name field if $search value is not empty.

I set a limit of 5 that you either remove or adjust its value according to your requirement.

Loop on the fetched records and initialize $data_arr Array. Store $user['id'] to id key and $user['name'] to text key.

NOTE – Key name must be id and text otherwise data will not get loaded properly in the select2 element.

Return $data_arr Array in JSON format.

Full Code

<?php
declare(strict_types=1);

namespace App\Controller;

class Select2Controller extends AppController
{

     public function index(){

     }

     // Get Select2 Data
     public function getUserList(){

          // POST value
          $search = "";
          if(isset($this->request->getData()['searchTerm'])){
               $search = trim($this->request->getData()['searchTerm']);
          }

          ## 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(
                   'id' => $user['id'],
                   'text' => $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 Select2 library in <head > section –

<!-- Select2 CSS --> 
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />

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

<!-- Select2 JS --> 
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

Full 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']) ?>

     <!-- Select2 CSS -->
     <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />

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

     <!-- Select2 JS -->
     <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.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 Select2 folder in templates/ location. In the Select2 folder create index.php file – templates/Select2/index.php.

Create 3 HTML elements  –

  1. <select > element to initialize select2. Added no option.
  2. A textbox to display selected item value.
  3. Button to read selected value when gets clicked.

jQuery

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

Initialize Select2 –

Initialize select2() on #seluser, use ajax option to load option list.

Send AJAX POST request to $this->Url->build(['controller' => 'Select2','action' => 'getUserList']), set dataType: 'json', pass CSRF token using headers – 'X-CSRF-Token': csrfToken, pass the typed value as data – searchTerm: params.term.

With processResults handle AJAX successful callback response. Pass response to results and return.

Read selected value from Select2 – 

Define click event on #btnview button. Read selected value using val() on <select > element. Pass selected value to #userid textbox for displaying.

Full Code

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

         <!-- User -->
         <div class="input" style="margin-bottom: 15px;">
             <label for='seluser'>User</label>
             <?php 
             echo $this->Form->select(
                 'seluser',
                 [],
                 [
                     'id' => 'seluser',
                     'empty' => '-- Select User --',
                     'style' => 'width: 250px;'
                 ]
             );
             ?>
         </div>

         <!-- Selected User ID -->
         <?php 
         echo $this->Form->control('userid',['id'=>'userid','label' => 'Selected User ID','class' => 'form-control']);
         echo $this->Form->button('view', ['type' => 'button','id'=>'btnview','value'=>"View Selected User ID"]);
         ?>
         <!-- -->
     </div>
</div>

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

    // Select2
    $("#seluser").select2({
         ajax: { 
              url: "<?= $this->Url->build(['controller' => 'Select2','action' => 'getUserList']) ?>",
              type: "post",
              dataType: 'json',
              delay: 250,
              headers:{
                   'X-CSRF-Token': csrfToken
              },
              data: function (params) {
                   return {
                        searchTerm: params.term // search term
                   };
              },
              processResults: function (response) {
                   return {
                        results: response
                   };
              },
              cache: true
         }
    });

    // Read selected value
    $('#btnview').click(function(){
         var userid = $('#seluser').val();
         $('#userid').val(userid);
    });

});
</script>

7. Output

View Output


8. Conclusion

You can easily implement select2 on your project by following the above steps and providing users with an improved user interface that is both efficient and user-friendly.

In the example, I only passed typed search value as data but you can also pass additional data with it.

You can also check other jQuery AJAX tutorials in CakePHP 4 –

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

Leave a Comment