# Eloquent models (part 1)
- 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 (opens new window) ORM (Object-Relational Mapping) included with Laravel provides a beautiful, simple ActiveRecord (opens new window) implementation for working with your database
- Each database table has a corresponding model which is used to interact with that table
- In fact, the model represents a single row in the 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
- ...
REMARKS
- In this chapter, we only discuss mass assignment and relations
- The other methods will be implemented later in part 2
# Model blueprint
- We have created a little blueprint with the most used (not all) public and private methods for a models
- Open the model classes inside the App/Models/ folder and:
- Line 5: add a reference the Illuminate Attribute class
- Line 13-54 add the blueprint INSIDE the class
Genre.php
Record.php
Order.php
Orderline.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Genre extends Model { use HasFactory; /** * The attributes that are mass assignable * $fillable: array of attributes that are mass assignable * $guarded: array of attributes that are not mass assignable * REMARK: the save() methode does not pass the guarded attributes! */ protected $guarded = ['id', 'created_at', 'updated_at']; /** * Relationship between models * hasMany('model', 'foreign_key', 'primary_key'): method name is lowercase and plural case * belongsTo('model', 'foreign_key', 'primary_key')->withDefaults(): method name is lowercase and singular case */ /** * Accessors and mutators (method name is the attribute name) * get: transform the attribute after it has retrieved from database * set: transform the attribute before it is sent to database */ /** * Add additional attributes that do not have a corresponding column in your database * REMARK: additional attributes are not automatically included to the model! * - add the attributes to the $appends array to include them always to the model * - or append the attributes in runtime with Model::get()->append([]) */ protected $appends = []; /** * Apply the scope to a given Eloquent query builder * prefix the method name with 'scope' e.g. 'scopeIsActive()' * Utilize the scope in the controller Model::is_active()->get(); */ /** * Add attributes that should be hidden to the $hidden array */ protected $hidden = []; }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 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 = []
: array of attributes that are mass assignableprotected $guarded = []
: array of attributes that are NOT mass assignable
- Use only one of them, not both!
User.php
Genre.php
- 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
protected $fillable = ['name', 'email', 'password'];
Copied!
1
- Add the attributes
active
andadmin
to the$fillable
array
protected $fillable = ['name', 'email', 'password', 'active', 'admin'];
Copied!
1
# Add relationships
- The relations between the primary keys and foreign keys are already defined in MySQL (through the code in the corresponding migrations)
- It's also necessary to define those relations in the Eloquent models (opens new window)
- 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
- In Eloquent, we define this relationship by applying the
// app/Models/Genre.php class Genre extends Model { use HasFactory; /** The attributes that are mass assignable ...*/ protected $guarded = ['id', 'created_at', 'updated_at'] /** Relationship between models ...*/ public function records() { return $this->hasMany(Record::class); // a genre has many records } }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
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
/** Relationship between models ...*/ public function records() { // long version return $this->hasMany(Record::class, 'genre_id', 'id'); // short version return $this->hasMany(Record::class); }
Copied!
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
More info: One To Many (opens new window)
- 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 (opens new window))
- 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
- In Eloquent, we define this relationship by applying the
// app/Models/Record.php class Record extends Model { use HasFactory; /** The attributes that are mass assignable ...*/ 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 } }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
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
/** Relationship between models ...*/ public function genre() { // long version return $this->belongsTo(Genre::class, 'genre_id', 'id')->withDefault(); // short version return $this->belongsTo(Genre::class)->withDefault(); }
Copied!
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
More info: One To Many (Inverse) (opens new window)
# Define the other relations in the Eloquent models
User.php
Order.php
Orderline.php
- A user has many orders
class User extends Model { use HasFactory; /** The attributes that are mass assignable ...*/ protected $fillable = ['name', 'email', 'password',]; /** Relationship between models ...*/ public function orders() { return $this->hasMany(Order::class); // a use has many orders } }
Copied!
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' |