Appearance
Eloquent models: part 1
PODCAST
- In a previous section, we created model classes for all the database tables
- All models can be found in the app/Models folder
- The Eloquent ORM (Object-Relational Mapping) included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database
- Each database table has a corresponding model which is used to interact with that table
- Models allow you to:
- query for data in your tables, as well as insert, update or delete a specific table row
- protect attributes from mass assignment
- add relationships between tables
- transform Eloquent attribute values when you retrieve or set them on model instances (accessors, mutators, attribute casting, ...)
- add additional attributes that do not have a corresponding column in your database
- apply scoped filtering
- prevent attributes to be visible in JSON output
- ...
REMARKS
- In this chapter, we only discuss mass assignment and relations
- The other methods will be implemented later in part 2
Prevent mass assignable vulnerability
- Mass assignment vulnerability means that users can modify data items that the user should not be allowed to
e.g. access passwords, grant permissions, add administrator status to himself, ... - Mass assignment means that you're assigning multiple values to attributes in a single operation (insert or update a record)
- Laravel has 2 protected methods to protect against Mass assignment vulnerability:
protected $fillable = []
andprotected $guarded = []
- It's preferable to uso only one of them, not both.
- If you use
$fillable
, you should only specify the attributes that are mass assignable - If you use
$guarded
, you should specify the attributes that are NOT mass assignable - If you use both, the
$guarded
array will take precedence over the$fillable
array
- If you use
- The user model was already created by Laravel and uses the
$fillable
array - By default, only the attributes
name
,email
andpassword
are allowed to be mass assignable through e.g. a form
php
protected $fillable = ['name', 'email', 'password'];
1
- Add the attributes
active
andadmin
to the$fillable
array
php
protected $fillable = ['name', 'email', 'password', 'active', 'admin',];
1
Add relationships
- The relations between the primary keys and foreign keys are already defined in SQLite (through the code in the corresponding migrations)
- It's also necessary to define those relations in the Eloquent models
- The database of our application only contains one-to-many and many-to-one relations
SUMMARY FOR OUR DATABASE TABLES
Genre 1 <-> ∞ Record
- If we consider this relation as a one-to-many (1 -> ∞) relationship, we can say that "a genre has many records"
- In Eloquent, we define this relationship by applying the
hasMany()
method on our Genre model, with the Record class ('Record::class'
) as parameter - Eloquent will automatically determine the proper foreign key column in the Record model, i.e.
genre_id
(the name of the owning model, suffixed with_id
) - The former code should be wrapped in a method
records()
, which shall be used later on to query all the records of a specific genre - Open the app/Models/Genre.php file and add the following code inside the
Genre
class
- In Eloquent, we define this relationship by applying the
php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Genre extends Model
{
use HasFactory;
protected $guarded = ['id', 'created_at', 'updated_at'];
// Relationship between models
public function records()
{
return $this->hasMany(Record::class); // a genre has many "records"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NAMING CONVENTIONS hasMany()
The relationship method should be named according to the used model (with lower case), and plural case
Full method: hasMany('model', 'foreign_key', 'primary_key')
- The first parameter is the model to refer to (
Record::class
) - You may omit the third parameter if the primary key of the referred model (
Record
) is namedid
- You may omit the second parameter if the foreign key of the owning model (
Genre
) is the the "snake_case" name of the owning model, suffixed with_id
php
// Relationship between models
public function records()
{
// short version
return $this->hasMany(Record::class);
// long version
return $this->hasMany(Record::class, 'genre_id', 'id');
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
More info: One To Many
Record ∞ <-> 1 Genre
- Inversely, we can look at the same relation as a many-to-one (∞ -> 1) relationship: "a record belongs to a genre"
- In Eloquent, we define this relationship by applying the
belongsTo()
method on our Record model, with the Genre model ('Genre::class'
) as parameter - Eloquent will try to match the
genre_id
from the Record model to anid
in the Genre model- The
withDefault()
method returns an empty Genre model (instead ofnull
) if thegenre_id
does not match anid
in the Genre model, which results in less conditional checks (and is often referred to as the Null object pattern)
- The
- The former code should be wrapped in a method
genre()
, which shall be used later on to query the genre of a specific record - Open the app/Models/Record.php file and add the following code inside the
Record
class
- In Eloquent, we define this relationship by applying the
php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Record extends Model
{
use HasFactory;
protected $guarded = ['id', 'created_at', 'updated_at'];
// Relationship between models
public function genre()
{
return $this->belongsTo(Genre::class)->withDefault(); // a record belongs to a "genre"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NAMING CONVENTIONS belongsTo()
The relationship method should be named according to the used model (with lower case), and singular case
Full method: belongsTo('model', 'foreign_key', 'primary_key')
- The first parameter is the model to refer to (
Genre::class
) - You may omit the third parameter if the primary key of the referred model (
Genre
) is namedid
- You may omit the second parameter if the foreign key of the owning model (
Record
) is named the name of the relationship method, suffixed with_id
php
/** Relationship between models ...*/
public function genre()
{
// short version
return $this->belongsTo(Genre::class)->withDefault();
// long version
return $this->belongsTo(Genre::class, 'genre_id', 'id')->withDefault();
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
More info: One To Many (Inverse)
Define the other relations in the Eloquent models
- A user has many orders
php
class User extends Model
{
use HasFactory;
/** The attributes that are mass assignable ...*/
protected $fillable = ['name', 'email', 'password', 'active', 'admin',];
// Relationship between models
public function orders()
{
return $this->hasMany(Order::class); // a use has many orders
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
More naming conventions
- The "magic" behind Laravel only works when you follow the naming conventions
- As we've seen earlier in this chapter, if you follow the naming convention for the relations, the return statement can be very short
- Some other naming conventions that make your live easier for working with Laravel are:
Table names
- By convention, the "snake_case", plural name of the class will be used as the table name
- If your model's corresponding database table does not fit this convention, you must manually specify the model's table name by defining a
protected $table
property - Some examples:
Model | Database table | $table property |
---|---|---|
Flight | flights | (not needed) |
MainCountry | main_countries | (not needed) |
User | gebruikers | protected $table = 'gebruikers' |
Primary keys
- By convention, the primary key column must be named
id
- If the primary key does not fit this convention, you must manually specify the primary key by defining a
protected $primaryKey
property
Table | Primary key | $primaryKey property |
---|---|---|
Flight | id | (not needed) |
Flight | flight_id | protected $primaryKey = 'flight_id' |