Doing AJAX calls in CakePHP: Part 1 - The Front-end
Thursday, October 1st 2009, 4:30pm
Topics: CakePHP, jQuery, Tutorials
Tags: Ajax, Ajax Handler, Request, Response, Headers, Http, Json, Jquery
Views: 1,409, Comments: 5
- Doing AJAX calls in CakePHP: Part 1 - The Front-end
- Doing AJAX calls in CakePHP: Part 2 - The Controller
- Doing AJAX calls in CakePHP: Part 3 - The JSON Response
One of topics I always see pop up in the CakePHP Google Groups is how to correctly do AJAX calls in CakePHP. I will be splitting this up into 3 different entries, the first will be pertaining to the view/Javascript (front-end) and how to fire off the AJAX call, the second will deal with the Controller action and how to process the request, and the final entry will be dealing with the response (in JSON format).
Additionally I will be using jQuery for all my Javascript interactions. Some developers ask me if I have ever used CakePHP's built in Javascript helpers, and I always so no. I don't find the benefit in using the helpers when it causes extra parsing in the front-end by running it through PHP objects, when you can simply write out the function name manually within the HTML template, which takes no processing!
Now lets begin by creating your forms within the templates. I will be doing a user login system to demonstrate the AJAX calls. Take note that I am applying a "return false" on the form's onsubmit attribute. This will stop the form from posting when a user hits enter, so that they must use the AJAX trigger.
The form is pretty straight forward, so lets start out by creating our login() function within the Javascript. The login() function will be firing an AJAX call to the AjaxController's login action. Furthermore, I will NOT be doing any form input validation in the AJAX, feel free to figure it out yourself!
There are a few key points I would like to discuss first about the code above, before continuing with the tutorial.
1) AJAX functions
In my example I am using jQuery's built in ajax() function. However, you can use the alternative post() function, but as a personal preference I like to use ajax(). (I will also post an article about the difference between get/post and when to use each one).
2) Form input names
By default if you use jQuery's serialize() function, it will convert all inputs into name/value pairs and wrap them into a query string. This means it also includes the "data[Model][field]" setup when posting to your AjaxController, which in turn means you can access the data at $this->data['Model']['field'].
If you want to do it the old fashion way (or when its not possible to serialize()), you can create the query string yourself by grabbing the value of each input and building the name/value pairs (You can see this in the example below). However, if you do not include the "data[Model][]" around each of the named variables, you cannot access the data in the controller with $this->data, you would have to use the $this->params['form']['field']. Below is a quick example of the possible jQuery data values and how to access them in CakePHP.
If you go the path of not wrapping your fields in data[] and are using my AjaxHandler component, the component will automatically process and parse the jQuery posted data into the Controllers $this->data.
3) Response type (dataType)
One major feature of jQuery is the "dataType" variable within its AJAX calls. If you are responding with a JSON object with the HTTP header of application/json, be sure to set the dataType to json or your response will fail -- this goes the same for XML. If your responding with plain text or HTML you can omit the dataType variable.
Now that we have the form and Javascript written, lets test our AJAX call. If you have Firebug installed (which you should), open it beforehand to check the HTTP request and response. If everything worked correctly so far, you should see the following HTTP request and response headers (Do note, your headers will be similar, and not exactly the same!).
Basically what these headers are telling us is that our request was posted to /ajax/login/ and the server responded with an OK using the Content-Type of application/json. If you are unfamiliar with HTTP headers, I highly suggest you read the book "HTTP Developers Handbook, by Chris Shiflett.
That is all for now in this entry, stay tuned shortly for part 2 where I discuss the Controller and Action setup and how to properly respond to an AJAX call. Hope this helps you confused developers so far!
- Doing AJAX calls in CakePHP: Part 2 - The Controller
- Doing AJAX calls in CakePHP: Part 3 - The JSON Response
One of topics I always see pop up in the CakePHP Google Groups is how to correctly do AJAX calls in CakePHP. I will be splitting this up into 3 different entries, the first will be pertaining to the view/Javascript (front-end) and how to fire off the AJAX call, the second will deal with the Controller action and how to process the request, and the final entry will be dealing with the response (in JSON format).
Additionally I will be using jQuery for all my Javascript interactions. Some developers ask me if I have ever used CakePHP's built in Javascript helpers, and I always so no. I don't find the benefit in using the helpers when it causes extra parsing in the front-end by running it through PHP objects, when you can simply write out the function name manually within the HTML template, which takes no processing!
Now lets begin by creating your forms within the templates. I will be doing a user login system to demonstrate the AJAX calls. Take note that I am applying a "return false" on the form's onsubmit attribute. This will stop the form from posting when a user hits enter, so that they must use the AJAX trigger.
<?php echo $form->create('User', array('onsubmit' => 'return false;')); echo $form->input('username'); echo $form->input('password'); echo $form->end(); ?> <button type="button" onclick="login();">Login</button>
The form is pretty straight forward, so lets start out by creating our login() function within the Javascript. The login() function will be firing an AJAX call to the AjaxController's login action. Furthermore, I will NOT be doing any form input validation in the AJAX, feel free to figure it out yourself!
function login() { var data = $("#UserAddForm").serialize(); /* Or no serialization (Read #2 below) var username = $("#UserUsername").val(); var password = $("#UserPassword").val(); var data = "username="+ username +"&password="+ password; */ $.ajax({ type: "post", // Request method: post, get url: "/ajax/login/", // URL to request data: data, // Form variables dataType: "json", // Expected response type success: function(response, status) { // Will continue in part 3 }, error: function(response, status) { alert('An unexpected error has occurred!'); } }); return false; }
There are a few key points I would like to discuss first about the code above, before continuing with the tutorial.
1) AJAX functions
In my example I am using jQuery's built in ajax() function. However, you can use the alternative post() function, but as a personal preference I like to use ajax(). (I will also post an article about the difference between get/post and when to use each one).
Pro tip: The post() function calls ajax() internally, so ajax() in theory is faster because it takes less internal functions to fire. Ajax() also offers you more control and customization.
2) Form input names
By default if you use jQuery's serialize() function, it will convert all inputs into name/value pairs and wrap them into a query string. This means it also includes the "data[Model][field]" setup when posting to your AjaxController, which in turn means you can access the data at $this->data['Model']['field'].
If you want to do it the old fashion way (or when its not possible to serialize()), you can create the query string yourself by grabbing the value of each input and building the name/value pairs (You can see this in the example below). However, if you do not include the "data[Model][]" around each of the named variables, you cannot access the data in the controller with $this->data, you would have to use the $this->params['form']['field']. Below is a quick example of the possible jQuery data values and how to access them in CakePHP.
var data = "data[Model][field]=foobar"; $field = $this->data['Model']['field']; // Equals foobar var data = "data[field]=foobar"; $field = $this->data['field']; // Equals foobar var data = "field=foobar"; $field = $this->params['form']['field']; // Equals foobar
If you go the path of not wrapping your fields in data[] and are using my AjaxHandler component, the component will automatically process and parse the jQuery posted data into the Controllers $this->data.
3) Response type (dataType)
One major feature of jQuery is the "dataType" variable within its AJAX calls. If you are responding with a JSON object with the HTTP header of application/json, be sure to set the dataType to json or your response will fail -- this goes the same for XML. If your responding with plain text or HTML you can omit the dataType variable.
Pro Tip: If you are responding with a JSON content type, jQuery will automatically decode the JSON object for you (and apply it to the response argument for success, error, etc), so that you do not have to use a 3rd party library.
Now that we have the form and Javascript written, lets test our AJAX call. If you have Firebug installed (which you should), open it beforehand to check the HTTP request and response. If everything worked correctly so far, you should see the following HTTP request and response headers (Do note, your headers will be similar, and not exactly the same!).
// Request Headers POST /ajax/login/ HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) Accept: application/json, text/javascript, */* Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 73 // Response Headers HTTP/1.x 200 OK Server: nginx/0.8.15 Date: Thu, 01 Oct 2009 23:22:13 GMT Content-Type: application/json Transfer-Encoding: chunked Connection: keep-alive X-Powered-By: PHP/5.2.10 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache // Posted Variables data[User][password] password data[User][username] milesj
Basically what these headers are telling us is that our request was posted to /ajax/login/ and the server responded with an OK using the Content-Type of application/json. If you are unfamiliar with HTTP headers, I highly suggest you read the book "HTTP Developers Handbook, by Chris Shiflett.
That is all for now in this entry, stay tuned shortly for part 2 where I discuss the Controller and Action setup and how to properly respond to an AJAX call. Hope this helps you confused developers so far!
5 Comments
blackbooksingles.com
Oct 1st 2009, 21:29
<button type="button" onclick="login();">Login</button> With this: <?php echo $form->button('Login', array('onclick' => 'login();')); ?>www.milesj.me
Oct 1st 2009, 21:36
Oct 2nd 2009, 00:18
Just another suggestion, how comes you aren't using triggers? I.e .click?
www.milesj.me
Oct 2nd 2009, 09:04
Furthermore, I'm not a big fan of adding event listeners and binds unless its truly necessary. Binds are just a gimmick.
Oct 15th 2009, 14:45
I like that you hate JavaScript form validation as I despise it
I like that you use jQuery
I like that you like the lowest Ajax call Ajax() because it is more flexible
I liked your use of the serialize() method
I liked that you will use JSON format the format that most of the developers avoid it and I don't know why!!!