For those few who don't know, behaviors are "extensions" for models. In other words, they are reusable model logic. They help you keep your code DRY, easily maintainable and portable. In this case, we're creating a hit counter behavior, hitcount for short.
When I first began writing this behavior, I was a bit scared because I didn't have any idea where to start from. Then I've looked at some other behaviors and the Cake core itself and decided this was as easy as anything else written in Cake.
My initial version wasn't really as good, and I needed some help. Luckily for me, I did get some help.
But, let us take a look at the behavior itself.
To begin, create the file
/app/models/behaviors/hitcount.php
and type in the following:
class HitcountBehavior extends ModelBehavior
{
var $name = 'Hitcount';
var $__defaultOptions = array('keyField' => 'id', 'hitField' => 'hitcount');
var $mapMethods = array('/hit/' => 'hit');
function setup(&$Model, $settings = array())
{
if (!isset($this->settings[$Model->alias]))
{
if (empty($settings))
{
$this->settings[$Model->alias] = $this->__defaultOptions;
}
else if (is_array($settings))
{
$this->settings[$Model->alias] = array_merge($this->__defaultOptions, $settings);
}
}
}
function hit(&$Model, $key = null, $settings = null)
{
$_settings = array();
if (isset($this->settings[$Model->alias]))
$_settings = $this->settings[$Model->alias];
if (is_array($settings))
$_settings = array_merge($_settings, $settings);
if (!isset($_settings['hitField']) || !isset($_settings['keyField']))
{
$this->log('Miscofigured hitcount behavior!', LOG_WARNING);
return;
}
if (!$Model->hasField($_settings['keyField']))
return;
if (!$Model->hasField($_settings['hitField']))
return;
if (empty($key))
$key = $Model->id;
if (empty($key))
{
$this->log('Invalid call to hitcount behavior!', LOG_WARNING);
return;
}
$Model->updateAll
(
array ($_settings['hitField'] => $Model->alias.'.'.$_settings['hitField'].' + 1'),
array ($Model->alias.'.'.$_settings['keyField'] => $key)
);
}
}
And now for some naughty bits.
1. $mapMethods bit
var $mapMethods = array('/hit/' => 'hit');
This is a little something I've learned from a presentation about behaviors by Mariano Inglesias, called "Behaviors - Making Your Models Behave". Apparently, by using the $mapMethods variable, you can create magic methods, just like the model does with findBy*.
This means that you can call the hit method of this behavior by calling the $Model->hit(...) which will be mapped to $Model->Hitcount->hit(...) Pretty handy, right?
2. UpdateAll bit
$Model->updateAll
(
array ($_settings['hitField'] => $Model->alias.'.'.$_settings['hitField'].' + 1'),
array ($Model->alias.'.'.$_settings['keyField'] => $key)
);
As mentioned in previously mentioned discussion, updateAll(...) is used for something completely different than the regular model calls. This is a nice thing to know, I really had no clue about it. One of the tricky parts was the $Model->alias prefix, which has to be used exactly as above.
Usage
I won't elaborate on how to use this behavior, I will just show you how do I use it in Neutrino, to count article reads and download count.
Article example:
// use it in model
class Article extends AppModel
{
var $actsAs = array('Hitcount');
}
// call it in controller
class ArticlesController extends AppController
{
var $name = 'Articles';
function view($slug = null)
{
// snip..
// separate rss stats, with custom field
if (isset($this->passedArgs['from']) && $this->passedArgs['from'] == 'rss')
$this->Article->hit($article['Article']['id'], array('hitField' => 'hitcount_rss'));
// regular hitcount
$this->Article->hit($article['Article']['id']);
// snip..
}
}
Download example:
// use it in model, with default custom hitField
class Download extends AppModel
{
var $name = 'Download';
var $actsAs =
array
(
'Hitcount' => array('hitField' => 'downloaded')
);
}
// call it in controller
class DownloadsController extends AppController
{
var $name = 'Downloads';
function get($slug = null)
{
// snip..
$this->Downloads->hit($download['Download']['id']);
// snip..
}
}
I hope you find it useful in some way! If you find any bugs or have an advice, I'd be glad to hear it!
Happy baking!


Article comments — View · Add
Thanks for commenting, appreciate it!