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:
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:
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