I think in this instance I would use a combination of action caching and view caching. I won't go into the vagaries of different caching engines, just the basics to solve your problem.
The first thing you need to do is turn caching on. In app/config/core.php, you'll need to make a couple changes. First, comment out the Cache.disable line:
//Configure::write('Cache.disable', true);
Second, uncomment the Cache.check line:
Configure::write('Cache.check', true);
Finally, you'll need to make CacheHelper available to all views, so add it to the $helpers member definition in app/app_controller.php:
class AppController extends Controller
{
var $helpers = array('Cache');
}
Next, we'll set up the view element that contains your category menu (doing the view stuff before the controller stuff may seem a little backwards, but bear with me -- it'll make sense). Putting the menu in a view element makes caching it a bit simpler than trying to selectively designate parts of a larger view or layout as uncached with <cake:no-cache></cake:no-cache> tags.
Here's a sample view element, rendering the category menu in a simple unordered list (<ul>):
<?php
$menuItems = $this->requestAction(array('controller'=>'categories','action'=>'menu'));
echo $html->nestedList( $menuItems, 'ul' );
?>
You may have been told by one or more people in the past that requestAction is evil, as it initiates a whole new dispatch. This is true. You should avoid requestAction like the plague, except when you're doing this type of view-caching, in which case it's the best way to go.
We'll store this new view element as app/views/elements/category_menu.ctp. The requestAction invokes the menu action in CategoriesController. Here's what CategoriesController looks like, then I'll go through it bit by bit:
class CategoriesController extends AppController
{
var $cacheAction = array(
'menu/' => '1 day'
);
function menu()
{
return $this->Category->find('list', array(
'fields' => array('Category.id','Category.label'),
'order' => array('Category.order')
));
}
}
What this does:
- The member definition for
$cacheAction tells Cake that it should cache the results of the menu action for 1 day.
- The
menu method returns a one-dimensional array of categories, using Category.id as the keys, and Category.label as the values.
So, let's go over what we've got so far. We have a category_menu element, which requests the CategoriesController::menu action. When this request is received, Cake will check to see whether the result of that action has been cached, and hasn't expired. If the cached result is still valid, Cake will return it without invoking the menu method we defined. Otherwise, Cake will invoke the method, and will cache the result. The view element then renders the result as a nestedList.
All that remains, then, is to use the view element in other views. Here's a simple sample app/views/layouts/default.ctp:
<html>
[ snip... ]
<body>
<div class="category-menu">
<?php echo $this->element('category_menu', array('cache'=>'1 day')); ?>
</div>
<div class="content">
<p>Lorem ipsum sit dolor amet.</p>
</div>
</body
</html>
We use the familiar View::element to render the category_menu view element we created earlier, but we also tell Cake that we want it to render the view element once, then cache this rendered version, and hold onto it for a month. Any subsequent requests for that element, until a month elapses or we clear the cache, will deliver the pre-rendered view element. It won't even interpret app/views/elements/category_menu.ctp, much less invoke requestAction, or hit the database.
To ensure changes you make to Category model records are reflected immesiately, you can invoke the global function clearCache() in the create/update actions. One caveat about clearCache(): it's a bit of a shotgun approach: inelegant but effective. clearCache() wipes out the entire cache, rather than selectively clearing only the necessary views. It is a simple approach, however, and given you're talking about updating the categories monthly, having to regenerate the cache once a month is probably worth the diminished hassle of a more selective approach.
Very long answer! HTH