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.phpfile. - Specify your database configuration details in the
Datasourcesdefault.
'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
UsersModel.
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
falseif 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.phpfile insrc/Controller/folder. - Create
UsersControllerClass that extendsAppControllerclass. - 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>. Checkresponselength. If it is greater than 0 then loop on theresponseand 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.