Laravel 6 – Model的mass assignment

在Laravel的Eloquent ORM中,Mass Assignment是指以数组形式对模型的属性进行赋值,比如:Model->fill(array $attributes)Model::create(array $attributes) 。与之对立的是指单一属性的赋值,比如:Model->age=18

考虑这么一个场景:

在用户注册的时候,后台通常使用User::create($attributes) 来进行用户模型的创建并写入数据库。这里的$attributes数组来自于用户提交的表单中的$_POST[’email’],  $_POST[‘password’]等属性。正常情况下用户注册时候只会提交表单中列出的属性,所以一切正常。

但是如果有个不怀好意的用户,在提交表单时候添加一些虚假数据进来,比如$_POST[‘user_type’]=’admin’。而偏巧数据库中user表结构就有一个字段’user_type’用来表示用户类型是管理员(admin)还是客户(customer)。那么此时他就成功注册了一个管理员用户。这不是我们想见到的。

为了防止这种情况的发送,Laravel提供了mass assignment保护。在定义一个Model类型的时候,必须通过Model->fillable 或者Model->guarded 变量来指定哪些属性是允许mass assignment的。(这两个成员变量继承自trait GuardsAttributes ,默认情况下所有的属性都不允许mass  assignment。)

  • $fillable 数组的作用相当于一个白名单,在数组里的属性都是允许mass assignment的,不在里面的都是不允许的。
  • $guarded 数组的作用相当于黑名单,在数组里的属性都是不允许mass assignment的,不在里面的都是允许的。
  • 注意,在一个模型类的定义里,$fillable$guarded 只需要二选一即可,不要两个都出现。

在执行mass assignment方法的时候,Model会自动忽略不允许mass assignment的属性。详细实现可以查看Model->fill(array $attributes) 的源码,实际上Model::create() 底层也是调用了Model->fill() 对新建实例的$attributes进行填充。mass assignment保护会在底层默默执行而不会报告异常,除非在所有属性都不允许mass assignment的情况下调用mass assignment方法时才会抛出一个MassAssignmentException异常。

以larael/ui自带的用户模型为例:

class User
{
   protected $fillable = [
        'name', 'email', 'password',    // 只有这三个属性是允许mass assignment的,其他均不允许
    ];
    // ... ...
    // 新建用户成功,但email_verified_at会被自动忽略
    \App\User::create([
        'name'=>'test',
        'password'=>'pass',
        'email'=>'test@qq.com',
        'email_verified_at'=>date("Y-m-d H:i:s")
    ]);

    $u1 = \App\User::find(1);
    $u2 = \App\User::find(2);

    // email_verified_at会被自动忽略,name修改成功
    $u1->fill(['email_verified_at'=>date("Y-m-d H:i:s"), 'name'=>'u1_'.date('is')]);
    $u1->save();

    $u2->email_verified_at = date("Y-m-d H:i:s");  // 单一属性赋值是可以的
    $u2->save();                                   // 注意,fill()方法只是修改模型实例,并不会写入数据库。

另外,Model还实现了forceFill(array $attributes) 方法避开mass assignment保护。

 

 

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top