第二层内功 - 表单模型 - 多场景 ¶
作者:KK
发表日期:2017.3.2
表单模型就是所谓的“逻辑层”,但并非一个表单就只是一个业务逻辑,其实一个表单可以定义多个业务逻辑
所以在不同业务逻辑的情况下,就意味着有不同的场景,比如说一个文章表单,有添加场景,也有编辑场景
定义多场景的表单 ¶
下面是一个带有添加/编辑的文章表单示例:
namespace app\forms;
use Yii;
use app\models\Article;
class ArticleForm extends \yii\base\Model{
const SCENE_ADD = 'add'; //添加场景
const SCENE_EDIT = 'edit'; //编辑场景
public $id = 0; //文章ID,编辑时需要
public $title = ''; //文章标题
public $categoryId = 0; //分类ID
public $content = ''; //文章内容
private $_article = null; //文章实例
public function rules(){
return [
[['id', 'title', 'categoryId', 'content'], 'required'],
['title', 'string', 'length' => [4, 20]],
['content', 'string', 'length' => [10, 65535]],
[['title', 'content'], 'safe'],
[['id', 'categoryId'], 'integer'],
['id', 'validateId'],
['categoryId', 'exist', 'targetClass' => 'app\models\ArticleCategory', 'targetAttribute' => 'id', 'message' => '无效的文章分类'],
];
}
//定义不同场景所需要校验的表单属性
public function scenarios(){
return [
//添加,需要标题、分类、内容
self::SCENE_ADD => ['title', 'categoryId', 'content'],
//编辑,需要ID、标题、内容,没有分类,表示禁止修改分类
self::SCENE_EDIT => ['id', 'title', 'content'],
];
}
public function validateId(){
$article = Article::findOne($this->id);
if(!$article){
$this->addError('id', '无效的文章ID');
return;
}
$this->_article = $article;
}
public function add(){
if(!$this->validate()){
return false;
}
$article = new Article([
'title' => $this->title,
'category_id' => $this->categoryId,
'content' => $this->content,
'add_time' => time(),
]);
if(!$article->save()){
throw new \yii\base\ErrorException('添加文章失败');
}
return $article;
}
public function edit(){
if(!$this->validate()){
return false;
}
Yii::configure($this->_article, [
'title' => $this->title,
'content' => $this->content,
]);
if(!$this->_article->save()){
throw new \yii\base\ErrorException('编辑文章失败');
}
return $this->_article;
}
}
以上表单中,add方法就是添加文章的业务逻辑,edit方法是编辑文章咯,可是两个业务要验证的用户端输入参数都包含了title和content,所以要通过scenarios
方法来声明两个场景要校验的属性名称
当执行validate的时候,底层会自动在rules找到相关的规则进行校验,不会对无关的属性规则进行校验
控制器调用示例 ¶
public function actionAdd(){
$form = new ArticleForm([
'scenario' => ArticleForm::SCENE_ADD,
]);
//菜鸟注意:如果前端用ActiveForm就别像我这样传第2个参数空字符串
if(!$form->load(Yii::$app->request->post(), '')){
return '接收参数失败';
}
if($article = $form->add()){
return '添加成功,文章ID是:' . $article->id;
}else{
return $form->firstError[0];
}
}
public function actionEdit(){
$form = new ArticleForm([
'scenario' => ArticleForm::SCENE_EDIT,
]);
//菜鸟注意:如果前端用ActiveForm就别像我这样传第2个参数空字符串
if(!$form->load(Yii::$app->request->post(), '')){
return '接收参数失败';
}
if($form->edit()){
return '保存完毕';
}else{
return $form->firstError[0];
}
}
如果表单定义了多场景,执行add或edit这些业务处理方法前(其实是validate被执行之前),一定要先设定scenario
属性(属性值就是场景的标识)
最终其实就是为了告诉validate要对哪些属性进行validate,而不是盲目地全部validate,毕竟不同场景下有不同的校验字段
深入应用 ¶
其实不能仅仅把场景理解为使用在validate控制上的,自己在form里面写的逻辑代码都可以if($this->scenario == self::SCENE_ADD)
这样来判断确定是否要做某些逻辑(当该方法与其它场景共用时)
这种情况很少,但是会有,比如我在xoa项目中这个TaskForm表单用到场景判断