StateGridColumn-A custom widget to display a model’s state in a CGridView
On 7 minutesAs 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
- Use image to represent it on or off state.
- The model’s property state should be determined form the property value or a PHP expression that will return its state.
- 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 |