phplaravellaravel-5

Insert related models: "Field doesn't have a default value"


The character field is filled by a form (dd() proves), but this error is showing up:

General error:

1364 Field 'character' doesn't have a default value (SQL: insert into `characters` (`name`, `user_id`, `updated_at`, `created_at`)

Save or Create?

I try to save another model related to a user. There is form data and internal data. That's why I can't just use the create-method, right? Nobody should be able to manipulate some of these values.

public function store()
{
    // validation

    $character = new Character([
        'user_id' => auth()->id(),
        'character' => request('character'),
        'name' => request('name'),
        'level' => 1,
        'experience' => 0,
        'health' => 500,
        'primary' => 'test',
        'secondary' => 'test',
    ]);
    $user = auth()->user();
    $user->characters()->save($character);

    // redirect
}

My two main questions:

  1. Why would this throw a SQL error pointing to character?
  2. Is this good saving approach, or should I make everything fillable and use create?

Solution

  • The $fillable property doesn't just affect the create() method, it affects anything that uses the fill() method. When you pass attributes to a new instance through the constructor, this also uses the fill() method behind the scenes. Therefore, all of these values are affected by the $fillable property as well. So, if character is not in the $fillable array, the code you have shown will throw the error.

    It is hard to say what a "good" approach is because all approaches have different trade-offs regarding security and usability. If your model is completely unguarded, that makes code easy to write, but it is easier to introduce security issues. If your model is completely guarded, the code must be much more verbose, but is less prone to security issues. It all depends on what you're comfortable with.

    The important thing is to know how the framework works so you can know understand these trade-offs and determine what is "good" for you and your application.

    Personally, unless the situation dictates otherwise, I tend to make all fields fillable except for primary and foreign keys. The responsibility of protecting the fillable fields then falls to where the input is processed (e.g. controller actions).

    public function store()
    {
        // validation
    
        // make sure to only accept "character" and "name" input from the user.
        // all other fields are defaulted.
        // note: foreign key user_id has been removed
        // note: all these fields must be fillable or else they will be skipped
        $data = array_merge(
            $request->only(['character', 'name']),
            [
                'level' => 1,
                'experience' => 0,
                'health' => 500,
                'primary' => 'test',
                'secondary' => 'test',
            ]
        );
    
        // create a new instance with the given data; does not touch the db yet.
        $character = new Character($data);
    
        $user = auth()->user();
    
        // assigns the foreign key field then saves record to the database.
        $user->characters()->save($character);
    
        // redirect
    }