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
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);
}
}
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
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;
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);
}
}
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
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;
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);
}
}
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);
}
}
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
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);
}
}
Usgae:
$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:
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.
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:
$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:
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
.
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:
$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:
phones;
id - integer;
price - float;
model - string;
laptops;
id - integer;
price - float;
model - string;
reviews;
id - integer;
reviewable_id - integer;
reviewable_type - string;
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:
$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.
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.
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');
}
}
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.
Top comments (2)
even clearer then the official docs. thank you
Nice tutorial