Monday, August 31, 2009

CakePHP 1.2 – Introduction to Ajax

After I have finished reading the Ajax Helper section in the Cookbook I felt unsatisfied - 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.

The goal is 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 have to generate every model, view and controller using the cake bake.

Secondly,  we have to 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.
-->
<div id='city'>
<img src="/ajax/img/progress.gif" alt="progress" id="progress" style="display:none">
</div>

<!-- 
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 the 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/

1 comment:

  1. Hi...
    This was a good one...It really helped me to study ajax.Thankz

    ReplyDelete