AJAX (Asynchronous JavaScript and XML) is an important part when building a dynamic web application. You can send requests to a server and update parts of a web page without reloading the whole page.
In CakePHP 4 CSRF protection is already enabled, you can use this to securely send AJAX requests.
In this tutorial, I show how you can send AJAX request using jQuery with and without a CSRF token in CakePHP 4 project.
Table of Content
- Create a Table and Insert Data
- Update Database Configuration
- Creating a Model
- Creating a Controller
- Include jQuery and CSRF token
- Create a Template
- Output
- Conclusion
1. Create a Table and Insert Data
In the example, I am using users
table. It has the following structure –
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 ) 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');
2. Update 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. Creating a Model
- Create
Users
Model.
bin/cake bake model Users
- This command will generate 2 files –
src/Model/Entity/User.php src/Model/Table/UsersTable.php
src/Model/Entity/User.php
This file contains your Entity class, where you can specify the field names that are eligible for insertion and updating. If there are fields you want to exclude from this process, you have two options:
- Remove the field name entirely from the Entity class.
- Set the field to
false
if you want to disallow insertion or updating for that specific field.
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 class serves a crucial role in your application’s Object-Relational Mapping (ORM). It informs the ORM which database table to use and defines validation rules for your 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. Creating a Controller
- Create a
UsersController.php
file insrc/Controller/
folder. - Create
UsersController
Class that extendsAppController
class. - Create 3 methods –
- index()
- fetchAllUsers() – Using method handle AJAX request.
Create an object of the Users
Table class. Use this object to fetch all records from the users
table and store them in a $usersList
variable.
Return the $usersList
in JSON format.
-
- fetchUserById() – This method also handles AJAX requests.
Read POST userid
value and assign it to $userid
variable. Create an object of Users
Table class to fetch a record from the users
table where id = $userid
.
Assign the fetched record to $userList
and return it in JSON format.
<?php declare(strict_types=1); namespace App\Controller; class UsersController extends AppController { public function index(){ } // Fetch all records public function fetchAllUsers(){ $users = $this->getTableLocator()->get('Users'); $query = $users->find('all')->order(['id' => 'ASC']); $usersList = $query->toArray(); echo json_encode($usersList); die; } // Fetch record by id public function fetchUserById(){ // POST value $userid = $this->request->getData()['userid']; // Fetch record $users = $this->getTableLocator()->get('Users'); $query = $users->find('all') ->where(['id ' => $userid]); $userList = $query->toArray(); echo json_encode($userList); die; } }
5. Include jQuery and CSRF token
I am including jQuery and CSRF token on templates/layout/default.php
file.
Stored CSRF token in <meta >
tag –
<?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
and jQuery in <head >
section –
<?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>
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 --> <?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?> <?= $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 a Template
Create a new Users
folder in templates/
folder. Now in the Users
folder create index.php
file – (templates/Users/index.php)
.
Create an input element for entering the user id for searching and create 2 buttons. 1st button is use to fetch all records and 2nd button is use to fetch record of entered userid in the search box.
Using <table id="usersTable">
to list fetched records using jQuery AJAX.
Script –
Read CSRF token from <meta >
tag and assign it to csrfToken
variable.
Define click
event on #btnfetchall
and #btnsearch
–
- #btnfetchall – From here send AJAX GET request.
Send AJAX GET request to $this->Url->build(['controller' => 'Users','action' => 'fetchAllUsers'])
, set dataType: 'json'
. On successful callback pass response
to createTableRows()
to add new <tr>
to <table >
.
- #btnsearch – From here send AJAX request with CSRF token.
Send AJAX POST request to $this->Url->build(['controller' => 'Users','action' => 'fetchUserById'])
, pass userid
as data, set dataType: 'json'
.
Pass CSRF token using header – 'X-CSRF-Token': csrfToken
.
On successful callback pass response
to createTableRows()
to add new <tr>
to <table>
.
Create 1 function –
- createTableRows() – Emtpy the
<table id="usersTable" > <tbody>
. Checkresponse
length. If it is greater than 0 then loop on theresponse
and append<tr>
to<table id="usersTable" > <tbody>
.
Completed Code
<div class="row"> <div class="col-6"> <?php echo $this->Form->control('search',['label' => 'Enter User id','type'=>'number','id'=>'search','class' => 'form-control']); echo $this->Form->button('Fetch all', ['type' => 'button','id'=>'btnfetchall']); echo $this->Form->button('Search', ['type' => 'button','id'=>'btnsearch','style'=>'margin-left: 15px;']); ?> <!-- Users list --> <table id="usersTable"> <thead> <tr> <th>ID</th> <th>Username</th> <th>Name</th> <th>Gender</th> <th>Email</th> <th>City</th> </tr> </thead> <tbody> </tbody> </table> </div> </div> <!-- Script --> <script type="text/javascript"> // Read CSRF Token var csrfToken = $('meta[name="csrfToken"]').attr('content'); jQuery(document).ready(function(){ // Fetch all records $('#btnfetchall').click(function(){ // AJAX request $.ajax({ url: "<?= $this->Url->build(['controller' => 'Users','action' => 'fetchAllUsers']) ?>", type: "get", dataType: 'json', success: function(response){ // Add rows to <table > createTableRows(response); } }); }); // Fetch single record $('#btnsearch').click(function(){ var userid = $('#search').val(); // AJAX request $.ajax({ url: "<?= $this->Url->build(['controller' => 'Users','action' => 'fetchUserById']) ?>", type: "post", data: {userid: userid}, dataType: 'json', headers:{ 'X-CSRF-Token': csrfToken }, success: function(response){ // Add rows to <table > createTableRows(response); } }); }); }); // Create table rows function createTableRows(response){ var len = 0; $('#usersTable tbody').empty(); // Empty <tbody> if(response != null){ len = response.length; } if(len > 0){ for(var i=0; i<len; i++){ var id = response[i].id; var username = response[i].username; var name = response[i].name; var gender = response[i].gender; var email = response[i].email; var city = response[i].city; var tr_str = "<tr>" + "<td align='center'>" + id + "</td>" + "<td align='center'>" + username + "</td>" + "<td align='center'>" + name + "</td>" + "<td align='center'>" + gender + "</td>" + "<td align='center'>" + email + "</td>" + "<td align='center'>" + city + "</td>" + "</tr>"; $("#usersTable tbody").append(tr_str); } }else{ var tr_str = "<tr>" + "<td align='center' colspan='4'>No record found.</td>" + "</tr>"; $("#usersTable tbody").append(tr_str); } } </script>
7. Output
8. Conclusion
In the example, I showed how you can send AJAX GET and POST request to fetch records from the MySQL database and read their data on the client side. You have to send CSRF token when sending AJAX POST request otherwise server returns a 403 error.
If your required response not returning from the AJAX request or getting an error then use the browser console or network tab to debug.
If you found this tutorial helpful then don't forget to share.