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'),
);
}