PHP的Yii框架的基本使用示例
在Yii自动生成的代码里,我们总能在admin的界面看到CGridView的身影。这是一个很好用的展示数据的表格控件,用的好可以明显地加快开发进度。下面就让我们来探索一下CGridView的基本使用吧:
简单起见,我们的代码就用Yiidemo中的blog例子来做修改。首先,这是修改后的部分Mysql语句:
droptableifexists`tbl_user`; CREATETABLEtbl_user ( `user_id`INTEGERNOTNULLAUTO_INCREMENTcomment'主键', `username`VARCHAR(128)NOTNULLcomment'用户名', `nickname`VARCHAR(128)NOTNULLcomment'昵称', `password`VARCHAR(128)NOTNULLcomment'密码', `email`VARCHAR(128)NOTNULLcomment'邮箱', `is_delete`tinyintnotnulldefault0comment'删除标志', uniquekey(`username`), primarykey(`user_id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8comment='用户表'; droptableifexists`tbl_post`; CREATETABLEtbl_post ( `post_id`INTEGERNOTNULLAUTO_INCREMENTcomment'主键', `title`VARCHAR(128)NOTNULLcomment'标题', `content`TEXTNOTNULLcomment'文章内容', `tags`TEXTcomment'标签', `status`INTEGERNOTNULLcomment'状态,0=草稿,1=审核通过,-1=审核不通过,2=发布', `create_time`INTEGERcomment'创建时间', `update_time`INTEGERcomment'更新时间', `author_id`INTEGERNOTNULLcomment'作者', `is_delete`tinyintnotnulldefault0comment'删除标志', CONSTRAINT`post_ibfk_1`FOREIGNKEY(author_id) REFERENCEStbl_user(`user_id`)ONDELETECASCADEONUPDATERESTRICT, primarykey(`post_id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8comment='日志表';
两个表一个存储作者信息一个存储日志,其中日志有一个外键关联到user。两个表里面的is_delete字段是标志该条记录是否被删除,0为未删除,1为已删除。让我们看一下用gii生成的Post类的relation方法:
/** *@returnarrayrelationalrules. */ publicfunctionrelations() { //NOTE:youmayneedtoadjusttherelationnameandtherelated //classnamefortherelationsautomaticallygeneratedbelow. returnarray( 'comments'=>array(self::HAS_MANY,'Comment','post_id'), 'author'=>array(self::BELONGS_TO,'User','author_id'), ); }
其中的author外键作为BELONGS_TO关系存在,符合我们的预期。
说了这么多,看看自动生成的Post中admin.php里CGridView的代码吧:
<?php$this->widget('zii.widgets.grid.CGridView',array( 'id'=>'post-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( 'post_id', 'title', 'content', 'tags', 'status', 'create_time', 'update_time', 'author_id', 'is_delete', array( 'class'=>'CButtonColumn', ), ), ));?>
看!虽然我们什么都没写,但这就是这个控件的最基础使用了。dataProvider是由model里面的search函数提供的数据,filter...暂时看不出这里的作用,columns控制展示的每一列,其中最后一项的CButtonColumn向我们展示了三个按钮,分别是查看 更新和删除。
接下来我们一点点地改造.
用CGridView展示我们真正要的数据形式:
很多时候,数据库里的东西不适合直接展示给用户看,需要我们进行一定的处理之后才适合阅读。但在这里不经修改的话CGridView只会把数据库的值原封不动地呈现,所以,我们应该在相应的字段进行修改。比如is_delete字段,数据库里存放的是0和1,但是在这里阅读就不太好了,我们应该改成1展示'是',0展示'否'。看看下面的代码,我们用了一个array,两个键分别是name和value,name对应的要填写该model拥有的字段,而value是你想展示的数据,这里可以写成一个php语句,作为可以执行的代码。看到这里,是不是觉得对这个value我们可以做很多东西?有的同学可能要问,如果我想执行的代码很长,难道都写在value里面?。。。我说同学,你不会在其他地方写成一个函数然后在这里调用它吗??
<?php$this->widget('zii.widgets.grid.CGridView',array( 'id'=>'post-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( 'post_id', 'title', 'content', 'tags', 'status', 'create_time', 'update_time', 'author_id', 'is_delete', array( 'name'=>'is_delete', 'value'=>'is_delete?"是":"否"'//value是可以执行php语句的哦 ) array( 'class'=>'CButtonColumn', ), ), ));?>
除此之外,还有一些常用的选项,都可以在array里面填写,下面是比较常见的使用方式(其他部分代码省略):
array( 'name'=>'is_delete', 'value'=>'is_delete?"是":"否"'//value是可以执行php语句的哦 'filter'=>array(0=>'否',1=>'是'),//自己定义搜索过滤的方式,这里为是和否的下拉菜单 'htmlOptions'=>array('class'=>'delete'),//可以定义html选项,这里是定义了带一个delete的类 ),
上面我们用name的话那是model里原来就有的字段,如果我们想展示自己定义的新内容呢,用header:
array( 'header'=>'备注', 'value'=>'displayyourdata' ),
添加CCheckBoxColumn:
有时也许我们会需要一个复选框,来对每一行进行选择,这时,我们可以增加一列,用CCheckBoxColumn类:
<?php$this->widget('zii.widgets.grid.CGridView',array( 'id'=>'post-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( array( 'selectableRows'=>2,//允许多选,改为0时代表不允许修改,1的话为单选 'class'=>'CCheckBoxColumn',//复选框 'headerHtmlOptions'=>array('width'=>'18px'),//头部的html选项 'checkBoxHtmlOptions'=>array('name'=>'myname','class'=>'myclass'),//复选框的html选项 ), 'post_id', 'title', 'content', 'tags', 'status', 'create_time', 'update_time', 'author_id', 'is_delete', array( 'name'=>'is_delete', 'value'=>'is_delete?"是":"否"',//value是可以执行php语句的哦 'filter'=>array(0=>'否',1=>'是'),//自己定义搜索过滤的方式,这里为是和否的下拉菜单 'htmlOptions'=>array('class'=>'delete'),//可以定义html选项,这里是定义了带一个delete的类 ), array( 'class'=>'CButtonColumn', ), ), ));
修改ButtonColumn:
注意到列表每一项的最后三个小图标吗?不需要的话当然是直接删了,那要是只要其中某几个呢?可以加一个template参数:
array( 'class'=>'ButtonColumn', 'template'=>"{view}{update}", ),
也可以自定义按钮:
array( 'class'=>'ButtonColumn', 'template'=>"{view}{update}{print}", 'buttons'=>array( 'print'=>array( 'label'=>'打印', 'url'=>'Yii::app()->controller->createUrl("print",array("id"=>$data->post_id))', 'options'=>array("target"=>"_blank"), ), ), ),
刷新时触发Javascript:
如果你想在每次搜索之后触发一些Javascript,Yii也提供了这个选项,你只要写成一个函数然后设置afterAjaxUpdate就好,记住这只是在ajax请求完成之后调用的,如果你想在页面一开始加载完成就调用的话需要另外加到页面的 Javascript
$js=<<<_JS_ function(){ alert('Theajaxfinish'); } _JS_; $this->widget('zii.widgets.grid.CGridView',array( 'id'=>'post-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'afterAjaxUpdate'=>$js,//看这里,ajax之后调用的javascript在这里.... 'columns'=>array( array( 'selectableRows'=>2,//允许多选,改为0时代表不允许修改,1的话为单选 'class'=>'CCheckBoxColumn',//复选框 'headerHtmlOptions'=>array('width'=>'18px'), 'checkBoxHtmlOptions'=>array('name'=>'myname','class'=>'myclass'), ), ....
添加关联表相关字段的搜索:
先说一句,我们在这里只谈”一对多“的关联搜索,首先,不要忘了我们的数据库,忘记的同学请戳这里:这里,可以看到在tbl_post中是有一个外键关联到tbl_user表的,用以查找作者的相关信息。建了数据库之后,看看我们生成的Yii代码的POST的Model,里面的realtion如下(忽略comment的):
/** *@returnarrayrelationalrules. */ publicfunctionrelations() { //NOTE:youmayneedtoadjusttherelationnameandtherelated //classnamefortherelationsautomaticallygeneratedbelow. returnarray( 'comments'=>array(self::HAS_MANY,'Comment','post_id'), 'author'=>array(self::BELONGS_TO,'User','author_id'), ); }
可以看到POST和USER表可以通过author键进行访问,例如:$model->author->nickname,而且这里是BELONGS_TO关系。
说了这么多,我们的需求究竟是什么?....
产品经理推了推眼镜:”我们要在日志的后台管理界面加一个功能,可以通过作者名称搜索到相应的文章。这个比较急,今晚就要完成。“
淡定淡定,不就是改需求吗。忽略进度要求,我们研究一下究竟要做什么。
其实很简单的,不就是在POST的admin界面中增加一列作者名称,然后可以通过作者名的模糊搜索去找到对应日志吗?看看代码,要是通过作者id去搜索不就简单了吗?不过这样确实不太友好...如果是展示作者名字而已不也是很简单吗?加一个header然后value是$data->author->username,问题是这样只能展示,不能进行搜索...哎,好苦恼。
淡定淡定,不就是多个搜索吗?来,让我告诉你怎么做。
首先,我们进入POST的model,在一开始的地方添加一个属性:
classPostextendsCActiveRecord { public$name;//添加一个public属性,代表作者名 然后改一下Model里面search的代码,改动部分都已经加了注释: publicfunctionsearch() { //@todoPleasemodifythefollowingcodetoremoveattributesthatshouldnotbesearched. $criteria=newCDbCriteria; $criteria->with=array('author');//添加了和author的渴求式加载 $criteria->compare('post_id',$this->post_id); $criteria->compare('title',$this->title,true); $criteria->compare('content',$this->content,true); $criteria->compare('tags',$this->tags,true); $criteria->compare('status',$this->status); $criteria->compare('create_time',$this->create_time); $criteria->compare('update_time',$this->update_time); $criteria->compare('author_id',$this->author_id); //这里添加了一个compare,username是User表的字段,$this->name是我们添加的属性,true为模糊搜索 $criteria->compare('username',$this->name,true); returnnewCActiveDataProvider($this,array( 'criteria'=>$criteria, )); }
然后在view里面,就是post文件夹的admin.php,CGridView改为下面代码:
<?php$this->widget('zii.widgets.grid.CGridView',array( 'id'=>'post-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( 'post_id', 'title', 'content', 'tags', 'status', 'create_time', 'update_time', 'author_id', /*下面就是添加的代码啊*/ array( 'name'=>'作者名称', 'value'=>'$data->author->username',//定义展示的value值 'filter'=>CHtml::activeTextField($model,'name'),//添加搜索filter ), array( 'class'=>'CButtonColumn', ), ), ));?>
你是不是发现现在有了搜索框但是不起作用呢?哈哈,所以我们说文章要坚持看到最后。我们要做的最后一步,就是在rule里面,把name属性加入到安全搜索字段中,要不然会被Yii认为是不安全字段而过滤掉的。看,就在下面函数的最后一行,safe前面多了个name....
publicfunctionrules() { //NOTE:youshouldonlydefinerulesforthoseattributesthat //willreceiveuserinputs. returnarray( array('title,content,status,author_id','required'), array('status,create_time,update_time,author_id','numerical','integerOnly'=>true), array('title','length','max'=>128), array('tags','safe'), //Thefollowingruleisusedbysearch(). //@todoPleaseremovethoseattributesthatshouldnotbesearched. array('post_id,title,content,tags,status,create_time,update_time,author_id,name','safe','on'=>'search'), ); }