29
Php

Laravel – Model Relationship to Itself

Laravel provides a nice and easy way to build model relationship using some methods like hasOne(), hasMany(), belongsTo() and belongsToMany() and each one of these methods are used for different kinds of relationships, for example, hasOne() used for one to one relationship and hasMany() for one to many and so on. These methods let use build a relationship among models. Model relationship is useful for retrieving or saving/updating related models (database records) from or to the database. These relationships allow us to save or select related data to/from database very easily without writing complex queries manually, instead, Laravel’s powerful Eloquent ORM does the job for us using those pre-defined relationship. For example, if we have a table Posts and another table comment for storing comments for each posts using the primary key of posts table in the comments table as a foreign key then it may look something like this

// posts table
id | post_title | post_slug | post_content
// comments table
id | post_id | user_id | comment_text

So, comments for each post could be found using the post_id foreign key in the comments table and we can retrieve related comments for a post by joining the two tables using id field from post table and the post_id field from comments table. But, by the help of Eloquent ORM we can easily build this relationship between these tables and can easily retrieve comments for any post from the comments table without writing a single query, just like this

$post = Post::find(1);
foreach($post->comments as $comment)
 // Here we can loop through all the comments
 // of a particular post (first post here by id 1)
endforeach

To make this working we just need to declare a relationship using appropriate method in the Post Model like:

class Post extends Eloquent {
    public function comments()
    {
        return $this->hasMany('Comment');
    }
}

That’s all we need to do to build the relationship between posts and comments table, it’s a very simple task in Laravel.

Well, I’m not going to discuss on how to make a relationship for all the relation types and to read more about model relationships visit Laravel website. Instead, I’m going to share an experience of mine where I needed to build a relation to a model itself using it’s own primary key and another field as it’s foreign key.

The situation I had:

I had a posts table and a comments table. Each posts could contain multiple comments and each comment can contain one or more child comments as replies. To achieve this, I made the comment table like this

// comments Table
id (PK) | status | content | post_id | user_id | comment_parent

So post_id is the primary key of posts table as id field in the posts table which was an auto incremented field. So, comments were related with each post using id (PK) from posts table and post_id from comments table as foreign key.

To build the relationship between parent comment and child comments I used comment_parent field in the comments table and stored the comment table’s id (PK) in this field if it was a child of that comment and zero by default for all comments for those which are not a child.

The relationship I used for post and comment:

// Post model
public function nestedComments()
{
    return $this->hasMany('Comment')->where('comment_parent', 0);
}

The relationship for parent and child comments:

// Comment model
public function replies()
{
    return $this->hasMany('Comment', 'comment_parent');
}

This replies() method used to select all the comment replies of a parent comment and I used it in the view like

@foreach($comments as $comment)
    {{-- comment text --}}
    @if($comment->replies->count())
        @foreach($comment->replies as $reply)
            {{-- comment reply text --}}
        @endforeach
    @endif
@endforeach

Here in the replies() method I’ve used hasMany('Comment', 'comment_parent'), this built the relationship to the comment table itself using comment_parent as a foreign key where primary key was the id of this same comment table. This was a solution to my nested commenting system and it was only for a test and it worked. This is a screenshot of the nested comment view:
nested_comment

This was the query to load the post with comments:
// PostRepository.php
public function showPostBySlug($post_slug)
{
    $post = $this->post->with(array('nestedComments' => function($q){
        $q->where('comments.status', 1)->orderBy('comments.id', 'desc');
    }))->where('slug', $post)->first();
    // Load and show the view
    return View::make('post.single')->with('post', $post_slug);
}

The $post variable in where('slug', $post) is the post_slug. I’ve selected post by slug with all the parent comments and loaded replies on per comment loop.

Update:
This requires a recursive function and I’ve used one in the view, the loop is done inside that function but I din’t post it here to keep the article small and this article is not about how to implement the thing, instead it’s the idea that I used and shared here. Thanks!

Update (19-04-2014):
This is the recursive helper function that I created to loop all the comments and stored in a helper file:

// app/start/custom_helpers.php (Included in global.php)
function dumpComments($comments, $view = 'comment.comment_partial.comment')
{
    $commentList = '';
    foreach ($comments as $comment) {
        $commentList .= View::make($view)->with('comment', $comment);
    }
    return $commentList;
}

This is how I called the dumpComments function:

// views/comment/index.blade.php
@if($post->comments->count())

    {{ dumpComments($post->nestedComments) }}
@endif

This is the partial comment template:

// views/comment/comment_partial/comment.blade.php
@if($comment->comment_parent)
  
@else
  • @endif

    {{ $comment->author_name }}

    {{ $comment->content }}

    Reply

    @if($comment->replies->count()) {{ dumpComments($comment->replies) }} @endif
    @if($comment->comment_parent)
  • @else @endif

    Note: Too much nesting of comments may cause stackoverfow/maximum level of recursion error.

    Latest Blog

    0
    Php

    PHP – 8.0 Match Expression

    In PHP 8.0 there is a new feature or I should say a new language construct (keyword) going to be introduced,  which has been implemented depending […]

    0
    Php

    New Union Type in PHP – 8

    A new RFC has been finished voting and accepted for PHP – 8. That is “Union Types”, which is an extension of PHP’s type system. […]