CakePHP: Auto populating foreign key dropdown fields

Richard's picture

One of the neet features of CakePHP's scaffolding is that it automatically populates your foreign key (belongsTo, hasAndBelongsToMany) fields in your forms.

Lets take a look at a simple example to illustrate this:

app/models/article.php:

class Article extends AppModel {
	var $hasAndBelongsToMany = array(
		"Tag"
	);
}

app/models/tag.php:

class Tag extends AppModel {
 
	var $hasAndBelongsToMany = array(
		"Article"
	);
}

app/controllers/article_controller.php:

class ArticleController extends AppController {
 
	var $scaffold;
}

app/controllers/tags_controller.php:

class TagController extends AppController {
 
	var $scaffold;
}

Now, when you visit http://webroot/articles/add you'll see that CakePHP has helpfully changed the foreign key field for Tags to a multi-select list of all the Tags:

Screenshot of CakePHP foreign key lookup

Once you switch off scaffolding and start creating your own add/edit methods you'll loose this handy feature. You have to manually add the following to the add/edit actions to re-enable this feature:

function add() {
 
	// usual add code
 
	// populate the foreign key lookups
	$this->set("Tags", $this->Article->Tag->find("list");
 
}

That can be a bit of a bind to remember to do that for each foreign key and for each view that needs this functionality.

Here's an automatic method for retaining this feature in your own hand built methods.

Add the following code to your AppController (so the functionality is inherited for all your controllers):

/**
 * Populates the view with variables required for foreign key fields
 * 
 * @param $models array	list of models to fetch lookups for
 * 						if empty, the list automatically populated with the associated models
 * 
 * @return none			view vars are set for each associated model
 */
function _populateLookups($models = array()) {
 
	if (empty($models)) {
 
		// build a list of all associated models:
 
		// create a reference to the Controllers root model
		// this is the first item in the controllers $uses array
		// or the default if $uses is empty
		$rootModel = $this->{$this->modelClass};
 
		// build list of belongsTo Models
		foreach($rootModel->belongsTo as $model=>$attr) {
 
			$models[] = $model;
 
		}
 
		// build list of hasAndBelongsToMany Models
		foreach($rootModel->hasAndBelongsToMany as $model=>$attr) {
 
			$models[] = $model;
 
		}
 
	}
 
	// populate the view vars with the lookup lists for each associated model
	foreach($models as $model) {
 
		// calculate the name of the variable to populate based on the model name
		$name = Inflector::variable(Inflector::pluralize($model)); 
 
		// populate the view var
		$this->set($name, $rootModel->{$model}->find("list"));
 
	}
 
}

What this does is to automate the process off populating those foreign key view vars. If you don't pass in a list of models it will dig out the associated models automatically.

Now we need to run this function every time we display an add or edit form. Add the following code to your AppController. If you already have a beforeRender() function you can append this code to end.

/**
 * Runs before the view is rendered
 * @see cake/libs/controller/Controller#beforeRender()
 */
function beforeRender() {
 
	switch($this->action) {
 
		case "add":
		case "edit":
 
			$this->_populateLookups();
			break;
 
	}
 
}

What this does is to check which action we are performing and then calls _populateLookups() if its an add or edit action.

Comments

Richard's picture

Source code markup

I've just noticed the source code markup is being double escaped.

Lines such as:

$this->set("Tags", $this->Article->Tag->find("list");

Should read:

$this->set("Tags", $this->Article->Tag->find("list");

I'm working on a fix for this :-)

Richard's picture

Issue is fixed

Added the following three lines to sites/all/modules/geshifilter/geshi/geshi.php:

$code = str_replace(">", ">", $code);
$code = str_replace("&lt;", "<", $code);
$code = str_replace("&amp;", "&", $code);

as recommended in the comments here: http://drupal.org/node/269937

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Post new comment

The content of this field is kept private and will not be shown publicly. If you have a Gravatar account, used to display your avatar.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <c>, <cpp>, <drupal5>, <drupal6>, <java>, <javascript>, <php>, <python>, <ruby>. Beside the tag style "<foo>" it is also possible to use "[foo]".

More information about formatting options