Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Laravel Eloquent Relationships

Table of Contents

  1. One To One Relationships
    1. hasOne()
    2. belongsTo()
    3. hasOneThrough()
  2. One To Many Relationships
    1. hasMany()
    2. belongsTo()
    3. hasManyThrough()
    4. hasOne() - of Many
  3. Many To Many Relationships
    1. belongsToMany()
  4. 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.

This assumes a user can own just one phone, not multiple phones.

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.

This assumes a user can own just one phone, not multiple phones.

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);
    }
}

This is likely going to be one of your most used relationships.

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');
    }
}

The variables for the 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).

In this case, each user can have any number of phones, not just one.

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.

You can have foreign keys in both MySQL and PostgreSQL that do not reference the primary key of another table. However, the column being referenced will need to be a unique key.

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);
    }
}

In this case, we are accessing all of the deployments for a project, with each deployment belonging to one environment, and each environment belonging to one project. Hint, check those other two models have belongsTo().

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);
    }
}

You can choose not to provide the name of the intermediary table, in which case laravel will try to derive it as the names of the two models combined in alphabetical order. Thus, this is likely to go wrong half of the time, unless you took this into account when naming your intermediate table.

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

Last updated: 19th August 2022
First published: 8th August 2022