I wanted some auto populating Select boxes in a site I was creating so that when I changed a Category the Subcategories would automatically update. CakePHP can do this pretty easily but it is let down by the documentation as there are no examples.
Initially I came up with a version that wrote JSON into a JavaScript variable in the page and then used jQuery to achieve the updating of select elements but I wanted to do this the Cake way which uses AJAX and as little code as possible.
Here is a simplified solution to demonstrate how it can be done.
This assumes a new CakePHP site already configured with a database connection. I used CakePHP 2.0.3 but this may also work with 1.3 (?)
1. Models
We need 3 associated tables
CREATE TABLE `categories` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(60) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `categories` (`id`, `name`) VALUES (1,'books'), (2,'music'), (3,'electronics'); CREATE TABLE `posts` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(50) DEFAULT NULL, `subcategory_id` INT(10) UNSIGNED DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `posts` (`id`, `title`, `subcategory_id`) VALUES (1,'The title',1), (2,'A title once again',4), (3,'Title strikes back',7); CREATE TABLE `subcategories` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `category_id` INT(10) UNSIGNED DEFAULT NULL, `name` VARCHAR(60) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `subcategories` (`id`, `category_id`, `name`) VALUES (1,1,'fiction'), (2,1,'biography'), (3,1,'children'), (4,2,'classical'), (5,2,'rock'), (6,2,'jazz'), (7,3,'camera'), (8,3,'audio'), (9,3,'tv');
Bake the 3 Models for these and allow CakePHP to define model associations for you.
2. Controllers
Bake a Controller for the Post Model with the default CRUD actions.
While you are at it you will also need to bake the CRUD Views for the Post Controller.
If you browse to the posts index page now you can view the data.
In this example I will add the Category and Subcategory select lists to the Add view, so now we need to change this so that the selection in the second list changes according to the selection in the first list.
This will be done via the Js Helper so make it available at the top of the Posts controller (after the Class declaration) with:
public $helpers = array('Js');
You need a Subcategories Controller with a single action to provide the data via AJAX:
<?php App::uses('AppController', 'Controller'); class SubcategoriesController extends AppController { public function getByCategory() { $category_id = $this->request->data['Post']['category_id']; $subcategories = $this->Subcategory->find('list', array( 'conditions' => array('Subcategory.category_id' => $category_id), 'recursive' => -1 )); $this->set('subcategories',$subcategories); $this->layout = 'ajax'; } }
3. Views
The view is a very simple AJAX view that renders the option tags that go within the select tag.
<!-- file path View/Subcategories/get_by_category.ctp --> <?php foreach ($subcategories as $key => $value): ?> <option value="<?php echo $key; ?>"><?php echo $value; ?></option> <?php endforeach; ?>
4. Putting it all together
In the Post Add view, file path: View/Posts/add.ctp we can finally add the Js methods that make the dynamic updating happen. This is the cryptic bit that I struggled with for a few hours as although the CakePHP documentation outlines all the options there are not any complete examples.
Firstly add a categories select element to the form (and change the order of the elements):
<?php echo $this->Form->input('category_id'); echo $this->Form->input('subcategory_id'); echo $this->Form->input('title'); ?>
Add the following code to the bottom of the view, this uses the Js Helper to create the necessary jQuery to perform the updating:
<?php $this->Js->get('#PostCategoryId')->event('change', $this->Js->request(array( 'controller'=>'subcategories', 'action'=>'getByCategory' ), array( 'update'=>'#PostSubcategoryId', 'async' => true, 'method' => 'post', 'dataExpression'=>true, 'data'=> $this->Js->serializeForm(array( 'isForm' => true, 'inline' => true )) )) ); ?>
This is saying – watch the HTML element with Id PostCategoryId for a change. When it changes update the HTML element with Id PostSubcategoryId with the response from subcategories/GetByCategory, the data option is used to send the current value from the initial select element.
Before you leap to test this you need to make changes to the default layout to include jQuery and provide a place for your scripts to be written out.
<?php // file path View/Layouts/default.ctp ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <?php echo $this->Html->charset(); ?> <title> <?php echo $title_for_layout; ?> </title> <?php echo $this->Html->meta('icon'); echo $this->Html->css('cake.generic'); ?> </head> <body> <div id="container"> <div id="header"> <h1>Dynamic Select Box Demonstration</h1> </div> <div id="content"> <?php echo $this->Session->flash(); ?> <?php echo $content_for_layout; ?> </div> <div id="footer"> footer </div> </div> <?php echo $this->element('sql_dump'); ?> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> <!-- scripts_for_layout --> <?php echo $scripts_for_layout; ?> <!-- Js writeBuffer --> <?php if (class_exists('JsHelper') && method_exists($this->Js, 'writeBuffer')) echo $this->Js->writeBuffer(); // Writes cached scripts ?> </body> </html>
Now you can test the application. On changing the category select list, the subcategory one will automatically update to show the relevant options.

Im new to this Cakephp and currently im using cakephp 1.3.
My Question is is cakephp1.3 compatible with your tutorial because i follow the the step but it seem it cant populate the 2nd select list.
Please help me ^_^
This is what I’m looking for! Since it’s my first CakePhp project I have lots of doubts, for example, what do you mean by “Bake the 3 Models for these and allow CakePHP to define model associations for you. “.
Does it means to create the CRUD model, view, controller files for each table through console?
Thanks!
Well, it finally worked!!! I did all the baking for my models through the command prompt, had to do it twice, but it’s all worth it
I’m using CakePHP 2.0 and now I have to a second level, hopefully I’ll have this done this year!
It’s the first time I use a framework and all I have to say is CakePHP rocks!!!
Found this very useful. Thank you.
Very very util Richard.
Thanks!!
Great post, you saved my job hahaha. I spend almost 2 days to find this. Good job ^^
And, it works in 1.3, just remove the line “App::uses(‘AppController’, ‘Controller’);”
and change “$this->request->data['Post']['category_id'];” to “$this->data['Post']['category_id'];” in in SubcategoriesController.
Seriously thank you for this tutorial. Works great.
Bonus thanks to that Vinicius dude for the help with getting it working on 1.3
Thanks for this tutorial, excellent work.
If you are having trouble getting this to work, as I was, pay mind to your layout file. If you are using a default layout for all pages you have to add $this->layout = false to the getByCategory function.