Laravel Eloquent Relationships
Table of Contents
- One To One Relationships
- One To Many Relationships
- Many To Many Relationships
- One To Many (Polymorphic)
One To One Relationships
hasOne()
class User extends Model
{
/**
* Get the phone associated with the user.
*/
public function phone()
{
return $this->hasOne(Phone::class);
}
}
- The phones table is expected to have a user_id column that is a foreign key pointing to the id primary key of the users table.
- To define this relationship on the Phone model (so that you can get to the user from the phone*, then add the belongsTo() method to the Phone model.
- If instead, you have a phone_id column in the users table that points to the id in the phones table, then use the belongsTo() relationship here.
belongsTo()
Rule of thumb: Use this method when this model's table has the column that contains the ID of the other table's primary key (foreign key).
class Phone extends Model
{
/**
* Get the user that owns this phone
*/
public function user()
{
return $this->belongsTo(Phone::class);
}
}
- The phones table is expected to have a user_id column that is a foreign key pointing to the id primary key of the users table.
- To define this relationship on the User model (so that you can get to the phone from the user), then add the hasOne() method to the User model.
- If instead, you have a phone_id column in the users table, then use the hasOne() relationship here.
Specifying Keys
Please refer here to avoid me having to duplicate.
hasOneThrough()
- This method can only be used in a chain of one-to-one relationships and is incredibly niche.
- The requirements of this method are so niche, that I can't think of a single example of this being useful, and the example given by the docs is pretty rubbish too, because why would a mechanic be limited to just one car.
- TLDR; not worth looking at.
One to Many Relationships
hasMany()
Rule of thumb: Use this method when this model's id is referred to by a column in another model's table, and there is not a one-to-one relationship. If it is a one-to-one relationship, then you want to use the hasOne() method instead. The other model should probably have a belongsTo() method referring to this model.
class User extends Model
{
/**
* Get all of the phones the user owns.
*/
public function phones()
{
return $this->hasMany(Phone::class);
}
}
Specifying Keys
Laravel will guess the name of the column in the other table that should be referring to this table. However, you can manually specify it. In this example,
we are editing the users table with hasMany() to refer to the author_id
column in the events table, rather than the name user_id
that Laravel would
otherwise guess.
class User extends Model
{
/**
* Get the events that this user created/authored.
*/
public function createdEvents()
{
return $this->hasMany(Event::class, 'author_id');
}
}
Taking it further, if one needs to specify the local column that is being referred to (e.g. its not the field marked as the primary key in this model, which is usually id), then one can specify that as well. In this example I am specifying this model's/table's legacy_id field as the one being referenced.
class User extends Model
{
/**
* Get the events that this user created/authored.
*/
public function createdEvents()
{
return $this->hasMany(Event::class, 'author_id', 'legacy_id');
}
}
hasMany
method are called foreignKey
and localKey
, but actually they take the names of the columns/fields,
and not the key names, which are usually something like users_pkey1
. This saves you a lot of effort looking up what the keys are callled.
belongsTo()
Not only does belongsTo() work in one-to-one relationships, but it also applies to one-to-many relationships. This is because there is still just one "parent" model that this child model being edited is associated with. In this case, there are just multiple children instead of just one.
Rule of thumb: Use this method when this model's table has the column that contains the ID of the other table's primary key (foreign key).
Specifying Keys
If one is not using naming conventions that Laravel expects, then one can manually specify the name of the local column and the remote table's column like so:
class Phone extends Model
{
/**
* Get the user that owns this phone.
*/
public function user()
{
return $this->belongsTo(User::class, 'owner_id', 'legacy_id');
}
}
As you can see, we are specifying the local column as owner_id
, instead of Laravel guessing user_id
, and
telling Laravel that this references the legacy_id
column on the remote table, rather than the id
column.
hasManyThrough()
Use this relationship when you wish to get to another model through an intermediary. However, be careful to check that the relationships are many-to-one, and not actually many-to-many. In case of many-to-many, you will want to use belongsToMany() instead.
class Project extends Model
{
/**
* Get all of the deployments for the project.
*/
public function deployments()
{
return $this->hasManyThrough(Deployment::class, Environment::class);
}
}
hasOne() - of Many
@TODO. For now, there's this.
Many To Many Relationships
belongsToMany()
Use this relationship whenever there is a many-to-many relationship through the use of a mapping table (Laravel refers to this as a "pivot table").
class Permission extends Model
{
/**
* Get the users that have this permission.
*/
public function users()
{
$intermediateTableName = 'user_permissions';
return $this->belongsToMany(User::class, $intermediateTableName);
}
}
class User extends Model
{
/**
* Get the permissions the user has been granted.
*/
public function permissions()
{
$intermediateTableName = 'user_permissions';
return $this->belongsToMany(Permission::class, $intermediateTableName);
}
}
One To Many (Polymorphic)
Just don't. Pretend you never read anything about this.
You would either have to not set up foreign keys on your tables, or if you do manage to, you will have to create a messy/confusing solution. In case it comes up, don't forget that PostgreSQL inheritance (currently) has a lot of caveats that pretty much make it useless.
Just have a think, and I'm sure you will be able to come up with a much better solution where you can have database normalization and your foreign-key set up correctly.
References
First published: 8th August 2022