默认Yii的代码生成器Gii生成的CRUD的特点是:
1、创建actionCreate,不带GET参数
$model = new MyModel;
if(isset($_POST['MyModel'])){ //带POST变量值,这是提交后的处理
$model->attributes = $_POST['MyModel']; //批量属性赋值
if(isset($_POST['keyname']) $v = $_POST['keyname']; //可以有和模型属性不对应的POST变量
$model->attr1 = $_POST['keyname'].'一定处理'; //可以在批赋值后对个别属性值进行调整(和POST变量无关的调整不放这里,应该放入beforeSave)
if($model->save()){
//保存成功,通常$this->redirect(array('view', 'id' => $model->id));重定向
}
}
//渲染create视图,一般保存后因为重定向不会到此,所以$model是空的数据收集器
//create视图主要就是渲染分部视图 _form,以便用户输入。表单动作的指向如果未设定CActiveForm的action属性,默认就是当前页面URL,即 MyModel/create
$this->render('create', array('model' => $model));
2、读取单条记录actionView($id),带GET参数 ?id=nnn
用$this->loadModel($id)读取id为$id的模型,渲染view视图,将读取的模型$model作为参数传递
Gii生成的view视图用zii.widgets.CDetailView部件展示$model中的数据,主要是定制每个属性列(和CGridView有类似之处,但又不同),
因为模型只有一个,所以每列中引用模型时直接用变量$model,定制'value'值不用对表达式加引号
3、读取所有记录(即列表)actionIndex,不带GET参数
显然,此时有很多个模型,因此需要“数据提供者”这一重要角色,数据提供者$dateProvider和查询标准$criteria对最终展示哪些数据,数据排序、分页起着重要作用。
渲染index视图时,主要的参数就是数据提供者$dataProvider。Gii生成的index视图用zii.widgets.CListView部件展示数据,每个模型用分部视图_view进行渲染。
因为有很多模型了,所以_view分部视图用$data变量表示当前模型,可以像设计一小块页面一样设计_view分部视图。
4、删除单条记录actionDelete($id),带GET参数 ?id=nnn
delete没有也不需要对应的视图,而且Gii生成的代码delete动作只能从admin管理视图下进行操作,并且是Ajax方式的。delete动作主要包括
找出id为$id的模型,执行其实例方法delete(),如果不是Ajax请求,重定向到前一位置或admin
5、更新单条记录actionUpdate($id),带GET参数 ?id=nnn
更新部分的代码和创建非常类似,只有一点区别,创建时通过new MyModel给$model赋值,更新时用$this->loadModel($id)给$model赋值。值得注意的是,new MyModel得到的模型,其isNewRecord属性值为true,而$this->loadModel方法得到的模型,其isNewRecord属性值为false。而且,渲染update视图时,$model变量是包含了旧模型数据的,它需要展示到分部视图_form(和create共享的分部视图)
6、管理所有记录actionAdmin,不带GET参数(但可以有GET过来的表单数据)
$model=new Labclass('search'); //新建search场景的模型,场景主要决定需要验证哪些字段
$model->unsetAttributes(); // clear any default values //因为此模型用作过滤器,所以清除所有属性的默认值
if(isset($_GET['Labclass']))
$model->attributes=$_GET['Labclass']; //这里用GET变量区分是否已经输入过滤器表单的数据
$this->render('admin',array(
'model'=>$model, //渲染视图,如果$model是空的,就显示表单给用户来输入,如果$model含有数据,那么同时被search()方法用于数据提供者
));
admin视图中包含了一个初始隐藏的表单_search(分部视图),该表单用于高级搜索,包含$model变量,并且是用GET方法传递参数的(对照上述代码)
admin视图的重头戏是CGridView,它的过滤器也是$model变量,而数据提供者是$model->search()实例方法得到的(注意,如果$model是包含数据的,这些数据会被search()方法中的查询标准所利用,从而过滤出数据)。CGridView的列定义在另一个帖子中已经详细描述,就是要注意,和CDetailView相比,它定义列时'value'对应的表达式要用引号(可以用单引号,然后表达式内用双引号或转义的单引号)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
理解了Gii生成的CRUD,我们来表述单页面的CRUD(即CRUD杂合体),用于批量操作
public function actionBatch($cid = '', $oid = '', $tid = '', $id = '') //参数 $cid/$oid/$tid为模型公共数据,充当过滤器角色, $id识别模型用
{
$model = $id ? $this->loadModel($id) : new Labclass; //带GET[‘id’]参数,用于更新操作
$oldmodels = array();
if($cid && $oid && $tid){
$model->class_id = $cid; $model->course_id = $oid; $model->teacher_id = $tid; //预置模型公共部分数据,在视图中用于判断公共部分信息是否确定
$oldmodels = Labclass::model()->findAllByAttributes(array(
'class_id'=>$cid, 'course_id'=>$oid, 'teacher_id'=>$tid //过滤出已经存在的同类模型数据
));
}
if(isset($_POST['Labclass'])){ //有POST数据,可能是公共部分,也可能是新模型(含公共部分)
$model->attributes = $_POST['Labclass'];
if(isset($_POST['week'])){ //带week参数,是新模型,修正部分属性,再保存
$model->date = Term::getDateByWeekDay($_POST['week'], $_POST['day']);
$sectionNames = Term::getSectionMap(true);
$timeSegs = Term::getSectionMap(false);
$model->section = $sectionNames[$_POST['section']];
$model->timebegin = $timeSegs[$model->section][0];
$model->timeend = $timeSegs[$model->section][1];
$model->status = Labclass::STATUS_APPROVED;
if($model->save()){}
}
$this->redirect(array('batch', 'cid' => $model->class_id,
'oid' => $model->course_id, 'tid' => $model->teacher_id)); //重定向到自己,但带上公共数据作为参数,实际进入batch,最后到render部分
}
$this->render('batch', array( //如果$model,$oldmodels均为空的,此时用表单获取公共部分数据
'model' => $model, //如果$model非空,$oldmodels为空,此时$model包含的是公共部分数据
'oldmodels' => $oldmodels, //如果$model,$oldmodels均非空,是更新
));
}
batch视图:
如果有flash,显示
================表单开始
根据$model->isNewRecord,设置$form->action属性(如果是更新,传递id参数回actionBatch)
公共部分信息已经确定,表格显示已经存在的同类模型数据(每个模型后有链接按钮可以提交到deleteForBatch、updateForBatch)
渲染添加新数据或修改旧数据的表单(公共部分数据可以用隐含字段POST,同样用isNewRecord可以区分新旧)
未确定公共信息,就显示表单让用户输入
===============表单结束
public function actionDeleteForBatch($id)
{
$modelToDelete = $this->loadModel($id);
$modelToDelete->delete();
$this->redirect(array('batch', 'cid'=>$modelToDelete->class_id,
'oid'=>$modelToDelete->course_id, 'tid'=>$modelToDelete->teacher_id));
}
public function actionUpdateForBatch($id)
{
$modelToUpdate = $this->loadModel($id);
$this->redirect(array('batch', 'id'=>$id, 'cid'=>$modelToUpdate->class_id,
'oid'=>$modelToUpdate->course_id, 'tid'=>$modelToUpdate->teacher_id));
}
总的来说,我们让actionBatch不带参数,是新增(包含第一步获取公共信息,第三步新增记录),我们让actionBatch不带id参数,但带cid/oid/tid参数,这用于第二步获取公共信息后的重定向筛选显示,以及删除记录后的重定向筛选显示,类似列表显示index,我们让actionBatch带id参数和cid/oid/tid参数,这包含了更新过程。