2017-07-04 21:27:51 11859次浏览 4条回答 4 悬赏 100 金钱

最近在使用场景的时候碰到一些不解的问题,我简单模拟下:
我新建一张表info,有name和age2个字段
模型代码:

public function rules()
{
    return [
        [['age'], 'integer'],
        [['name'], 'string', 'max' => 22],
        ['name','required','message'=>'姓名不能为空','on'=>['add','update']],
        ['age','required','message'=>'年龄不能为空','on'=>'add'],
        ['age','isMath','on'=>'add']
    ];
}

public function scenarios()
{
    return [
        'add' => ['age','name'],
        'update'=>['name']
    ];
}

当我进行编辑操作的时候,使用$model->scenario = 'update';也就是使用update场景,出现的结果是:name改变了age竟然没有更改,我把代码修改下

public function scenarios()
{
    return [
        'add' => ['age','name'],
        'update'=>['name','age'] //这里加个age
    ];
}

这个时候正常修改了name和age字段

还有就是我不重写 scenarios() 方法,也是正常执行验证的

所以有2个问题:
1.重写 scenarios() 方法,具体应该在什么时候?
2.上面代码加个age和不加age区别是什么?在update场景下我只验证name字段,age只在add场景下,但是在update场景下 'update'=>['name'] 没有写age就不能修改age字段?????

最佳答案

  • drodata 发布于 2017-07-05 09:20 举报

    默认情况下,scenarios() 返回的是一个关系数组,键是模型中所有可用的场景,值是场景对应的 active attributes 列表。active attributes 有两个特点:

    • 所说,调用 validate() 时仅会验证 active attributes
    • 使用 $model->load() && $model->save() 保存数据时只保存 active attributes;

    在你的例子中,把 'age' 从 update 场景中移除意味着 age 不再是 active attribute, 这就是为什么对 age 的修改没有写入数据库的原因。

    默认的 scenarios() 过程(以你的例子中声明的规则为例)

    scenarios() 会进行两次遍历操作。首先遍历 rules 中声明的规则,把所有可用的场景找出来,并初始化数组,以你的例子中的 rules 为例,值为:

    [
        'default' => [], // 框架自带的默认场景
        'add' => [],
        'update' => [],
    ]
    

    第二次遍历时,对每个 rule 中声明的 attributes “对号入座”,如果 rule 中不带 'on' 属性,表示该属性所属的规则适用于所有场景(例如 [['age'], 'integer']), 会将 'age' 追加到所有场景的 active attibutes 列表中;如果指定了场景值,仅在对应的场景下的active attributes 内追加。

    经过这两次遍历,scenarios() 最终返回的值将是:

    [
        'default' => ['age', 'name'], 
        'add' => ['age', 'name'],
        'update' => ['age', 'name'],
    ]
    

    可以看到, 'age' 在每个场景中都是 active attribute, 这就是为什么在你不重写 scenarios() 的情况下,对 age 的修改也能保存的原因。

    了解了这个过程,我的理解是:在大多数情况下,是不需要重写 scenarios() 的,通过配置 rules() 来改变 scenarios() 的返回值,而不是直接手动覆盖 sceanrios(). 覆盖带来的一个明显的弊端是:你的精力被分散到了两个方法内(rules() 和 scenarios()),假设将来你的 info 表又新增了一列,在 rules() 追加新的规则的同时,你还要修改重写后的 scenarios(), 把新增加的列追加进去使其成为 active attribute,否则就会出现新增列的数据无法存入数据库。使用默认的 scenarios() 的话,只需要将精力放在 rules() 上即可。

    2 条回复
    回复于 2017-07-05 12:34 回复

    如果不重写scenarios()方法,把rules()改成如下,应该正常运行的吧?

    {
        return [
            [['age'], 'integer'],
            [['name'], 'string', 'max' => 22],
            ['name','required','message'=>'姓名不能为空'], //这里不写场景
            ['age','required','message'=>'年龄不能为空','on'=>'add'],
            ['age','isMath','on'=>'add']
        ];
    }
    
    回复于 2017-07-05 13:34 回复

    可以,而且去掉一个场景后让规则看上去更简洁明了。

    , , , 觉得很赞
  • 回答于 2017-07-04 21:38 举报

    2、你在update场景中没有配置age当前就不能修改了。
    yii是照这个清单去一个个校验的,清单上没有的东西,不处理。

    觉得很赞
  • 回答于 2019-11-18 19:47 举报

    我有个很大的疑惑:

    public function scenarios()
    {
        return [
            'add' => ['age','name'],
            'update'=>['name','age'] //这里加个age
        ];
    }
    

    这样写的话,如果我调用 'add'场景,那么系统应该知道我需要验证 ‘age’,'name'这两个属性;
    那问题来了。

    为什么还要在 rules()的age属性中开启'on'=>['add']呢?
    为什么要这么做?这么设计的原因是什么?

    1 条回复
    回复于 2019-11-18 21:18 回复

    rules是表单元素验证, scenarios是表单允许上传哪些元素。

  • 回答于 2020-09-01 15:57 举报

    那么请问rules()函数和scenarios()函数的关系是什么?

您需要登录后才可以回答。登录 | 立即注册
xyf90314
副总裁

xyf90314

注册时间:2015-03-04
最后登录:2023-03-13
在线时长:95小时23分
  • 粉丝21
  • 金钱5257
  • 威望40
  • 积分6607

热门问题