Laravel eloquent 播种期间无人看管

Laravel eloquent unguarded during seeding

我在使用 eloquent 时遇到了一个奇怪的问题。

我有两个 eloquent 模型,一个用户模型和一个配置文件模型,它们都有单独的表并且具有一对一的关系。

我有一个表单,其中传递了这两个模型的数据。因此属于 User 和 Profile 的数据出现在表单中。

我尝试从控制器尽可能干净地执行插入:

$user = $this->users->createUser($request->all());

createUser函数定义如下

public function createUser(array $data)
{
    $user = new User($data);
    // set some non relevant user options here...
    $user->save();

    $profile = new Profile($data);
    $user->profile()->save($profile);

    return $user;
}

但这会产生 sql 错误,因为 eloquent 将表单中的所有值分配给两个模型,因此 User 和 Profile 都获得所有相同的属性,所以 eloquent尝试插入给定模型不存在的列。

我以为 $fillable 字段是用来防止这种事情发生的,但我确实在两个模型中都定义了 $fillable 字段:

// User
protected $fillable = ['email', 'password', ...];

// Profile
protected $fillable = ['first_name', 'last_name', ...];

我也试过这个:(new User)->fill($data) 但这不起作用,也不应该,因为 fill 方法也是从 Model 构造函数调用的。

在我看来,这不是预期的行为,但话又说回来,我可能误解了可填充的概念。任何见解将不胜感激。我知道这可以通过声明我想发送给构造函数的每个键来解决,但这对我来说似乎很混乱。另外,我正在使用 laravel v5.2.29

编辑

经过进一步测试,我得出的结论是,这只会在我 运行 数据库播种器内部发生,我确实在其中使用了这些函数。深入挖掘,我发现这是完全合理的,因为 运行 将种子命令 "unguards" 设置为基本模型 class。所以这是预期的行为。

我的问题是,有什么方法可以覆盖此默认值,以便在 运行 执行种子命令时模型不会得到 "unguarded",这样我就可以使用我的辅助函数填充我的测试数据库,还是我必须在播种机中手动正确定义每个数据记录 classes?

是的,从 Laravel 5.2 的某些版本开始,当使用播种模型时,正如您所注意到的那样无人看管。其实不设防也是很合理的,但如果你想改,也可以,但没那么简单。

以下是您应该执行的步骤:

  1. config/app.phpIlluminate\Foundation\Providers\ConsoleSupportServiceProvider::class 中添加注释,添加自定义行 ConsoleSupportServiceProvider class.

  2. 创建与原始 ConsoleSupportServiceProvider 相同的 class(实际上您可以扩展它)但在 $providers 属性 中更改 Illuminate\Database\SeedServiceProvider进入自定义

  3. 再次创建扩展原始 SeedServiceProvider 的自定义 SeedServiceProvider,现在覆盖 registerSeedCommand 以再次创建自定义 SeedCommand class

  4. 创建扩展原始 SeedCommand 的自定义 SeedCommand class,现在您只需要更改:

    public function fire()
    {
        if (! $this->confirmToProceed()) {
          return;
       }
    
       $this->resolver->setDefaultConnection($this->getDatabase());
    
       Model::unguarded(function () {
          $this->getSeeder()->run();
       });
    }
    

    进入

    public function fire()
    {
        if (! $this->confirmToProceed()) {
          return;
       }
    
       $this->resolver->setDefaultConnection($this->getDatabase());
    
       $this->getSeeder()->run();
    
    }