Laravel 4 Eloquent Model Relationships, Part 1.

The Benefits of Making Your Models “Eloquent”

If you are familiar with MVC and are learning Laravel 4, you may be tempted to simply use models as a place to store your database logic. However, you would be robbing yourself of the many benefits of extending the Eloquent Object-Relational Mapper in your applications.

Among these benefits are route & form model binding – both of which are features that can save you loads of time and spare you from having to write the same boring code that is required in just about every web application.

Enter Laravel Model Relationships

One of the more powerful features of the Eloquent ORM are model relationships. Model relationships, once defined, enable you to fetch & update a model object in your database, as well as any models it is related to, without having to write explicit query logic. Common tasks like fetching blog posts along with all metadata, author data, and comments associated with them become trivial.

As of Laravel 4.0 there were the 3 primary types of relationships:

  • One-to-One
  • One-to-Many
  • Many-to-Many

Laravel 4.1 (released late 2013) brings with it a couple more relationship types:

  • Has-Many-Through
  • Polymorphic One-to-One/One-to-Many

This article will cover the first 3 relationship types available as of Laravel 4.0; one-to-one, one-to-many, & many-to-many.

Look for a future article that will cover Eloquent relationships introduced in Laravel 4.1.

A Simple Blog Example

For the sake of illustration, we will be looking at how these relationships might be used in a real-world web application, such as a blog.

This demo project is a very simple web development blog that has posts, authors, & content tags (i.e. HTML, CSS, Javascript, PHP, MySQL, MongoDB).

Laravel Eloquent Blog Example

The project that we will be using can be found here: https://github.com/dtrenz/laravel-model-demo

To setup the project;

  1. clone the repo
  2. run composer install
  3. create a new database in MySQL
  4. update the db connecton (i.e. host, database, username, password) in app/config/database.php
  5. run migrations, to setup the DB schema: php artisan migrate
  6. seed the DB with sample data: php artisan db:seed

One-to-One

Let’s start with a one-to-one relationship. For the purposes of this demo, I’ve chosen a somewhat contrived example of a one-to-one relationship.

Each blog post has a title and body text, but the body text is stored in a separate table, called “texts.”

Here is the schema for posts and texts:

posts
– id
– title
texts
– id
– text
– post_id

You can see that the texts table has a foreign key of post_id, since each post “owns” one text.

Eloquent needs to know how to map a relationship from a post to text. We define this relationship in each model.

First, we create a model file for each of these models, tell Eloquent which table to find the data for the model, and then define the relationship using a model method:

That was easy, right?

All we had to do was return a hasOne relationship to the Text model and now, anytime you need to access or display the text for a given post, you can simply reference the text attached to the post as if it were a direct property on the post object:

$post->text

But…how?

By returning a hasOne relationship in a method called “text()” Eloquent is able to map that method to the row in the texts table with the current post_id. It then returns that row as an instance of the Text model. This enables you to get the text, like so:

$post->text()->first()

But that’s not all! Eloquent gives you a dynamic property for accessing the text directly, w/o having to use query builder methods like “first().” This enables you to get the text, like so:

$post->text

One-to-Many

Next, let’s try a one-to-many relationship – this is probably the most common relationship type you will use. In our blog we have posts and each post belongs to a single author. In other words, one author can have many posts.

Laravel Blog Example

Let’s take a look at the relevant part of the schema:

users
– id
– name
posts
– id
– title
– author_id (aliased user.id)

There are 2 things you should notice here; (1) we have a users table to store all kinds of users on our website (i.e. authors, commenters, admins), and (2) we have added a new column – author_id – to the posts table, which is the foreign key to the users table.

Now, I could have simply named it user_id, but I felt that was too vague. What kind of post user is this? Is this the author of the post, or a commenter, etc? By naming the column author_id, it makes the relationship much clearer and, fortunately, Eloquent makes this very easy to manage.

Next, let’s create a User model and define a one-to-many relationship to the Post model:

To define a one-to-many relationship, we simply return a hasMany relationship to the Post model and – since we are aliasing the foreign key – we pass the name of the foreign key column we want to use as the second parameter. If you simply wanted to use user_id as the column name, you would not need to pass a second argument.

Now we can access all posts by an author like so:

$author->posts

Since this is a one-to-many relationship, instead of getting back a single Post instance, we get a collection of multiple Post instances. Then, to access/display each instance, we could simply loop through them:

Pretty great, right?

So, now we can get posts for a given author, but when we display a single post page, we also want to be able to display author attribution. So, how do we get the relationship to go the other way, to get the author for a given post?

blog-single-post

The answer is simple: For every model relationship you define, there is a way to define the inverse relation. For any one-to-* relationship, you can use the belongsTo method to create an inverse mapping.

So, let’s add an “author()” method to the Post model that returns a belongsTo relationship.

Now, we can display the author’s name on a post page like so:

Many-to-Many

The last relationship type we will be covering is many-to-many. In the blog we have content tags for tagging posts based on what topics are discussed (i.e. HTML, PHP, MySQL).

A post can have many tags, and a tag may be attributed to many posts, thus we have a many-to-many relationship between posts and tags.

Laravel Blog Example

Let’s take a look at the schema for tags:

tags
– id
– name
post_tag (pivot table)
– post_id
– tag_id
– author_id (aliased user.id)

Just as you would expect, we add a table called tags w/ an auto-incrementing “id” column. But we are establishing a many-to-many relationship, so we cannot simply add a foreign key to either table, since that would limit us to a single owner.

This is why we also need to add a pivot table that maps post_ids to tag_ids. Fortunately, Eloquent expects this in a many-to-many relationship and knows to look for a table named in this format (“model_model”) in order to map properly to the other model.

To define the relationship, just like the previous examples, we simply add a mapping method to each model:

That’s all! Here we simply use the belongsToMany relationship method on both sides of the relationship to be able to map between the models:

It should be noted that – while this pivot table naming convention is the recommended and default convention for an Eloquent many-to-many pivot table – you are welcome to name the pivotal table whatever you like (just like we did with author_id) by passing the name as the second argument of the belongsToMany method.

Working with Laravel Model Relationships

Now that we have defined all of these relationships between our models, what else can we do with them?

We’ve seen how we can access and read the data through these model relationships, but what happens if we want to write data?

Let’s say we are creating a new blog post, what will that entail?

Laravel Blog Example

First, We’ll need to create a new Post & a new Text that will be attached to that Post.

This can be accomplished very easily by using the query builder save() method:

Second, we’ll need to associate an existing User to the Post as the author & associate some existing tag(s) to the post:

Pretty cool, right?

I don’t know about you, but when I think of all of the query logic I didn’t have to write to make all of this happen, I get pretty excited.

The End…?

This article introduces the fundamentals of Eloquent ORM model relationships in Laravel, but there are many more aspects that you should look into, such as eager loading.

As I mentioned at the top, more relationships were added in Laravel 4.1 (i.e. Polymorphic Relations & Has-Many-Through), so expect a follow-up to this article that will cover those.

Published by

Dan Trenz

Dan is a full-stack developer from Ann Arbor, MI.

32 thoughts on “Laravel 4 Eloquent Model Relationships, Part 1.”

    1. If you’re referring to the foreach loop that echos each tag name, there should be 1 query to get the post, and 1 query to get all the tags associated with that post, so 2 total.

      There are cases where the queries can really start to add up, so I would read about Eager Loading to significantly reduce queries: http://laravel.com/docs/eloquent#eager-loading

      If you’re asking about the store/save example, that would be more like 20 queries (ouch). A more optimized approach would be to use findMany to get all the tags at once, then use saveMany to store them all on the post, at once. That should take us down to 2 queries.

        1. Great catch, Ian! For some reason I thought it was more clever than that.

          I guess I would say, if there is a risk that a model operation like this will potentially spawn loads of queries OR if even a handful of queries will be a deal-breaker for the specific user experience, you will really need to construct more optimized/efficient DB logic via query builder methods.

          I don’t want this comment thread to digress into a debate about when performance matters in a web application, but perhaps that would be a good future post.

          1. Agreed :). Just pointing out a pit fall I ran into with the saveMany function acting differently then one would think.

    1. This is a WordPress plugin, but my editor looks like this. The color scheme is Tomorrow Night and Dan and I both personally use Sublime Text.

  1. when i try to display the tags ($post->tags as $tag) i get this:
    ErrorException
    Undefined property: stdClass::$tags …
    any help?

  2. I believe the first example is missing a major detail. In the One-to-One example, the Test model should have the following method:

    public function post() {
    return $this->belongsTo('Post');
    }

    If this is missing, then $post->text will return NULL. It took me a while to figure this out.

  3. I see a lot of interesting articles on your blog.

    You have to spend a lot of time writing, i know how to save you a lot of work, there is a tool that creates unique, SEO friendly posts in couple
    of seconds, just search in google – laranita’s free content source

  4. Hmm it seems like your website ate my first comment (it was
    super long) so I guess I’ll just sum it up what I submitted and say,
    I’m thoroughly enjoying your blog. I too am an aspiring blog blogger but
    I’m still new to the whole thing. Do you have any suggestions for rookie blog writers?
    I’d really appreciate it.

    Feel free to visit my blog – road accident lawyers

  5. 1 Month Hourly Chart of Performance Sports Group is trading at Office Depot is the sole supplier
    of ink to the shop. Sandalio Gómez, top eleven hack a professor who specialises in the world.
    If you log top eleven hack in, or even 2010. In the game, but the new world.

    4% M/M and 2. You never had babies and is similar.
    S companies who are now. Witnesses and a top
    eleven hack dislike of draws – so no cleats are extremely lightweight, and a half or nearly three kilometers.

    Here is my site – Top eleven hack bluestacks

  6. Attractive section of content. I just stumbled upon your website and in accession capital to assert that I get in fact enjoyed account your blog posts. Any way Ill be subscribing to your feeds and even I achievement you access consistently rapidly. gdafbgkegekd

  7. hello I have hard time to understand about change the file here an example @foreach (Tag::all() as $tag)

    name) }}”>{{ $tag->name }}

    @endforeach

    I am not sure why not using {{tag->name}}.blade.php but you decided put show.blade.php. if the tags name is not exist and it still function for show.blade.php it does not making any sense to me. Where does show.blade.php go? till I found out in the routes.php it said Route::get(‘/tag/{name}’, ‘TagController@show’); ahh making sense! thanks for greatest post this!

  8. Great article! but I fell a little incomplete…

    You talk about the process of “save” and I did understand perfectly, but did not read about the process of “update” and “delete”.

    It would be good and I will appreciate if you talk about it.

    Regards

Leave a Reply

Your email address will not be published. Required fields are marked *