Dynamic Sidebar using CClipWidget

You are viewing revision #2 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.

« previous (#1)

  1. The Problem
  2. The Solution

The Problem

Most applications will have one or two sidebars and often you want to control the content that should appear in the sidebar based on the action. For example you will want to show something different on the homepage as opposed to some view page. While achieving this you generally also want to avoid putting the layout into each view, as that would mean if you wanted to change the layout (e.g. put the sidebar on the left instead of the right or do some funky layout which requires an additional div tag to be added) you'd have to go through every view, which does not achieve good code re-use. It is also bad practice to have lots of if statements in column2 layout just so that you can generate the sidebar correctly.

The Solution

The best option is to generate the sidebar content as part of your view, but instead of rendering it, capture what is generated for the sidebar, so that it can be rendered in the column2 layout.

There are a couple of ways to achieve this, but the first is preferred.

CClipWidget

To achieve the desired outcome with a dynamic sidebar all we need to do is use the CClipWidget and in the view capture the sidebar output into a clip and render it in the column2 layout.

To achieve this, firstly change your column2.php file to have something along these lines...

<div class="container">
  <div class="span-19">
    <div id="content">
      <?php echo $content; ?>
    </div><!-- content -->
  </div>
  <div class="span-5 last">
    <div id="sidebar">
      <?php echo $this->clips['sidebar']; ?>
    </div><!-- sidebar -->
  </div>
</div>

Notice what is being rendered in the sidebar content.

Then, in each your views just use the following:

<?php $this->beginWidget('system.web.widgets.CClipWidget', array('id'=>'sidebar')); ?>
  <div>Some non-common output</div>
  <?php $this->widget('application.components.widgets.SomeReusableWidget'); ?>
  <div>Some more non-common output</div>
<?php $this->endWidget();?>

This will solve the problem in the best possible way.

Your Own Widget

You could create your own widget to capture content specifically for the sidebar and then render it out in the column2 layout, but why go to all this trouble when you can just use CClipWidget.

Output buffering

You could use raw output buffering and add a sidebar variable into the controller, but once again there's not really a need for it as that's what CClipWidget is already doing for you under the covers. If you do want to do it this way (for some weird and unknown reasons) then this is how you do it.

Add the following into your Controller class (from which all other controllers inherit).

public $sidebar;

Then in column2.php layout file use the following:

<div class="container">
  <div class="span-19">
    <div id="content">
      <?php echo $content; ?>
    </div><!-- content -->
  </div>
  <div class="span-5 last">
    <div id="sidebar">
      <?php echo $this->sidebar; ?>
    </div><!-- sidebar -->
  </div>
</div>

Then in each view just do the following:

<?php ob_start(); ?>
  <div>Some non-common output</div>
  <?php $this->widget('application.components.widgets.SomeReusableWidget'); ?>
  <div>Some more non-common output</div>
<?php $this->sidebar = ob_get_clean(); ?>

Whichever of these ways you choose to do it, it's better than lots of if statements in your column2.php layout file or repeating the layout in each view file!