CGridView: Adding behaviors from configuration, and template items from behaviors to CGridView

Changing the way a CGridView is rendered from the configuration file, or through behaviors specified at the moment it is used, is handy to extend a CGridView without creating tons of different classes for it. I am surprised that this is not in the default implementation, so I made my default CGridView implementation.

The implementation below allows you to add 'behaviors' to the gridview as a parameter. The behavior can implement a 'renderMyDateRangeSearch' method for instance which you can reference in the 'template' parameter with '{myDateRangeSearch}'. If your 'dateRangeSearch' would depend on the site or theme, you could also define it in the Yii configuration where you would set the default behavior parameter for the 'MyGridView' widget.

This implementation also avois rendering the CGridView when an ajax update occurs which does not involve this CGridView. When your application is finished and all 'ids' are fixed (not automatically indexed by Yii), you can also set 'callParentRender' to false.

I know that this calls for more explication, I'll come back to it when I have some more time, or you can update this article (it's a Wiki!).

<?php
Yii::import('zii.widgets.grid.CGridView');
/**
 * Modifies CGridView:
 * <ul><li>$behaviors properties, enabling the attachment of behaviors in the configuration (a CGridView is a widget);</li>
 * <li>To render only when:
 *   <ul><li>there is no ajax request, or,</li>
 *   <li>there is an ajax request containing the id of the gridview.</li></ul>
 * </li><ul>.
 *
 */
class MyCGridView extends CGridView {
    public $isRender=false;

    /**
     * @var array Behaviors to be enabled for the CGridView.
     */
    public $behaviors = array();

    /**
     * @var boolean When true (default), calls the parent rendering method to keep the default 'id' numbering.
     *              Can be set to 'false' when all ids are fixed (or of no importance).
     */
    public $callParentRender = true;

    public function __construct($owner=null) {
        parent::__construct($owner);
    }

    /**
     * Extends CGridview with:
     * <ul><li>Check if this CGridView is to be rendered</li>
     * <li>Attc
     * (non-PHPdoc)
     * @see CGridView::init()
     */
    public function init() {
        /* @var $request CHttpRequest */
        $request = Yii::app()->request;
        $ajax    = $request->getParam($this->ajaxVar,null);
        if($ajax!==null) {
            $this->isRender |= array_search($this->getId(),explode(',',$ajax))!==false;
        } else {
            $this->isRender = true;
        }
        $this->attachBehaviors($this->behaviors);
        return parent::init();
    }

    /**
     * Default 'run' method changed to render only when requests.
     *
     * The parent render method is called in this implementation to make sure that id sequences are the same.
     *
     *
     * (non-PHPdoc)
     * @see CBaseListView::run()
     */
    public function run() {
        if($this->isRender) {
            return parent::run();
        } else {
            if(!$this->callParentRender) {
                return null;
            } else {
                ob_start();
                ob_implicit_flush(false);
                parent::run();
                ob_get_clean();
                return null;
            }
        }
    }


    /**
     * Replaces default render method to include any rendering method (including methods provided by behaviors).
     *
     * The original method used 'method_exists' which does not work for methods provided by behaviors.
     *
     * (non-PHPdoc)
     * @see CBaseListView::renderSection()
     */
    public function renderSection($matches) {
        parent::renderSection($matches);
        $method='render'.ucfirst($matches[1]);
        try {
            $this->$method();
            $html=ob_get_contents();
            ob_clean();
            return $html;
        } catch (Exception $e) {
            // The exception should never occur because only existing sections should be rendered.
            throw $e;
        }
        return $matches[0];
    }
}