Now that you have setup the Javascript and view to properly fire off an AJAX call, it's time to process the call within the Controller. The AJAX call was firing off to AjaxController::login(), so lets begin by creating the action. What we need to do is check to see if the username and password is set (validation), then attempt to login the user by using the AuthComponent. If the login is successful, or even if it fails, we will return a JSON response.
Below would be a basic setup to accomplish what we need to do. However, in your actual application you would apply more strict validation and sanitization, but for the sake of the tutorial I will not.
function login() {
$this->layout = 'ajax'; // Or $this->RequestHandler->ajaxLayout, Only use for HTML
$this->autoLayout = false;
$this->autoRender = false;
$response = array('success' => false);
if (!empty($this->data['User']['username']) && !empty($this->data['User']['password'])) {
if ($this->Auth->login($this->data)) {
$response['success'] = true;
$response['data'] = $this->data['User'];
} else {
$response['data'] = 'Username/password combo incorrect';
$response['code'] = 0;
}
} else {
$response['data'] = 'No username/password';
$response['code'] = -1;
}
$this->header('Content-Type: application/json');
echo json_encode($response);
return;
}
As an added bonus, I will supply an example of how to achieve the same result above using my AjaxHandlerComponent. The component does all the necessary preparations and output leaving you to write less code.
function login() {
if ($this->AjaxHandler->valid($this->data['username'], true) && $this->AjaxHandler->valid($this->data['password'], true)) {
if ($this->Auth->login($this->data)) {
$this->AjaxHandler->response(true, $this->data);
} else {
$this->AjaxHandler->response(false, 'Username/password combo incorrect', 0);
}
} else {
$this->AjaxHandler->response(false, 'No username/password', -1);
}
$this->AjaxHandler->respond();
return;
}
Basically what we need to do, to get a proper JSON response is to build the response array. We create an array with a success variable that would be a true or false. If the success is true that means the core logic that we want completed was a success, else the AJAX logic failed and the user should try again. We also have a data index that would house the error/success message(s), an array of data to use, or anything else you might need. Lastly we have a code index which could be used to differentiate the levels of errors and their importance. Once we have our response array complete, we set the correct headers and respond it back while encoding it with json_encode().
On top of building the response we need to configure the controller a bit. We need to disable the view and layout from rendering or we will receive the "Missing View" errors within Cake, and we also need to remove the layout so that our response is not surrounded by HTML (unless you want to respond with HTML, but we are using JSON). We do this by setting autoLayout and autoRender to false -- furthermore we set the layout to ajax which would be required for HTML responses.
You must be thinking to yourself, "Wow that was easy, we are now done!", but you are wrong! We still have one final step, validating that the request is a legitimate AJAX call. To achieve this we will use the RequestHandler::isAjax() method. Since all of our AJAX calls are housed in the AjaxController, we can place this code in the beforeFilter() so it applies to all actions. If your AJAX actions are within your primary controllers, you will need to add a bit more logic.
I will be using the SecurityComponent in my examples, but you can use die() or something similar in place of blackHole(), like Controller::redirect().
// Must be disabled or AJAX calls fail
$this->Security->validatePost = false;
if (!$this->RequestHandler->isAjax()) {
$this->Security->blackHole($this, 'You are not authorized to process this request!');
}
// Use the following code in place of blackHole()
// $this->redirect(null, 401, true);
The previous code should blackhole any request that does not contain the XMLHttpRequest header. To further build on this code, we can check to make sure the AJAX request is coming from the current domain, if not then kill it. Our final beforeFilter() should look something like the following.
function beforeFilter() {
parent::beforeFilter();
// Must be disabled or AJAX calls fail
$this->Security->validatePost = false;
if (!$this->RequestHandler->isAjax()) {
$this->Security->blackHole($this, 'You are not authorized to process this request!');
} else {
if (strpos(env('HTTP_REFERER'), trim(env('HTTP_HOST'), '/')) === false) {
$this->Security->blackHole($this, 'Invalid referrer detected for this request!');
}
}
}
The controller code should now be complete and ready for your amazing AJAX use. We could however improve it a little more by utilizing the RequestHandlerComponent and some of its methods, but ill leave that up to you to adventure into. In the next chapter we will go over the response and how to interact with the JSON.