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;

B1B

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();
    }

B2B

 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