Question

I'm trying to get the related posts of a post, based on its tags.

My post (id 1) has several tags attached to it, i.e. tag (id 1-3) this means that my post has 3 tags. Based on those tags, I'd like to show other posts that have this tag(s).

My Tag Model:

<?php namespace Digitus\Base\Model;

class Tag extends \Eloquent{

    protected $table = 'tags';
    protected $guarded = ['id'];
    protected $fillable = ['name'];

    public function posts()
    {
        $this->belongsToMany('Digitus\Base\Model\Post');
    }

}

My post model:

    <?php namespace Digitus\Base\Model;

class Post extends \Eloquent {

    protected $fillable = array('title','body', 'author','slug');

    public function user()
    {
        return $this->belongsTo('Digitus\Base\Model\User', 'author');
    }

    public function tags()
    {
        return $this->belongsToMany('Digitus\Base\Model\Tag');
    }

    public function comments()
    {
        return $this->belongsToMany('Digitus\Base\Model\Comment');
    }

    public function categories()
    {
        return $this->belongsToMany('Digitus\Base\Model\Categorie');
    }
}

In my view I currently have:

<div class="col-xs-8 col-sm-8 col-md-8 col-lg-8 panel panel-info">
    <p>Gerelateerde berichten:</p>

            <?php 
            $posts = Digitus\Base\Model\Post::all();
            $tags = Digitus\Base\Model\Tag::all(); ?>
            @foreach($posts as $post)   
                @foreach($post->tags as $tag)
                    <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">{{ $post->title }}</div>
                @endforeach
            @endforeach


</div>

But it doesn't really show the posts which have the same tags, it just throws all my posts out. Also, I tried adding some sort of filter to it, so there won't be any duplicate posts (same post show up 2+ times) but this also failed at my end.

I'm not looking for direct answers, more for directions and suggestions etc.

Was it helpful?

Solution

This will do, but mind there will be run 3 db queries, so maybe you'd like some joins instead

<?php 
// don't do this in the view, better in controller, service or whatever and just pass $posts to the view
$posts = Digitus\Base\Model\Post::with('tags.posts')->get();
?>
@foreach($posts as $parentPost)
    @foreach($parentPost->tags as $tag)
        @foreach($tag->posts as $post)
            <?php if($post->id == $parentPost->id) continue; ?>
            <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">{{ $post->title }}</div>
        @endforeach
    @endforeach
@endforeach

----- edit: In your Tag model you don't return nothing from relation, so just add return:

public function posts()
{
   return $this->belongsToMany('Digitus\Base\Model\Post');
}

OTHER TIPS

In this code:

@foreach($posts as $post)   
     @foreach($post->tags as $tag)
         <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">{{ $post->title }}</div>
     @endforeach
@endforeach

you are iterating over all tags of all posts, and outputting the post title.

So for a post that has 3 tags, you will output the post title 3 times.

What you might do is this:

<?php $posts = Digitus\Base\Model\Post::with('Tags.Posts')->get(); ?>
@foreach ($posts as $post)
    @foreach ($post->tags as $tag)
        @foreach ($tag->posts as $relatedPost)
            <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">{{ $relatedPost->title }}</div>
        @endforeach
    @endforeach
@endforeach

Not tested, but as far as I understand how Eloquent eager loading works, it should work.

EDIT:

The above would output all posts actually, so if you want the related posts of a single post, this is the way to go:

The following should output an array with a single post in it:

<?php $posts = Digitus\Base\Model\Post::with('tags.posts')->where('id', $id)->get(); ?>

which you then 'iterate' over:

@foreach ($posts as $post)
    @foreach ($post->tags as $tag)
        @foreach ($tag->posts as $relatedPost)
            <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">{{ $relatedPost->title }}</div>
        @endforeach
    @endforeach
@endforeach
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top