CakePHP – Introduction to Ajax

After I have finished reading the Ajax Helper section in the Cookbook I felt unsatisfied. I mean it lacks more complex examples. To understand it better I played a bit with it. Below example is a compilation of my conclusions.

We have two simple models linked with one relation: every User belongs to a City.

We want to display all Users in a select box. Every time a user is selected, user’s city appears below the control.

DB tables for this example:

CREATE TABLE IF NOT EXISTS `cities` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

INSERT INTO `cities` (`id`, `name`) VALUES
(1, 'Warsaw'),
(2, 'Paris'),
(3, 'Lisbon');

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `city_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

INSERT INTO `users` (`id`, `name`, `city_id`) VALUES
(1, 'Anna', 1),
(2, 'Georges', 2),
(3, 'Rui', 3);

First of all, we generate every model, view and controller using the cake bake.

Secondly,  we prepare the view: views/users/index.ctp

<!-- 
Cake uses Prototype for Ajax operations, 
so first of all we need to link it to the 
desired view.
-->
<?php echo $javascript->link('/js/prototype'); ?>

<!--
Create select box.
-->
<?php echo $form->select('user',$users_list);?>

<!-- 
Create div for Ajax update. 
Inside this div there is a tiny gif indicating
that the update is loading.
-->
<!-- Ads observer to the select box, when the select box is updated call the function specified by 'url' property. While loading the Ajax update enable visibility of 'indicator'. --> <?php echo $ajax->observeField('user', array( //function called by Ajax 'url' =>'getCity', //id of element to be updated 'update' => 'city', //enable visibility of indicator 'indicator'=>'progress', )); ?>

Then we need to take care about the controller: controllers/user_controller.php. First, we have to add the needed helpers and components:

var $helpers = array('Html', 'Form', 'Javascript','Ajax');
var $components = array('RequestHandler');

…and then we need to prepare data to populate our select box.

function index() {
	(...)
	$users_list=$this->User->find('list');
	$this->set('users_list', $users_list);
}

In the last step for the controller we create a function that handle the Ajax request:

function getCity($id=null){
	if(!empty($this->data['user'])){
		$user=$this->User->read(null, $this->data['user']);
		$this->set('city',$user['City']['name']);
	}
	else{
		$this->set('city'," ");
	}
}

Update: Here I should mention that this view will be also accessible browser window. To avoid it we should check first if the request is really an Ajax request:

function getCity($id=null){
  if($this->RequestHandler->isAjax()){
    if(!empty($this->data['user'])){
	$user=$this->User->read(null, $this->data['user']);
	$this->set('city',$user['City']['name']);
    }
    else{
	$this->set('city'," ");
    }
  else{
      $this->Session->setFlash(__('Invalid request', true));
  }
}

Finally, we need to create a view for getCity() - views/users/get_city.ctp. This view will be loaded inside the defined tag in index view.

<?php echo $city; ?>

As a result every time we select a user from the select box the city of the selected user should appear below preceded by a progress indicator.

a

b

BTW: nice loading indicators generator: http://www.webscriptlab.com/