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.
Contents
- Create Table
- Database Configuration
- Create Model
- Create Controller
- Include jQuery and CSRF token
- Create Template
- Output
- Conclusion
1. Create Table
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. 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
UsersController.php
file insrc/Controller/
folder. - Create
UsersController
Class that extendsAppController
. - Create 3 methods –
- index()
- fetchAllUsers() – Using this method handle AJAX request.
Create an object of Users
Table class and use this to fetch all records from the users
table and assign to $usersList
. Return $usersList
in JSON format.
-
- fetchUserById() – This method is also use to handle AJAX request.
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.
Completed Code
<?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 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.