CGridView组件是Zii库组件,主要用来展示表格式的数据,默认Gii生成的admin视图就用它展示
参考Yii手册可以发现,CGridView类位于Zii.widgets.grid命名空间下,所以它就是个单元格部件,Zii.widgets.grid命名空间下只有CGridView类和配合它工作的一些CXxxColumn类(表示不同的列对象)。
CGridView属于视图部件,所以,基本用法和不含body内容的部件相同,即 $this->widget('类名', array(key-value参数对));
最简短的代码如下:
$dataProvider=new CActiveDataProvider('Post'); $this->widget('zii.widgets.grid.CGridView', array( 'dataProvider'=>$dataProvider, ));
即参数只有1个:数据提供者,常见数据提供者有3种:CActiveDataProvider(对应AR模型)、CArrayDataProvider(对应二维数组)、CSqlDataProvider(对应SQL查询返回的数据),cookbook的第7章提供了例子说明CGridView如何使用这3种数据提供者。Yii手册说明推荐使用CActiveDataProvider,即AR模型数据作为数据提供者。
CActiveDataProvider、CArrayDataProvider、CSqlDataProvider及它们的基类CDataProvider、接口IDataProvider都在system.web下,这三个数据提供者中数组类型最直观,常见的属性是通用的排序sort和分页pagination。SQL类型的提供者默认使用了Yii::app()->db这个连接,所以构造数据提供者时可以不指定db连接而直接给出SQL语句,但必须同时给出SQL语句返回的记录个数(不然无法实现分页),所以,往往包含了2个SQL语句。SQL类型的数据提供者,可以用$dataProvider->getData()获取数组形式的数据。
CActiveDataProvider是最复杂的提供者,它基于AR,并使用了AR的CActiveRecord::findAll()方法来获取数据,也正因为如此,构造这类数据提供者时,需要指明对应的Model,提供查询标准criteria参数。和SQL类型提供者一样,也可以用$dataProvider->getData()获取数组形式的数据。
CActiveDataProvider构造的例子(获取不在某个组的用户作为提供者):
public function searchNotInGroup(Group $group) { $connection = Yii::app()->db; $sql = 'select user_id from {{role}} except select user_id from {{role}} where '. 'group_id='.$group->id; $command = $connection->createCommand($sql); $rows = $command->queryAll(); $user_ids = array(); foreach($rows as $row) $user_ids[] = $row['user_id']; return new CActiveDataProvider('User', array( 'criteria' => array( 'condition' => 'id in ('.implode(',', $user_ids).')', 'order' => 'username', ), 'pagination' => array( 'pageSize' => 3, ), )); }
CGridView 的一个重要属性是filter:一个模型实例参数,它保存了用户输入的过滤器数据(即单元格组件标题行下的那些输入框)
CGridView的另一个重要属性是columns:一个array,用来配置单元格,即应该显示哪些列,怎么显示。这个array类型中的每个元素对应一个列,每个元素的类型可以是string或array。当元素是string类型时,其格式必须是"name:type:header",因为最终会产生CDataColumn类型的实例,这三部分和CDataColumn的3个属性对应。当元素是array类型时,最终创建某种grid column实例(基类型CGridColumn),用'class'元素指明子类型(当前zii库提供CDataColumn[缺省时为此类型]、CLinkColumn、CButtonColumn、CCheckBoxColumn)。admin视图中前面几列都是CDataColumn类型,最后的那些按钮列是CButtonColumn类型。下面给出CLinkColumn类型的例子:
//GroupController public function actionView($id) { $usermodel = new User('searchNotInGroup'); $usermodel->unsetAttributes(); // clear any default values if (isset($_GET['User'])) $usermodel->attributes = $_GET['User']; $this->render('view', array('model' => $this->loadModel($id), 'usermodel' => $usermodel)); }
//group/view/$id $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'user-grid', 'dataProvider'=>$usermodel->searchNotInGroup($model), 'filter'=>$usermodel, 'columns'=>array( 'id', 'username', array( 'name' => 'status', 'value' => 'Lookup::item("userStatus", $data->status)', 'filter' => Lookup::items('userStatus'), ), array( 'class'=>'CLinkColumn', 'labelExpression' => '"添加用户".$data->username', 'urlExpression' => 'array("group/addUser", "id" =>'. $model->id .', "userid" => $data->id)' ), ), ));
说明:上述加载的CGridView中,状态列是CDataColumn类型:用'name'对应数据模型的属性名,'filter'对应过滤器输入(文本或dropdownlist的数据,这里是后者)。'value'对应的是字符串,但这些字符串代表一个PHP表达式,这个表达式会针对每行数据进行计算,计算的结果就是最后在单元格显示的内容。PHP表达式中,变量$row代表行号(从0开始),$this代表列对象,$data代表该行的数据模型,所以,上面的例子中,$data->status代表该行对应的User数据模型实例的状态。
上述例子中,最后一列是CLinkColumn类型,所以,用'class'指明类,'label'是显示的文字,'url'是对应的链接,如果显示的文字和对应的链接是含参数的,可以分别换成'labelExpression'和'urlExpression‘来指定,其中可以使用$row、$this、$data等变量(和上面的'value'内的情况一样)。阅读CLinkColumn类的源码,只有唯一的protected方法renderDataCellContent($row, $data)
protected function renderDataCellContent($row,$data) { if($this->urlExpression!==null) $url=$this->evaluateExpression($this->urlExpression,array('data'=>$data,'row'=>$row)); else $url=$this->url; if($this->labelExpression!==null) $label=$this->evaluateExpression($this->labelExpression,array('data'=>$data,'row'=>$row)); else $label=$this->label; $options=$this->linkHtmlOptions; if(is_string($this->imageUrl)) echo CHtml::link(CHtml::image($this->imageUrl,$label),$url,$options); else echo CHtml::link($label,$url,$options); }
可以知道,使用了urlExpression和labelExpression,是通过evaluateExpression计算得到url和label的。evaluateExpression是CLinkColumn的基类方法,它的第一个参数是一个PHP表达式或PHP回调,第二个参数是传递给表达式或回调的附加参数。
public function evaluateExpression($_expression_,$_data_=array()) { if(is_string($_expression_)) { extract($_data_); return eval('return '.$_expression_.';'); } else { $_data_[]=$this; return call_user_func_array($_expression_, $_data_); } }
在我们的例子中,urlExpression是一个PHP表达式(字符串),即'urlExpression' => 'array("group/addUser", "id" =>'. $model->id .', "userid" => $data->id)',这一句相当于翻译成了$urlExpression = array("group/addUser", "id" =>'. $model->id .', "userid" => $data->id); 然后Yii会以它的生成URL的方式利用这个数组,生成类似r=group/addUser&id=2&userid=1 的链接。
evaluateExpression第二个参数是数组形式的,执行时会用PHP函数extract解析该数组并插入变量,即键作为变量名,值作为变量值,$url = $this->evaluateExpression($this->urlExpression, array('data' => $data, 'row'=>$row)); 翻译成
$data = $data值;
$row = $row值;
$url = $this->urlExpression中代入$data,$row值得到的结果;
根据手册说明,evaluateExpression第一个参数也可以是PHP回调:即一个类的方法,格式为array(类名或实例, 方法名),也可以是匿名函数,并且方法签名或匿名函数的格式如下function foo($param1, $param2, ..., $component) { ... },从上面evaluateExpression的源码中也可以知道,最后一个参数是组件自身,而之前的参数就是我们以数组形式传递的变量和对应值。
CGridView、数据提供者、使用表达式或回调作为参数这些东西所采用的思维,在整个Yii框架中随处可见,所以值得深究