Elemental: Conditional Content with CakePHP

I finally got around to implementing conditional content here on Snook.ca. I've wanted to do this since I moved to the new design just over a month and a half ago. The left sidebar has a number of elements like the projects I've worked, quick links, etc. As some people have noticed, if the article was particularly short, the comments would often overlap the rather lengthy content. I didn't want that.

With a little bit of late night development and some quick help from Larry (aka PhpNut, the lead developer of CakePHP), I've developed what I have dubbed "Elemental".

Elemental

Elemental consists of 2 parts: some new controller methods added to app_controller.php, and a new helper called Elemental.

In app_controller.php add:

    function enableComponent($component, $params=null)
{
$name = Inflector::camelize($component);
$found = loadComponent($name);
if($found)
{
$cn = $name . 'Component';
$comp = new $cn($params);
$comp->startup($this);
$this->viewVars['elements'][$component] = true;
}
}

function enableElement($element)
{
$this->viewVars['elements'][$element] = true;
}

function disableElement($element)
{
$this->viewVars['elements'][$element] = false;
}

The methods enableElement and disableElement will turn on or off whether a particular element should be displayed in the view. The method enableComponent takes the extra step and conditionally loads a component and enables the related element that goes along with that.

To use the element in your view just do:

echo $elemental->load('projects');

In this particular example, it calls the projects.ctp element. This should work exactly like $this->renderElement but will only load if the element has been enabled using enableElement or enableComponent..

Here's the Elemental helper:

<?php
class ElementalHelper extends Helper {
function load($element, $params=array())
{
$view =& ClassRegistry::getObject('view');
if(isset($view->viewVars['elements']) &&
isset($view->viewVars['elements'][$element]) &&
$view->viewVars['elements'][$element])
{
return $view->renderElement($element, $params);
}
}
}
?>

Using it on Snook.ca

What I've done on Snook.ca then, is to break up the sidebar into a few different elements. All elements are enabled by default via the app_controller. Then, in my posts controller, I check the length of the body element and disable elements as the article gets shorter. Here are some examples:

I can now keep the sidebars light and avoid overlap or throw in some bonus material if the article is longer.

Published June 23, 2007 · Updated June 23, 2007
Categorized as CakePHP
Short URL: http://snook.ca/s/827

Conversation

13 Comments · RSS feed
Mandy said on June 24, 2007

Hey thanks for sharing this. However, I still am little confused wrt renderElement vs your enableElement. renderElement can also do choicing out of content based on our conditions...do you mean to say, it will still load those elements but then just not display them and your enableElement will not even load them? Like how is it diff from -


if ($x = 1) {
  echo $this->renderElement('projects1');
} else {
  echo $this->renderElement('projects2');
}

Tarique Sani said on June 24, 2007

This is so cool - I can think of combining it with some other stuff out there and we have got truely dynamic or contextual sidebars

Thanks for the efforts

Jonathan Snook said on June 24, 2007

@Mandy: Sure, renderElement vs enableElement may seem trivial but I dislike having to put conditionals around everything in my view. Being able to easily turn it on or off via the controller is nice, leaving my views really clean. Think about using this for displaying admin menus or what have you. In addition, using enableComponent is the sweetest part as components get loaded only when needed and allows you to link a component with an element and have both "turned on" via one command. That's why I like it. :)

Nate Klaiber said on June 24, 2007

Looks good Snook. I agree with you, I would rather have the logic for this inside of my controllers versus the view. I despise using conditional statements in my views (regardless of the framework).

I have found myself using custom helpers for some things. For things like alternating and such - I create a custom method in my helpers to display thing accordingly (some of this might be included in an element). This is usually for things that are nested in a loop and cannot be achieved in the controller (without running through result sets and assigning new arrays).

I like how you have it check the length of the content and only include as necessary - keeps your frontend layout nice and clean as well! Nice work...

Nasko said on June 25, 2007

This is really neat, Jonathan. I have an ongoing project where I've managed to do without using conditionals in my views by setting variables to empty strings and still echoing them in my views. No doubts you've shared a neater approach and I'm going to revise my code :-)

Thanks!

Andy Dawson (AD7six) said on June 26, 2007

Hi Jonathan,

I like the freedom/cleanliness of what you propose here, I'm curious as to what your components look like, and what convention, if any, you follow for the data that you want to pass from your components to your elements. There is the risk for element and main content vars to overlap/conflict if there is no active separation.

I wrote about something similar in the past, in which I always rendered a side_bar element which would loop on the defined/enabled dynamic elements - didn't seem perfect at the time since the side_bar element contained a bit too much logic. Making use of a helper seems a good step forwards.... I think I feel a refactoring coming on :)

Cheers,

AD

Jonathan Snook said on June 26, 2007

@Andy: currently, I don't have any convention. I suppose, like this Elemental script, I should contain it within a single namespace of sorts. Now that I have this script, I'm more likely to use it because of the way it easily ties components and elements together. Otherwise, conflict is always possible and hopefully your naming convention is clear and unique enough not to be a problem.

Steve Lounsbury said on July 06, 2007

I can see using this in a CMS, allowing the user to define what elements show in a sidebar (similar to the sidebar widgets in wordpress 2.2). It would be a pretty simple modification to make. Thanks again for the inspiration!

Dan Cramer said on July 18, 2007

Great piece of information. Thanks for putting this out there for the cake community.

Kjell Bublitz said on July 19, 2007

Interesting approach. I like it :)

What do think about assigning the params for renderElement from within the enable method? Saving you the $this->set() ..

f.e. $this->enableElement('projects', $projectData);
in the helper.. $view->viewVars['elements'][$element] = array( with the data ) = $params

Jonathan Snook said on July 19, 2007

@Kjell Bublitz: That is a good idea. I like it! I'm also thinking of looking into turning the controller methods into a component of its own so that it's more "drag and drop" for any project. If I get to that, I'll see about submitting it to the Bakery.

C. James Callaway said on July 21, 2007

I have implemented something similar. I've added a table to the database to maintain sidebar blocks. Each record contains, controller, action, helper, helper_method, sort_order. In app_controller.php, in beforeRender, I query the database to get a list of blocks based on $this->params. Derived controllers can override this behavior by setting a flag (renderBlocks). Helper::helper_method gets called from app/views/layout/mysite.thtml

Peter Jacobson said on October 20, 2008

Found this copy of your article elsewhere, with no attribution, thought you might want to know. Thanks for the original one though... (spaces etc inserted into link on purpose)


http://www dot designfloat dot com / Programming / Elemental_Conditional_Content_with_CakePHP/

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.