I am experiencing a strange problem with eloquent.
I've got two eloquent models, a User model and a Profile model which both have separate tables and have a one to one relationship.
I have a single form where the data for both of these models is passed into. So data belonging to User and Profile is present in the form.
I tried to perform the inserts as cleanly as possible from the controller:
$user = $this->users->createUser($request->all());
the createUser
function is defined like this
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;
}
But this produces a sql error, because eloquent assigns all the values from the form to both models, so User and Profile both get all the same properties, so eloquent tries to insert into columns that don't exist for the given model.
I thought that the $fillable
field was used to prevent this sort of thing from happening, but I do have the $fillable
fields defined in both models:
// User
protected $fillable = ['email', 'password', ...];
// Profile
protected $fillable = ['first_name', 'last_name', ...];
I've also tried this: (new User)->fill($data)
but that doesn't work, nor should it since the fill
method is also called from the Model
constructor.
In my mind this is not the expected behaviour, but then again, I may have misunderstood the concept of fillable. Any insight would be appreciated. I know this can be solved by declaring each key I want to send to the constructor, but that seems very messy to me. Also, I am using laravel v5.2.29
EDIT
Upon further testing I've come to the conclusion that this only happens inside when I run the database seeders, where I do use these functions. Digging deeper, I see that this makes perfect sense, since running the seed command "unguards" the base Model class. So this is expected behaviour.
My question is, is there any way to override this default, so that the models don't get "unguarded" when running the seed command, so that I can use my helper functions to populate my test database, or do I have to correctly define each data record manually inside the seeder classes?
Yes, from some version of Laravel 5.2 when using seeding models are unguarded as you noticed. In fact it's quite reasonable it's unguarded, but if you want to change it, well it's possible but not so simple.
Here are steps you should do to achieve that:
Comment in config/app.php
line Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class
, add add line with your custom ConsoleSupportServiceProvider
class.
Create this class same as original ConsoleSupportServiceProvider
(actually you can extend it) but in $providers
property change Illuminate\Database\SeedServiceProvider
into custom one
Again create custom SeedServiceProvider
that extends original SeedServiceProvider
and now override registerSeedCommand
to create again custom SeedCommand
class
Create custom SeedCommand
class that extends original SeedCommand
and now all you need is changing:
public function fire()
{
if (! $this->confirmToProceed()) {
return;
}
$this->resolver->setDefaultConnection($this->getDatabase());
Model::unguarded(function () {
$this->getSeeder()->run();
});
}
into
public function fire()
{
if (! $this->confirmToProceed()) {
return;
}
$this->resolver->setDefaultConnection($this->getDatabase());
$this->getSeeder()->run();
}