在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保护。