DEV Community

Karleb
Karleb

Posted on

Eloquent Relationship In Laravel

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:

users
id - integer
name - string

profile
id - integer
user_id - integer
username - string
Enter fullscreen mode Exit fullscreen mode

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.


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);
    }
}
Enter fullscreen mode Exit fullscreen mode

The relationship can be used like so:

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

$profile = Profile::first();
$profile->user->id;
//returns the user id through the profile
Enter fullscreen mode Exit fullscreen mode

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:

categories;
id - integer;
name - string;

posts;
id - integer;
category_id - integer;
name - string;
Enter fullscreen mode Exit fullscreen mode

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.


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);
    }
}
Enter fullscreen mode Exit fullscreen mode

The implemeted models can be used this way:


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

$post = Post::first();
$post->category->name;
//returns a category name through a post
Enter fullscreen mode Exit fullscreen mode

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:

students;
id - integer;
name - string;

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

teachers;
id - integer;
name - string;
student_id - integer;
Enter fullscreen mode Exit fullscreen mode

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


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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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:


$teacher = Teacher::first();
$teacher->parent
Enter fullscreen mode Exit fullscreen mode

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.

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Usgae:


$teacher = Teacher::first();
dump($teacher->parents()->get());
Enter fullscreen mode Exit fullscreen mode

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:

students;
id - integer;
name - string;

courses;
id - integer;
name - string;

course_student;
course_id - integer;
student_id - integer;
Enter fullscreen mode Exit fullscreen mode

Both models define their relationship using the belongsToMany class method.


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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage:


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

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

Enter fullscreen mode Exit fullscreen mode

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:

posts;
id - integer;
title - string;

profiles;
id - integer;
picture - string;

likes;
id - integer;
likeable_id - integer;
likeable_type - string;
Enter fullscreen mode Exit fullscreen mode

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.


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');
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage:


$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
Enter fullscreen mode Exit fullscreen mode

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:

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

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

reviews;
id - integer;
reviewable_id - integer;
reviewable_type - string;
Enter fullscreen mode Exit fullscreen mode

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');
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage:


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

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

Enter fullscreen mode Exit fullscreen mode

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.

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
Enter fullscreen mode Exit fullscreen mode

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.

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');
    }
}
Enter fullscreen mode Exit fullscreen mode
    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())
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)