1. 前言

初次遇到 批量赋值 的时候,很容易理解成 批量添加多条数据,实际上并非如此。下面我们举例说明,同时介绍在哪些情况下会进行批量赋值检查,以及该功能的最佳实践。

2. 实例

假设用户表 users 结构如下,且通过 is_admin 字段值为 10 来判断用户是否为管理员,其中 is_admin 字段默认值为 0

+----+-----------+------------------+----------+--------------------------------------------------------------+
| id | name      | email            | is_admin | password                                                     |
+----+-----------+------------------+----------+--------------------------------------------------------------+
|  1 | seekerliu | me@seekerliu.com |        1 | $2y$10$RL6r.MwoJd.oOvKRYhUpmeQI6hUpoG/KgGNhA6X5JrRqfVbooCs92 |
+----+-----------+------------------+----------+--------------------------------------------------------------+

正常情况下,我们通过这种方式新增一个普通用户:

public function store (Request $request, User $user)
{       
    // 赋值
    $user->name = $request->name;
    $user->email = $request->email;
    $user->password = bcrypt($request->password);

    // 新增用户
    $user->save();
}

当有很多属性时,这样赋值比较麻烦。为了方便,我们可以使用 $request->all() 获取用户提交的所有表单数据:

public function store (Request $request, User $user)
{
    // 批量赋值
    $data = $request->all();   

    // 新增用户
    $user->create($data);
}

这种情况下,如果用户提交正确的表单数据,例如:

['name' => 'liu', 'email' => 'liu@seekerliu.com', 'password' => 'test']

那么和我们预想的一样,会新增一个 普通用户

但是如果用户在表单中伪造一个 ['is_admin' => 1] 字段,就会新增一个 管理员用户

为单个模型实例赋值一个或多个属性,从而新增或更新对应的模型记录,这就是批量赋值。

Laravel 为批量赋值提供了保护方法,那就是在模型上定义 fillableguarded 属性,例如:

class User extend Model
{
    protected $fillable = ['name', 'email', 'password'];
}

或者:

class User extend Model
{
    protected $guarded = ['is_admin'];
}

这样,在执行 create() 方法时,Eloquent 模型会先使用 fill() 方法对数据进行过滤,去掉 $fillable 以外的字段(白名单),或者去掉 $guarded 中的字段(黑名单),来保证只获取预期的表单字段。

3. 适用场景

在 Laravel 中,哪些情况下会进行批量赋值检查呢?

有以下三种情况:

  1. 新增模型:
    User::create(['name' => 'seekerliu']);
  2. 更新模型:
    $user->update(['name' => 'seekerliu']);
  3. 使用 fill 方法:
    $user->fill(['name' => 'seekerliu']);

批量赋值主要针对单个模型实例,而不适用于一次更新多条记录的情况。例如:

User::where('billing', '>=', 10000)->update(['vip' => 1]);

因为一次更新多条记录时,实际上并不会获取模型实例。

4. 最佳实践

首先,将允许用户更新的模型字段列在 $fillable 属性中。接收用户提交的数据后,使用 fill 方法赋值给模型实例。然后再补充模型实例的其它属性。最后调用 save 方法保存或更新。

app/Models/Article.php

.
.
.
    protected $fillable = [
        'title', 'category_id', 'body_original',
    ];
.
.
.

app/Http/Controllers/ArticlesController.php

.
.
.
    public function store(ArticleRequest $request, Article $article, MarkdownParser $parser)
    {
        $article->fill($request->all());
        $article->user_id = Auth::id();
        $article->body = $parser->convertMarkdownToHtml($article->body_original);
        $article->save();

        return redirect()->route('articles.show', $article)->with('success', trans('Operation succeeded.'));
    }
.
.
.

5. 参考资料