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']) {
      srand();
      shuffle($results);
    }
    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(
    $tag['Tag']['name'],
    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

 
&nsbp;