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 AjaxController::login(). 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).
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 AjaxHandlerComponent, 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!