CGridView: Render customized/complex datacolumns cgridview datacolumn value调用 controller 和 model里的函数
来源:互联网 发布:客户数据怎么划分 编辑:程序博客网 时间:2024/04/30 18:12
cgridview datacolumn value调用 controller 和 model里的函数 或者自定义函数
If you have to display a overview of related data in a table, you can use the CListView, create your view with table/tr/td tags and implement db-queries in the view to display detaildata of the current (master-)record.
But the CGridView offers the possibility to add custom columns too.In the class reference of theCGridView you will find this example.
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider'=>$dataProvider, 'columns'=>array ( ... array( // display 'create_time' using an expression 'name'=>'create_time', 'value'=>'date("M j, Y", $data->create_time)', ), array( // display 'author.username' using an expression 'name'=>'authorName', 'value'=>'$data->author->username', ), array( // display a column with "view", "update" and "delete" buttons 'class'=>'CButtonColumn', ), ),));
Because Yii checks the 'value' of a column definition by using CComponent.evaluateExpressionyou can assign a PHP expression string - that's great.
If you take a look at the source of CComponent.evaluateExpression you will see, that also PHP call_user_func_array() is supported - and that's very great.
You can add a column that displays data generated by a object method, for example a method of your controller.
All you have to do is to assign the object method to the value:
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider'=>$dataProvider, 'columns'=>array( ... array( 'name'=>'newColumn', //call the method 'gridDataColumn' from the controller 'value'=>array($this,'gridDataColumn'), ), array( 'name'=>'Address', //call the method 'renderAddress' from the model 'value'=>array($model,'renderAddress'), ), ),));
class MyController extends Controller { ... //called on rendering the column for each row protected function gridDataColumn($data,$row) { // ... generate the output for the column // Params: // $data ... the current row data // $row ... the row index return $theCellValue; } ... }
class Address extends CActiveRecord { ... //called on rendering the column for each row public function renderAddress($data,$row) { // ... generate the output for a full address // Params: // $data ... the current row data // $row ... the row index return $theCellValue; } ... }
It's easier to handle/manage the cell value assigned by a method than to implement a PHP expression string.If you wantyou can use renderPartial to maintain the datacolumn.
Example
Assume you have related tables 'product' and 'category' with a MANY_MANY relation 'product_category' and want to display a tablewith the product and the related categories. You have to generate the product model and your dataProvider by using 'with', 'join', 'order' ...
I don't explain the working with the database and related models here. Please take a look a theRelational Active Record tutorialif you need more information.
A) Eager loading - load all data at once in a single query
You will (internally) generate a SQL statement like
SELECT p.id,p.name, p.description, c.name as category FROM product p JOIN product_category pc ON pc.product = p.idJOIN category c ON c.id = pc.category
Your standard gridview with the columns (id not displayed) product, description category will look like this:
PRODUCT | DESCRIPTION | CATEGORY----------------------------------------------iPhone 4S | Dual Core A5 Chip ... | Phone----------------------------------------------iPhone 4S | Dual Core A5 Chip ... | Offer----------------------------------------------iPhone 4S | Dual Core A5 Chip ... | Apple----------------------------------------------iPhone 4S | Dual Core A5 Chip ... | Communication----------------------------------------------iPhone 4S | Dual Core A5 Chip ... | Lifestyle----------------------------------------------
Now we want display the production information only in the first row of each product:
PRODUCT | DESCRIPTION | CATEGORY----------------------------------------------iPhone 4S | Dual Core A5 Chip ... | Phone---------------------------------------------- | | Offer---------------------------------------------- | | Apple----------------------------------------------
If you need to hide the repeated value you have to create your own GridView and override theCGridView.renderTableRow method.
Or you use the power of the 'value' property:
Create two protected methods 'gridProductName' and 'gridProductDescription' in your controller and keep the latest rendered row as private variable.
class ProductController extends Controller { $_lastProductId = null; .... //called on rendering a grid row //the first column //the params are $data (=the current rowdata) and $row (the row index) protected function gridProductName($data,$row) { return $this->_lastProductId != $data->id ? $data->name : ''; } //called on rendering a grid row //the second column protected function gridProductDescription($data,$row) { if($this->_lastProductId != $data->id) { $this->_lastProductId = $data->id; //remember the last product id return $data->name; } else return ''; } }
Add the custom columns to the gridview:
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider'=>$dataProvider, 'columns'=>array( array( 'name'=>'product', //call the method 'gridUniqueProductName' of the controller //the params extracted to the method are $data (=the current rowdata) and $row (the row index) 'value'=>array($this,'gridProductName') ), array( 'name'=>'description', 'value'=>array($this,'gridProductDescription') ), 'category', //display the category as a default column ),));
B) Lazy loading - load the categories for each product
You can load you products and select the assigned categories in an extra query for each row.I know you may think this is a bad performance, but it depends how complex your db schema is (how many joins you need) or type/size of the redundant data of the single query. See the remarks at 7. Relational Query Performance of the Relational Active Record tutorial. If you load product images from the db too, lazy loading can be the better choice.
And you can display a grid like below.The categories should bedisplayed as links to a page that lists all products with the specified category.
PRODUCT | CATEGORIES----------------------------------------------iPhone 4S | PhoneDual Core A5 Chip | Offer.... | Apple.... | Communication.... | Lifestyle----------------------------------------------Motorola Milestone | ...
The controller code:
class ProductController extends Controller { ... //return the value for the product column protected function gridProduct($data,$row) { return CHtml::encode($data->name) .'<br/>' . CHtml::encode($data->description); } //called on rendering a single gridview row protected function gridProductCategories($data,$row) { $sql = 'SELECT c.id,c.name FROM product_category pc JOIN category c ON c.id = pc.category '; $sql .= 'WHERE pc.product = ' . data->id; //the product id $rows = Yii::app()->db->createCommand($sql)->queryAll(); $result = ''; if(!empty($rows)) foreach ($rows as $row) { $url = $this->createUrl('bycategory',array('category'=>$row['id'])); $result .= CHtml::link($row['name'],$url) .'<br/>'; } return $result; } ... public function actionByCategory($category) { ... display a page with all products of the specified category ... } ... }
Render the CGridView in your view like this:
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider'=>$dataProvider, 'columns'=>array( array( 'name'=>'product', 'type'=>'raw', //because of using html-code <br/> //call the controller method gridProduct for each row 'value'=>array($this,'gridProduct'), ), array( 'name'=>'category', 'type'=>'raw', //because of using html-code 'value'=>array($this,'gridProductCategories'), //call this controller method for each row ), ),));
C) The fun part - render view files into a cell
In a controller method called on rendering a row of the gridview you can do more ...
You can use 'renderPartial' to manage the view of a datacell in a viewfile.For example, try to reuse the default 'view' generated by gii.
If you want to render the full address of a user in a cell you can do like this.The user has an attribute 'address', the addresses are stored in a table 'Address'
class UserController extends Controller { //the default admin action public function actionAdmin() { $model=new User('search'); $model->unsetAttributes(); if(isset($_GET['User'])) $model->attributes=$_GET['User']; $this->render('admin',array( 'model'=>$model, )); } //assume you have generated the Address model with gii too protected function gridAddress($data,$row) { $model = Address::model()->findByPk($data->address); //$data->address is the FK from the user table //get the view from the address CRUD controller (generated with gii) return $this->renderPartial('../address/view',array('model'=>$model),true); //set $return = true, don't display direct } }
The CGridView in the view: admin.php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider'=>$dataProvider, 'columns'=>array( ... user attributes here ... // add a column to display the full address array( 'name'=>'address', 'type'=>'raw', //because of using html-code from the rendered view 'value'=>array($this,'gridAddress'), //call this controller method for each row ), ),));
Now you have rendered an embedded CDetailView inside the cells of a CGridView.You can try to render a CListView (default actionIndex of a CRUD controller) into a cell too.
Access column properties
In the examples above, I use different methods for each gridcolumn. So I didn't have to take care about the column properties.
If you want to use a single method for each column or need more information about the currently rendered column, add a third param (for example $dataColumn) to your called method:
//view$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider'=>$dataProvider, 'columns'=>array( array( 'name'=>'product', 'value'=>array($this,'renderGridCell') ), array( 'name'=>'description', 'value'=>array($this,'renderGridCell') ), 'category', //display the category as a default column ),)); //controller codeclass ProductController extends Controller { ... protected function renderGridCell($data,$row,$dataColumn) { //var_dump($dataColumn); //$dataColumn is an instance of a [CDataColumn](http://www.yiiframework.com/doc/api/1.1/CDataColumn "CDataColumn") //you have access to the properties and methods here (name, id, type, value, grid ...) //implement the rendering switch($dataColumn->name) { case 'product': .... break; case 'description': .... break; .... } .... }
- CGridView: Render customized/complex datacolumns cgridview datacolumn value调用 controller 和 model里的函数
- yii的CListView和CGridView之分页
- 关于超链接标签 a 里的属性调用js的问题 以及yii button里面调用cgridview 某一行数据
- Yii中CGridView单元格组件和数据提供者的使用
- Yii DataProvider和CGridView的几个小提示
- 挂件CGridView
- CGridView详解
- 简述Yii 的CGridView如何使用及怎么用model的search搜索
- yii_wiki_216_update-delete-model-with-cjuidialog-works-in-cgridview(通过CJuiDialog在CGridView中CRUD)
- 自定义 Yii CGridView 和 CListView 组件
- 自定义 Yii CGridView Column 的显示
- Yii框架中CGridView的使用方法
- Yii CGridView的内联列编辑
- yii CGridView 分页 初始化 js的方法
- YII中使用CActiveDataProvider、CDbCriteria和CGridView实现订单详情的显示
- Yii Framework CGridView详解
- Yii Framework CGridView详解
- Yii CGridView使用
- Spring Security教程(5)---- 国际化配置及UserCache
- C++ 值传递、指针传递、引用传递详解
- ThreadSafe:诊断并发问题的利器
- 第五章 采用SVM和神经网络的车牌识别
- KMP算法实现
- CGridView: Render customized/complex datacolumns cgridview datacolumn value调用 controller 和 model里的函数
- mysql 用户和权限分配
- 用ajax返回验证的时候总是弹出error原因
- Spring Security教程(6)---- 使用数据库管理用户及权限
- Spring Security教程(7)---- 解决UsernameNotFoundException无法被捕获的问题
- 第五章 采用SVM和神经网络的车牌识别(流程图及详细解释)
- mysql root没有权限的问题解决
- Spring Security教程外篇(1)---- AuthenticationException异常详解
- Redis Pub发布/sub订阅实现机制