Eloquent Relationship In Laravel
Published: 2023-04-11 13:37:38

Eloquent relationship provides a fluent way by which we define relationship between databases and their respective models in Laravel. It is an important feature of the framework and one of its selling point, it is important to be farmilair with Eloquent relationship as it is inevitable when building application that interact with databses. So we will be looking at important eloquent model relationships in laravel.

# Prerequisite

* Basic knowledge of PHP
    
* basic Knowledge of Laravel
    

# One To One

This type of model-model relationship is used when one instance of a model can be related to only one instance of another model.

An example is the relatonship between a user and a profile. A user can only have a single profile and a profile can belong to a single user only.

The table structure for these models will look like this:

```js
users
id - integer
name - string

profile
id - integer
user_id - integer
username - string
```

In the user model, the relationship is defined by createing a method named after the model the relationship is to be designated to, in this case, `profile` and the function definition returns a `hasOne` through the current class. The `hasOne` method takes the profile model through its class, `Profle::class`.

In the profile model, profile model can access the user model by creating a user method and returning a `belongsTo` method that accepts the user class, `User::class`. Note that the inverse relationship is not compulsory for the relationship to work, but it helps us to retrieve a user from a profile and a profle from a user. Without the inverse relationship, we can not get a user from a profile but we can get a profile from a user.

```php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Get the profile associated with the user.
     */
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

class Profile extends Model
{
    /**
     * Get the user associated with the profile.
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
```

The relationship can be used like so:

```php
$user = User::first();
$user->profile;
//returns the profile through a user

$profile = Profile::first();
$profile->user->id;
//returns the user id through the profile
```

# One To Many

In one-to-many relationship, a single model can be related to one or more instances of another model. In a post and category model relationship, a category can have one or more posts but a post can only belong to a category.

The table structure for these models will look like this:

```js
categories;
id - integer;
name - string;

posts;
id - integer;
category_id - integer;
name - string;
```

To create this relationship, a posts method which returns a `hasMany` class method that accepts the `Post::class` is created in a the category model. The inverse of `hasMany` is `belongsTo`, same as in one-to-one relationship.

It is important to follow the naming conventions. Notice how we named the method in inverse relationship `category` and not `categories`, this is because one post can only have one category.

```php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    /**
     * Get the posts associated with the category.
     */
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

class Post extends Model
{
    /**
     * Get the category associated with the post.
     */
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}
```

The implemeted models can be used this way:

```php

$category = Category::first();
$category->posts;
//returns the posts through a category

$post = Post::first();
$post->category->name;
//returns a category name through a post
```

# Has One Through

One has through relationship is used to express a one to one relationship indirectly between two models through a third model.

For Instance, a `Teacher` model can access a `Parent` model throgh a `Student` model even though the `Teacher` model is not directly related to the `Parent` model.

The table structure for these models will look like this:

```js
students;
id - integer;
name - string;

parents;
id - integer;
student_id - integer;
name - string;

teachers;
id - integer;
name - string;
student_id - integer;
```

Let's create a one-to-one relationship between the `Parent` and `Student` model.

```php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Parent extends Model
{
    /**
     * Get the student associated with the parent.
     */
    public function student()
    {
        return $this->hasOne(Student::class);
    }
}

class Student extends Model
{
    /**
     * Get the parent associated with the student.
     */
    public function parent()
    {
        return $this->belongsTo(Parent::class);
    }
}
```

Now we create the has-through relationship between a `Teacher` and a `Student` using the `hasOneThrough` class method that accepts to classes. First is the class of the model we want to create a relationship with, the other is the model through which we are creating the relationship.

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Teacher extends Model
{
    /**
     * Get the parent associated with the teacher through the student.
     */
    public function parent()
    {
        return $this->hasOneThrough(Parent::class, Student::class);
    }
}
```

The syntax can be a bit confusing at first, but it basically reads: a `Teacher` model has one `Parent` model through a `Student` model.

The implemeted models can be used this way:

```php

$teacher = Teacher::first();
$teacher->parent
```

# Has Many Through

This model relationship is allows for multiple instances of one model to be accessed by another through an indurect relationship with a third model. It is like the has-one-through model relationship but now with can access multiple instances.

We will be using thesame database structure as has-one-through. The `Parent` and `Student` model will have a typical one-to-many relationship but the has-many-through relationship will be defined in the `Teacher` model with `parents` method that returns `hasManyThrough` method which accpets both the `Parent` and `student` classes.

It reads, get many parents through students.

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Parent extends Model
{
    /**
     * Get the students associated with the parent.
     */
    public function students()
    {
        return $this->hasMany(Student::class);
    }
}

class Student extends Model
{
    /**
     * Get the parent associated with the student.
     */
    public function parent()
    {
        return $this->belongsTo(Parent::class);
    }
}

class Teacher extends Model
{
    /**
     * Get the parents associated with the teacher's students.
     */
    public function parents()
    {
        return $this->hasManyThrough(Parent::class, Student::class);
    }
}
```

Usgae:

```php

$teacher = Teacher::first();
dump($teacher->parents()->get());
```

# Many To Many

Many to many as the name implies describes a type of model relationship in which the one or more instances of a model can be related to one or more instances of another model.

In a school management platform, one or more students can register for several courses and one or more courses can be studied by several students. Each database table do not take a foriegn key of its relative model, rather a third pivot database table is created to store the ids of both models.

The table structure will look like this:

```js
students;
id - integer;
name - string;

courses;
id - integer;
name - string;

course_student;
course_id - integer;
student_id - integer;
```

Both models define their relationship using the `belongsToMany` class method.

```php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Student extends Model
{
    /**
     * The courses that belong to the student.
     */
    public function courses()
    {
        return $this->belongsToMany(Course::class);
    }
}

class Course extends Model
{
    /**
     * The students that belong to the course.
     */
    public function students()
    {
        return $this->belongsToMany(Student::class);
    }
}
```

Usage:

```php

$student = Student::first();
dump($student->courses()->get());

$course = Course::first();
dump($course->students()->get());

```

# What are Polymorphic Realtionships?

Laravel docs define polymorphic relationship as a relationship that allows the child model to belong to more than one type of model using a single association. But what does that really means?

Imagine you have a `Post`, `Profile`, `Comment` and `Page` models and all these can be liked. One might think to store the likes, we would create a separate `Like` model for all these models, for instance, `PostLike`, `ProfileLike`, `CommentLike` and `PageLike` respectively. But this isn't only inefficient but also too much work and creates too many databases and models. imagine we need to like maybe a video or something else.

This is where polymorphic relationships come to the rescue, they allow you to put all the likes in one `likes` database and of course a single `Like` model is used. The child model, `Like` will contain a `likeable_id` and `likeable_type` in the database, these two columns store the `id` and class names of the parent models, with this two columns, we can differntiate between every parent models.

# One To One Polymorphic Relationship

This polymorphic relationship is the simplest type. The child model `Like` belongs to a single instance of both `Post` and `Profile` model. That is, even though parent models could be more than these two, the a post or profile can not be liked more than once.

The table structure will look like this:

```js
posts;
id - integer;
title - string;

profiles;
id - integer;
picture - string;

likes;
id - integer;
likeable_id - integer;
likeable_type - string;
```

In the child model, the polymorphic relationship is defined by returning a `morphTo` that does not take any parameter optionally from the class instance in a `likeable` method. In the inverse, the parents return a `morphOne` that accepts the `Like::class` and the `likeable`.

The convection is: the child model adds an `able` to its model named for the relationship defintion. If the child model were an `image` or `email`, the the method name would be `imageable` or `emaiable`.

```php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Like extends Model
{
    /**
     * Get the parent likeable model (post or profile).
     */
    public function likeable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    /**
     * Get the post's like.
     */
    public function like()
    {
        return $this->morphOne(Like::class, 'likeable');
    }
}

class Profile extends Model
{
    /**
     * Get the profile's like.
     */
    public function like()
    {
        return $this->morphOne(Like::class, 'likeable');
    }
}
```

Usage:

```php

$like = Like::first();

$like = $like->likeable;
//returns the parent, either a post or a profile

$post = Post::first();
$post->like

$profile = Profile::first();
$profile->like
```

# One To Many Relationship

Similar to the typical one to many relationship, this polymorphic relationship defines a relationship where a child model can have one or more instances of its parents' model.

For example, `Review` model can have a polymorphic relationship with `Phone` and `Laptop` models.

The table structure will look like this:

```js
phones;
id - integer;
price - float;
model - string;

laptops;
id - integer;
price - float;
model - string;

reviews;
id - integer;
reviewable_id - integer;
reviewable_type - string;
```

```php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Review extends Model
{
    /**
     * Get the parent reviewable model (laptop or phone).
     */
    public function reviewable()
    {
        return $this->morphTo();
    }
}

class Laptop extends Model
{
    /**
     * Get all of the laptop's reviews.
     */
    public function reviews()
    {
        return $this->morphMany(Review::class, 'reviewable');
    }
}

class Phone extends Model
{
    /**
     * Get all of the phone's reviews.
     */
    public function reviews()
    {
        return $this->morphMany(Review::class, 'reviewable');
    }
}
```


Usage:

```php

$laptop = Laptop::find(1);
$reviews = $laptop->reviews;

$phone = Phone::find(2);
$reviews = $phone->reviews;

```


# Many To Many Relationship

This is also similar to typical many-to-many-relationship but in this case, the relationship is polymorphic. Many child model can belong to many parent model and vice versa.

A fourth table is created in this relationship to store the id and class of the parent model.

```js
posts
id - integer
title - string
content - text

videos
id - integer
title - string
url - string

tags
id - intger
name - string

taggables
tag_id - intger
taggable_id - integer
taggable_type - string
```

The two parents model here, `Video` and `Post` return `tags` method that return a `morphToMany` on its class which accepts the child model class, `Tag::class` and `taggable`. In the child model, it returns two methods, `posts` and `videos` relating to its two parents and these two methods return a `morphByMany` methods on the class which accepts the respective class referenced by the method name and a second `taggable`.

Note that there is no method names `taggable`, rather, the taggable refers to the fourth table here. Also, notice its is `morphToMany` and not `morphMany` in the parent models.

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function tags()
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

class Video extends Model
{
    public function tags()
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

class Tag extends Model
{
    public function posts()
    {
        return $this->morphedByMany(Post::class, 'taggable');
    }

    public function videos()
    {
        return $this->morphedByMany(Video::class, 'taggable');
    }
}
```

```php
    use App\Models\{Post, Tag, Video}

    Post::find(2)->tags()->saveMany([Tag::find(1), Tag::find(2), Tag::find(3)]);
    
    Tag::find(5)->posts()->saveMany([Post::find(1), Post::find(2), Post::find(3)]);

    Video::find(2)->tags()->saveMany([Tag::find(1), Tag::find(2), Tag::find(3)]);

    Tag::find(5)->videos()->saveMany([Video::find(1), Video::find(2), Video::find(3)]);

    dump(Post::find(2)->tags()->get())
    dump(Tag::find(5)->posts()->get())
    dump(Video::find(2)->tags()->get())
```

# Conclusion
Building and managing relationships between database tables is an essential aspect of any Laravel application. Laravel's Eloquent ORM makes it easy to define and work with relationships between models, whether it's a one-to-one, one-to-many, or many-to-many relationship. By using the appropriate methods and conventions provided by Laravel, developers can quickly create and maintain complex relationships between their data.

In this article, we explored the different types of relationships that can exist between models in Laravel, including how to define and use them effectively. We saw that Laravel provides a variety of tools to simplify the process of working with relationships.

Understanding how to model relationships in Laravel is an important skill for any developer working with the framework. By following the best practices outlined in this article, developers can create robust and efficient applications that make use of the full power of Laravel's ORM.