Laravel relationship to show all posts by authors that they follow - php

Can normally figure these relationships out but stumped on this one.
I have a basic blog platform that allows user to sign up and post blog posts.
Users can also follow other users.
I am trying to make a view where I can see all of the blog posts by the authors that I follow
I have the normal users table, and then a blog posts table which contains the user_id of the creator.
For followers, I then have a pivot table called followers which has user_id and follower_id.
This is my basic line to get all posts:
$posts = Post::orderBy('created_at', 'DESC')->get();
How can I change that so it only shows the posts where there post user_id is a field that matches in the followers table?
Thanks.

You may try something like this:
App\User Model:
public function favoriteAuthors()
{
return $this->belongsToMany(
User::class, // as Author
'followers', // pivot table
'follower_id', // as follower_id
'user_id', // as author_id
);
}
public function posts()
{
return $this
->hasMany(Post::class, 'user_id', 'id')
->orderBy('created_at', 'DESC');
}
Then load all posts of your favorite authors:
$posts = User::with('favoriteAuthors.posts')->find(auth()->user()->id);
Also you can use something like this:
if($me = auth()->user()) {
$me->load(['favoriteAuthors.posts']);
}
Now the $me will contain users that you follw and each users will contain their posts.

Related

Laravel hasManyThrough manipulates user_id

I got problems with the hasManyThrough function in Laravel.
My goal is to get a feed where the posts show up by users that the logged in user follows. I try to achieve this by:
In User.php, get the ID of the logged in user
Match the user's id with the columns called user_id in Follow.php
Get the target_id from the rows in Follow.php
Get the posts where user_id matches the target_ids from Follow.php
I retrieve the data correctly by who the user follows. However, the user_id that gets returned in the data is the logged in user and not the author of the post. Why?
User.php
public function feed() {
return $this->hasManyThrough(
'App\Post',
'App\Follow',
'user_id',
'user_id',
'id',
'target_id'
)
->with('user')
->orderBy('id', 'DESC');
}
Post.php
public function user() {
return $this->belongsTo('App\User');
}
Called from controller via
$posts = Auth::user()->feed;
Your code creates this query: select `posts`.*, `follows`.`user_id`...
As you can see, posts.user_id gets overwritten by follows.user_id (follows.user_id is necessary for eager loading).
I see two possible solutions for your problem:
Rename follows.user_id.
Use two separate relationships: User → HasMany → Follow → HasMany → Post

Laravel Relationship through sub table

I'm trying to implement a blog type website where the user can rate the posts. So the relationship is like this:
Blog -> blog_links; To Many fk = blog_id
User -> blog_links; To Many fk = user_id
I've tried these solutions and each time get a different error:
User::hasManyThrough(Blog::class, BlogLink::class);
That didn't work, so I tried query scope:
scopeWithLinksAndResorts($query) {
return $query->with(['resorts' => function($q) {
$q->ordered()->withLinks();
}]);
}
That doesn't seem to return any results. So I'm not sure what I'm doing wrong here. And here's a table example:
blogs
id Title Text
1 Test This is a test blog
users
id email
1 test#test.com
blog_links
id blog_id user_id rating
1 1 1 4
Now from this I want to get the blogs the user has rated and their rating. But as I said I get no results. Anyone able to help?
In your User model define the blog's relation
/**
*
*/
public function blogs()
{
return $this->belongsToMany('App\Blog', 'blog_links', 'user_id', 'blog_id')->withPivot('rating');
}
Then in your Blog model define the user's relation
/**
*
*/
public function users()
{
return $this->belongsToMany('App\User', 'blog_links','blog_id', 'user_id');
}
To get all users with their blogs and rating you can do \App\User::with('blogs')->get()
I'm not sure if this is what you want but hope it helps.

Laravel Eloquent retrieve data from multiple tables

I have four tables
**Articles table**
id
title
body
owner_id
category_id
**Favorite articles table**
id
user_id
article_id
**User table**
id
user_name
user_type
**Category table**
id
category_name
How to get list of favorite articles (article_name,owner_name,category_name) which related to currently logged user from db using laravel eloquent?
Is it possible to do it in single line request? e.g.:
$articles_data=Auth::user()->favorite_articles->article...
EDIT
For the moment i have to use statement below:
$articles_data = FavoriteArticle::where('user_id', Auth::id())->join('articles', 'articles.id', '=', 'favorite_articles.article.id')
->join('users', 'users.id', '=', 'favorite_articles.user_id')
->join('categories', 'categories.id', '=', 'articles.id')
->get()
Which looks a bit complicated and doesn't use eloquent relations.
Completing #zippo_ answer, in the Controller you must reference what tables you want, e.g.
User.php
use Article;
public function article()
{
return $this->hasOne('App\Article');
}
and in the e.g. UserController.php
$user = User::with('article')->get();
EDIT:
if you want to relate User to Article.Category, after create a relation with user and article
Article.php
use Category;
public function category()
{
return $this->hasOne('App\Category');
}
e.g. UserController.php
$user_articles_categories = User::with('article.category')->get();
You can take advantage of laravel eager loading, which are also called as Eloquent relationships.
Eloquent relationships are defined as functions on your Eloquent model classes.
Eg. In Article Model
public function article()
{
return $this->hasOne('App\Model\Category');
}
In this way, you need to define all the relationships in the respective Model classes.
for more info: http://laravel.com/docs/5.1/eloquent-relationships

Laravel Eloquent Join vs Inner Join?

So I am having some trouble figuring out how to do a feed style mysql call, and I don't know if its an eloquent issue or a mysql issue. I am sure it is possible in both and I am just in need of some help.
So I have a user and they go to their feed page, on this page it shows stuff from their friends (friends votes, friends comments, friends status updates). So say I have tom, tim and taylor as my friends and I need to get all of their votes, comments, status updates. How do I go about this? I have a list of all the friends by Id number, and I have tables for each of the events (votes, comments, status updates) that have the Id stored in it to link back to the user. So how can I get all of that information at once so that I can display it in a feed in the form of.
Tim commented "Cool"
Taylor Said "Woot first status update~!"
Taylor Voted on "Best competition ever"
Edit #damiani
So after doing the model changes I have code like this, and it does return the correct rows
$friends_votes = $user->friends()->join('votes', 'votes.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['votes.*']);
$friends_comments = $user->friends()->join('comments', 'comments.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['comments.*']);
$friends_status = $user->friends()->join('status', 'status.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['status.*']);
But I would like them all to happen at once, this is because mysql sorting thousands of records in order is 100x faster then php taking 3 lists, merging them and then doing it. Any ideas?
I'm sure there are other ways to accomplish this, but one solution would be to use join through the Query Builder.
If you have tables set up something like this:
users
id
...
friends
id
user_id
friend_id
...
votes, comments and status_updates (3 tables)
id
user_id
....
In your User model:
class User extends Eloquent {
public function friends()
{
return $this->hasMany('Friend');
}
}
In your Friend model:
class Friend extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
}
Then, to gather all the votes for the friends of the user with the id of 1, you could run this query:
$user = User::find(1);
$friends_votes = $user->friends()
->with('user') // bring along details of the friend
->join('votes', 'votes.user_id', '=', 'friends.friend_id')
->get(['votes.*']); // exclude extra details from friends table
Run the same join for the comments and status_updates tables. If you would like votes, comments, and status_updates to be in one chronological list, you can merge the resulting three collections into one and then sort the merged collection.
Edit
To get votes, comments, and status updates in one query, you could build up each query and then union the results. Unfortunately, this doesn't seem to work if we use the Eloquent hasMany relationship (see comments for this question for a discussion of that problem) so we have to modify to queries to use where instead:
$friends_votes =
DB::table('friends')->where('friends.user_id','1')
->join('votes', 'votes.user_id', '=', 'friends.friend_id');
$friends_comments =
DB::table('friends')->where('friends.user_id','1')
->join('comments', 'comments.user_id', '=', 'friends.friend_id');
$friends_status_updates =
DB::table('status_updates')->where('status_updates.user_id','1')
->join('friends', 'status_updates.user_id', '=', 'friends.friend_id');
$friends_events =
$friends_votes
->union($friends_comments)
->union($friends_status_updates)
->get();
At this point, though, our query is getting a bit hairy, so a polymorphic relationship with and an extra table (like DefiniteIntegral suggests below) might be a better idea.
Probably not what you want to hear, but a "feeds" table would be a great middleman for this sort of transaction, giving you a denormalized way of pivoting to all these data with a polymorphic relationship.
You could build it like this:
<?php
Schema::create('feeds', function($table) {
$table->increments('id');
$table->timestamps();
$table->unsignedInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->morphs('target');
});
Build the feed model like so:
<?php
class Feed extends Eloquent
{
protected $fillable = ['user_id', 'target_type', 'target_id'];
public function user()
{
return $this->belongsTo('User');
}
public function target()
{
return $this->morphTo();
}
}
Then keep it up to date with something like:
<?php
Vote::created(function(Vote $vote) {
$target_type = 'Vote';
$target_id = $vote->id;
$user_id = $vote->user_id;
Feed::create(compact('target_type', 'target_id', 'user_id'));
});
You could make the above much more generic/robust—this is just for demonstration purposes.
At this point, your feed items are really easy to retrieve all at once:
<?php
Feed::whereIn('user_id', $my_friend_ids)
->with('user', 'target')
->orderBy('created_at', 'desc')
->get();

Laravel eager loading - relationship query

Say I have this 3 tables Blog, Post, Comment which has corresponding models Blog, Post, Comment.
No here is the relation between them:
Blog has many Post, posts()
Post belongs to Blog, blog()
Post has many Comment, comments()
Comment belongs to Post post()
Now I want to execute some query like this:
Blog::with(array('posts.comments' => function($q)
{
//query Post columns
})->find(1);
As I know the $q is corresponding to the Comment table. Is there any way to query the Post table?
Query nested relation like this:
$blog = Blog::with(['posts' => function ($q) {
$q->where('column','value'); // query posts table
}, 'posts.comments' => function ($q) {
$q->where('commentsColumn','anotherValue'); // query comments table
}])->find(1);
Eloquent will load posts accordingly, only then will it fetch the comments for those posts.

Categories