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
You can skip to the end and add a comment.
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.
Thanks for your reply! It works ok in #7639 (and earlier) and starts to fail in #7640 (inclusive)
https://trac.cakephp.org/changeset/7640. My guess is "Making Model::$_findMethods protected"
Mariano Guezuraga: Thanks, fixed in:
http://github.com/felixge/debuggable-scraps/commit/df3c8b68a7bd78fa2f2625b3e8f16101d23ac167
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.
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