Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eloquent ORM: Define allowed model attributes

In laravel's eloquent ORM, is there a way to define a model's allowed attributes?

By default I can put any attributes into the model's constructor - but then I only get notified about the erroneous attribute names when I actually try to save the model to database.

Example code:

// this works although there is a typo in "lastname"
$user = new \App\User(['firstname' => 'foo', 'lastnam' => 'bar']);

// this errors out with an SQL error
$user->save();

So, is there a way to let Laravel automatically check if there are invalid keys in the request's input data?

like image 462
cweiske Avatar asked Nov 11 '16 12:11

cweiske


3 Answers

If you would like to prevent not only filling not allowed attributes using fill() method but also directly setting them, like $model->foo = 'bar', then you got to override Model::setAttribute() method.

Best to do it in a custom base Model that extends Eloquent. So in app/Model.php:

namespace App;

use Exception;
use Illuminate\Database\Eloquent\Model as Eloquent;

class Model extends Eloquent
{
    // this should be actually defined in each sub-model
    protected $allowed = ['firstname', 'lastname'];

    public function setAttribute($key, $value)
    {
        // this way we can allow some attributes by default
        $allowed = array_merge($this->allowed, ['id']);

        if (! in_array($key, $allowed)) {
            throw new Exception("Not allowed attribute '$key'.");
        }

        return parent::setAttribute($key, $value);
    }
}

Then in the models that should not allow invalid attributes you can extend this base model:

use App\Model;

class User extends Model
like image 186
Paul Avatar answered Oct 08 '22 05:10

Paul


I don't believe this can be done natively. I think Laravel is intentionally permissive in that sense, and I personally don't mind having a SQL error instead of an Eloquent one if I make a mistake setting attributes somewhere.

That being said, it's not hard to customize your Models to fail when non-existing attributes are set:

// User.php

protected $fillable = [
    'firstname',
    'lastname',
];

public function fill(array $attributes)
{
    foreach ($attributes as $key => $value) {
        if (!in_array($key, $this->getFillable())) {
            throw new \Exception("Attribute [{$key}] is not fillable.");
        }
    }

    return parent::fill($attributes);
}
like image 6
Tomas Buteler Avatar answered Oct 08 '22 05:10

Tomas Buteler


When you're adding attributes like this, Laravel uses the fill() method which is part of mass assignment feature:

if ($this->isFillable($key)) {
    $this->setAttribute($key, $value);
} elseif ($totallyGuarded) {
    throw new MassAssignmentException($key);
}

So, to make it work add all allowed values you want to be saved to $fillable array :

$fillable = ['firstname', 'lastname'];
like image 4
Alexey Mezenin Avatar answered Oct 08 '22 03:10

Alexey Mezenin