As we learned about Laravel Eloquent ORM in our previous tutorial, now we will learn about Laravel Eloquent Relationships. With Laravel Eloquent relationship, managing the database becomes incredibly easy.

There can be many situations where you split data across multiple tables for ease of management or by following the normalizations on the database because each table would represent an individual entity. For example, posts and tags. A post can have multiple tags and a tag can be assigned to multiple one or many posts. So there is an existence of the many-to-many relationship between posts and tags tables. Similarly, for the one-to-one relationship, there can be an example of users and profiles, a user can have only one profile and a profile can be related to only one user. The implementation of these relationships among tables in the database can reduce redundancy and some database related anomalies like updating anomaly and deletion anomaly.

Laravel Eloquent ORM has built-in relationships that allow us to not only manipulate one table in the database but also we can manipulate all related data.

There are following types of relationships:-

One-to-one Relationship

As in above explanation, in users and profiles table, there exists a relationship called “one-to-one” relationship. So a user can have at most one profile and a profile can be related to only a single user. To implement this relationship, we have to define a foreign key in one of the tables. Here, the profiles table is dependent on users table, that means we have to create a foreign key attribute user_id in profiles table which will represent the user it belongs to by referencing user id in the users table.

The User model will look like:

class User extends Model {
    public function profile()
    {
        return $this->hasOne('App\Profile');
    }
}

 

The reverse of above relationship is going to reflect in Profile model. So the Profile model will look like:

class Profile extends Model {
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

 

Now we can query the user to access the profile easily.

$profile = User::find(1)->profile; // profile of user

// you can do the inverse also
$user = Profile::find(1)->user; // user belongs to the profile

 

When you access the relationship by name of the method like property, laravel will understand the need and will return the eloquent object of relation.

One-to-many Relationship

This relationship can be thinking of a user having multiple addresses. A user can have one or more address and an address will only belong to a single user. Laravel gives this functionality by using hasMany() method.

User class will look like:

class User extends Model {
    public function addresses()
    {
        return $this->hasMany('App\Address');
    }
}

 

And Address class will look like:

class Address extends Model {
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

 

Now to retrieve all addresses of user, we will do the following.

// collection of objects of addresses which belongs to the user
$address = User::find(1)->addresses;

// The inverse is also possible here.
$user = Address::find(1)->user;

 

Many-to-many Relationship

Many to many relationships is more complicated to the above two relationships. Here instead of two tables, a third table is involved as pivot table which will hold the information of the relationship.

Consider a scenario where you want to implement the relationships between posts and tags table. A post can have zero or many tags and same will be the inverse, i.e. a tag can have zero or many posts assigned to it. So there exists many-to-many relationship. To map this relationship information, you have to use a third table called join table or pivot table.

By default, Eloquent expects pivot tables to contain the singular names of the two tables, listed alphabetically and separated by an underscore. So in our scenario, this will be post_tag. The table itself contains only two columns (other than the primary key). These columns represent the foreign key of the Post model and the Tag model it is creating a relation between. In the convention, these should be lowercase, singular, with  _id appended, that is, post_id and tag_id.

Both the models, Post and Tag will be using the “belongsToMany()” methods.

// Post Model
class Post extends Model {
    public function tags()
    {
        return $this->belongsToMany('App\Tag');
    }
}

// Tag Model
class Tag extends Model {
    public function posts()
    {
        return $this->belongsToMany('App\Post');
    }
}

//We can now find out what tags a post has:
$tags = Post::find(1)->tags;

//We can also find out all posts with a particular tag:
$posts = Tag::find(1)->posts;

// If you need to add a new tag to a post, you can do so by using the  attach method:
$post = Post::find(1);
$post->tags()->attach($tagId);

// And, of course, the opposite of  attach is  detach :
$post->tags()->detach($tagId);

 

Both the attach and detach methods also accept arrays, allowing you to add/remove multiple relations in one operation.

Alternatively, you can use the sync method. The difference with sync is, only after the operation is complete are the IDs that are passed present in the join table, rather than adding/removing them from the existing relations.

$post->tags()->sync(1, 2, 3, 4, 5);

 

Has-many-through

This is a bit advanced type of relationship. Consider a simple e-commerce website. You may have a Product model, an Order model, and an OrderItem model that belongs to both a product and an order. Now you have to find all orders that contain a particular product. How do you do this if Product isn't directly associated with Order? Thankfully, in our scenario, we have a common relation—the OrderItem model.

We can use a "has-many-through" relationship to reach orders a product is a part of via the intermediate OrderItem model. We set the relationship up in our Product model, as follows:

class Product extends Model {
    public function orders()
    {
        return $this->hasManyThrough('App\Order', 'App\OrderItem');
    }
}

 

The first parameter in the hasManyThrough method is the target model, and the second parameter is the intermediate model we go through to get to it. We can now easily list the orders a product is a part of:

$product = Product::find(1);
$orders = $product->orders;

 

Polymorphic relations

A common use case for a polymorphic relationship is to create a comment system and then allow your other models to contain comments by linking to the relevant records in the comments table. A base Comment model will look like this:

class Comment extends Model {
    public function commentable()
    {
        return $this->morphTo();
    }
}

 

The morphTo method makes this model polymorphic. Now, in our other models, we can create a relation to the Comment model, as follows:

class Post extends Model {
    public function comments()
    {
        $this->morphMany('App\Comment', 'commentable');
    }
}

 

You can now fetch any related Comment models through your Post model:

$post = Post::find(1);

foreach ($post->comments as $comment) {
 // show the comments
}

 

You may think that this is no different to a one-to-many relationship, but the difference becomes apparent when you look at the relationship from the other side. When retrieving a Comment instance, if you access the commentable relation, you'll receive an instance of whatever model "owns" the comment. This may be a Post, a Page, a Product, or another model type in your application. Eloquent achieves this by not only storing a foreign key value but also the name of the model class. In the case of our comment model, the columns would be commentable_id and commentable_type. When creating your migration, you can use this method to create these two columns:

$table->morphs('commentable');

 

Many To Many Polymorphic Relationships

In polymorphic relations, you may also specify many-to-many polymorphic relations easily. let’s have an example, a blog Post, Audio and Video model could share a polymorphic relation to an Image model. First, let's examine the table structure:

posts
   id - integer
   name - string

audios
   id - integer
   name - string

videos
   id - integer
   name - string

images
   id - integer
   name - string

imagables
   images_id - integer
   imagable_id - integer
   imagable_type - string

 

Next, we're ready to set up the relationships on the model. The Post, Audio, and Video model will both have a morphToMany relationship via a images method:

class Post extends Model {
    public function images()
    {
        return $this->morphToMany('App\Image', 'imagable');
    }
}

 

The Image model may define a method for each of its relationships:

class Image extends Model {
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'imagable');
    }

    public function audios()
    {
        return $this->morphedByMany('App\Video', 'imagable');
    }

    public function videos()
    {
        return $this->morphedByMany('App\Video', 'imagable');
    }
}

 

Querying into relationships

Consider a situation where you want to get all categories that have at least one post. To do so, you can use the has method:

$categories = Category::has('posts')->get();

 

You can also specify an operator and a count:

$categories = Category::has('posts', '>=', 3)->get();

 

Nested has statements can also be constructed using "dot" notation:

$categories = Category::has('posts.published')->get();

 

You can use the whereHas and orWhereHas methods to put "where" conditions on your has queries:

$categories = Category::whereHas('posts', function($q)
{
    $q->where('title', 'like', 'something%');
})->get();