Contact Us

Cloud Behavior

Posted on 31/8/08 by Felix Geisendörfer

Hey folks,

this is post #12 of my 30 day challenge.

While preparing the application we use for our Raleigh, NC workshop, I had to implement an algorithm to display the records of a table in tag-cloud kind of view. Since the approach I took turned out fairly neat I thought other people might like to use this, so I refactored everything into a little behavior this morning.

In order to download & install the behavior, please go here: Cloud Behavior at Debuggable Scraps.

The code for the behavior itself looks like this:

class CloudBehavior extends ModelBehavior{
  var $mapMethods = array('/^_findCloud$/' => '_findCloud');

  function setup(&$Model, $settings = array()) {
    if (!isset($this->settings[$Model->alias])) {
      $this->settings[$Model->alias] = array(
        'scale' => 2,
        'shuffle' => true,
        'query' => array(),
        'countField' => 'count',
    if (!is_array($settings)) {
      $settings = array();
    $Model->__findMethods['cloud'] = true;
    $this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings);

  function _findCloud(&$Model, $method, $state, $query, $results = array()) {
    if ($state == 'before') {
      $query = array_merge($query, $this->settings[$Model->alias]['query']);
      return $query;
    if (empty($results)) {
      return array();

    $countField = $this->settings[$Model->alias]['countField'];
    if (!$countField || !$Model->hasField($countField)) {
      trigger_error('CloudBehavior: You have to configure a valid countField for querying this Model\'s records as a cloud!');
      return array();

    $max = $results[0][$Model->alias][$countField];
    $min = $results[count($results)-1][$Model->alias][$countField];
    $range = $max - $min;
    if (!$range) {
      $range = 1;

    foreach ($results as &$command) {
      $command[$Model->alias]['scale'] =
        (($command[$Model->alias][$countField] - $min) / $range)
        * $this->settings[$Model->alias]['scale']
        + 1;
    if ($this->settings[$Model->alias]['shuffle']) {
    return $results;

Please note how I have to violate the concept behind private methods and properties to make this work. My follow up post on this topic is close to completion ; ).

The elegant part about this approach is that you can really keep your view logic down to a minimum:

$out = array();
foreach ($tags as $tag) {
  $size = 10 * $tag['Tag']['scale'];
  $out[] = $html->link(
    array('controller' => 'tags', 'action' => 'view', $tag['Tag']['id']),
    array('style' => 'font-size: '.$size.'px;')
echo join(', ', $out);

Anyway, I hope this code is helpful to some of you out there. Feedback is welcome as always. Please comment!

-- Felix Geisendörfer aka the_undefined


You can skip to the end and add a comment.

Mariano Guezuraga  said on Sep 22, 2008:

Hey Felix, can you confirm if this breaks in breaks in the latest revisions of cake? It was working ok in 1.2 rc2, but in rev. 7644 (and before) I get:

Warning (512): SQL Error: 1054: Unknown column 'cloud' in 'where clause' [CORE/cake/libs/model/datasources/dbo_source.php, line 518]

Query: SELECT `Tag`.`id`, `Tag`.`name`, `Tag`.`count` FROM `alm_tags` AS `Tag` WHERE cloud LIMIT 1

Felix Geisendörfer said on Sep 22, 2008:

Mariano Guezuraga: don't have any time to look into this right now, but if you find a moment to trace down what core change might have triggered this I could have a look and verify.

Mariano Guezuraga  said on Sep 23, 2008:

Thanks for your reply! It works ok in #7639 (and earlier) and starts to fail in #7640 (inclusive) My guess is "Making Model::$_findMethods protected"

This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.