As discussed in our post CGridColumn, the CGridColumn is a widget that every Yii developer should be familiar with and able to customize. I hope you have read and understood it. If not please take a few minutes to read and come back. I am pausing now. Ok, you have read it so let's continue.

We are going to see how to customize the CGridView by creating a CGridColumn that will be used to render a property's state of a model using images. When I talk about a property's state, I am talking about a property/attribute that takes on the value of either true/ false/ or on/off / or 1/0. Let's call this widget StateGridColumn.

A User model can have his/her enabled status as enabled/unabled. This kind of property is usually represented with the database type tinyint(1). It is such a common property that most models would have. The CFormatter can be used to format this value to give 'Yes' or 'No' string. We don't want to go through the hassle of i18n this value so we will use an image which will represent the state of the attribute and which can be understood by everybody.

The StateGridColumn we will create should be able to

  1. Use image to represent it on or off state.
  2. The model’s property state should be determined form the property value or a PHP expression that will return its state.
  3. The GridColumn should be sortable. We should be able to group the data cell value according to its state.

Based on what we have discussed, we will use the following properties for our StateGridColumn.

/**
     * @var array the HTML options for the image tag.
     */
    public $imageOptions = array();
    /**
     * @var string the url of image to use for 'on' state
     */
    public $onImageUrl;
     /**
     * @var string the url of image to use for 'off' state
     */
    public $offImageUrl;
 
    /**
     * @var string the attribute name of the data model. The corresponding 
        attribute value will be rendered
     * in each data cell. If {@link value} is specified, this property will 
    be ignored
     * unless the column needs to be sortable or filtered.
     * @see value
     * @see sortable
     */
    public $name;
    /**
     * @var string a PHP expression that will be evaluated for every data 
    cell and whose result will be rendered
     * as the content of the data cells. In this expression, the variable
     * $row the row number (zero-based); 
        $data the data model for the row;
     * and $this the column object.
     */
    public $value;
    /**
     * @var boolean whether the column is sortable. If so, the header 
    cell will contain a link that may trigger the sorting.
     * Defaults to true. Note that if {@link name} is not set, or if 
    {@link name} is not allowed by {@link CSort},
     * this property will be treated as false.
     * @see name
     */
    public $sortable = true;
    /**
     * @var mixed the HTML code representing a filter input (eg a text field, 
    a dropdown list)
     * that is used for this data column. This property is effective only when
     * {@link CGridView::enableFiltering} is set true.
     * If this property is not set, a text field will be generated as the filter input;
     * If this property is an array, a dropdown list will be generated that uses 
        this property value as
     * the list options.
     * If you don't want a filter for this data column, set this value to false.
     * @since 1.1.1
     */
    public $filter;

If you recall from our discussion on CGridColumn, in order to present the column in a manner that we would like it to display our data, we need to override some methods of the CGridColumn class.

The first method we will override is the renderDataCellContent(integer $row, mixed $data). The following is our implementation of that method.

/**
     * Renders the data cell content.
     * @param integer the row number (zero-based)
     * @param mixed the data associated with the row
     */
    protected function renderDataCellContent($row, $data) {
        if ($this->value !== null) {
            $value = $this->evaluateExpression($this->value, array('data' => $data, 
                'row' => $row));
        }
        else {
            $value = CHtml::resolveValue($data, $this->name);
        } 
        if ($value) {
            if ($this->onImageUrl) {
                $this->imageOptions['src'] = $this->onImageUrl;
            }
            else {
                $basedir = dirname(__FILE__) . '/widgetfiles';
                $baseUrl = Yii::app()->assetManager->publish($basedir);
                $this->imageOptions['src'] = $baseUrl . '/images/checked.png';
            }           
        }
    else{
        if ($this->offImageUrl) {
                $this->imageOptions['src'] = $this->offImageUrl;
            }
            else {
                $basedir = dirname(__FILE__) . '/widgetfiles';
                $baseUrl = Yii::app()->assetManager->publish($basedir);
                $this->imageOptions['src'] = $baseUrl . '/images/cancel.png';
            } 
        }
    echo CHtml::tag('img', $this->imageOptions);
    }

We first check if the value is set. If it is, we evaluate the expression passing the data and row value. If not set, we get the value of the property's name. We then check if the value returned is true. A true value means we have to render the image that represents the state 'on'. If the image is not supplied, we provide a default. We then echo the image tag passing on the imageOptions property.

I will just provide the implementation of the other methods. It's not that difficult to understand.

/**
     * Renders the filter cell content.
     * This method will render the {@link filter} as is if it is a string.
     * If {@link filter} is an array, it is assumed to be a list of options, and a 
    dropdown selector will be rendered.
     * Otherwise if {@link filter} is not false, a text field is rendered.
     * @since 1.1.1
     */
    protected function renderFilterCellContent() {
        if (is_string($this->filter)){
            echo $this->filter;
        }
        else if ($this->filter !== false && $this->grid->filter !== null 
            && $this->name !== null && strpos($this->name, '.') === false) {
            if (is_array($this->filter))
                echo CHtml::activeDropDownList($this->grid->filter, $this->name, 
                $this->filter, array('id' => false, 'prompt' => ''));
        }
        else
            parent::renderFilterCellContent();
    }
 
    /**
     * Renders the header cell content.
     * This method will render a link that can trigger the sorting if 
        the column is sortable.
     */
 protected function renderHeaderCellContent() {
     if ($this->grid->enableSorting && $this->sortable && $this->name !== null)
         echo $this->grid->dataProvider->getSort()->link($this->name, $this->header);
     else if ($this->name !== null && $this->header === null) {
        if ($this->grid->dataProvider instanceof CActiveDataProvider){
         echo CHtml::encode($this->grid->dataProvider->model->getAttributeLabel($this->name));
        }
        else{
             echo CHtml::encode(ucfirst($this->name));
        }
     }
    else{
         parent::renderHeaderCellContent();
    }
 }

Let's see how we will use it.

$this->widget('zii.widgets.grid.CGridView', array(
    'dataProvider'=>$dataProvider,
    'columns'=>array(
        'username',          
        array(            
            'name'=>'create_time',
            'value'=>'date("M j, Y", $data->create_time)',
        ),
    array(
            'class'=>'StateGridColumn',
            'name'=>'enabled',
            'headerHtmlOptions'=>array(
                'width'=>'10%'
            ),
            'htmlOptions'=>array(
                'style'=>'text-align:center',
            ),
            'filter'=> array(1 => 'True',0 => 'False'),
        ),
        array(
            'class'=>'CButtonColumn',
        ),
    ),
));

If you have any difficult, just comment on it and I will explain if need be. So let's recap. I have shown how to customise a column in a CGridview. It is obvious your understanding of how CGridview and CGridColumn work is vital for making your own customized column. All that it requires is to override the methods in CGridColumn and we are done.

Resources

Attachment Size
StateGridColumn 3.53 kb