Laravel Relationships Conditions - 3 tables - laravel

I've got a situation where I've got Posts, Users and Comments.
Each comment stores a post_id and a user_id. What I want to do is get all of a user's comments on a particular post, so that I can do a call like this:
$comments = Auth::User()->comments(post_id=x)->text
(where I know what x is)
I have:
User->HasMany(comments)
Comments->HasOne(User)
Comments->HasOne(Project)
Project->HasMany(comments)
I feel like there needs to be a where or a has or a wherehas or something thrown in.. the best I can manage is that I pull Auth::User()->comments into an array and then search through the array until I find the matching post ID.. that seems wasteful.

with doesn't apply any join, so you can't reference other table.
You can use this:
// User model
public function comments()
{
return $this->hasMany('Comment');
}
// Comment model
public function scopeForPost($query, $postId)
{
$query->where('post_id', $postId);
}
// then you can do this:
Auth::user()->comments()->forPost($postId)->get();
Alternatively you can eager load comments with constraint:
User::with(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}])->find($someUserId);
// or exactly the same as above, but for already fetched user:
// $user .. or
Auth::user()->load(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}]);
// then you can access comments for $postId just like this:
Auth::user()->comments; // collection

When you need to filter your relations, you just have to do it in your Eloquent query:
$data = User::with('posts', 'comments')
->where('users.id', Auth::User()->id)
->where('posts.id', $postID)
->get();
Then you can
foreach($data->comments as $comment)
{
echo $comment->text;
}

Your Comments table would have foreign keys Post_Id and User_ID
To Access all the comments of a particular post from a particular user , can you try this way?
Comment::select('comments.*')
->where('comments.user_id', Auth::user()->id)
->leftJoin('posts','posts.id','=','comments.post_id')
->leftJoin('users','users.id','=','comments.user_id')
->get();
Am sure there is better way to achieve it, but this should give you desired results.
Note use aliases if you have conflicting column names
Let me know if this worked.

Related

Laravel how to use model scope method inside join query

i want to use scope method inside join subquery so is there any way I can do that in laravel?
Post Model
public function scopePublished($query)
{
return $query->where('published',true);
}
Now I want to join user table with post table but in join I want to use this scope method directly but its giving me error.
Users::join('posts',function($q){
$q->on('posts.user_id','users.id');
$q->published();
})->get();
So is there any way I can use scope directly inside join subquery ?
First, you need to add the relation between posts and users to the User model like so:
User Model
public function posts()
{
return $this->hasMany(Post::class);
}
and then your scope stays as it is, and your query if you wanna get users with their published posts:
return User::with(['posts' => function ($query) {
$query->published();
}])
->get();
and if you want to get only users that have published posts:
return User::whereHas('posts', function ($query) {
$query->published();
})
->get();
Note that while with('posts') will include the related table's data in the returned collection, whereHas('posts') will not include the related table's data.
Hence sometimes you may need to call both together, I mean, only with('posts') will eager load relations (in this case posts).

Laravel 5, get only relationship for eloquent model

I have a user and article models in my app. The relation between them is straightforward:
//article.php
use Taggable;
public function user()
{
return $this->belongsTo('App\User');
}
Article uses the Taggable lib which provides me with a variety of methods like Article::withAnyTags (returns all articles tagged with 'xyz' string). Now I'd like to get all users who posted an article/s tagged as 'xyz'. I know how to do this in two lines but something tells me that this is not right. Ideally I'd like to do sth like:
$users = Article::withAnyTags('xyz')->with('user')->select('user'); --pseudocode
Is it possible to do sth like this in eloquent?
Side note: I knot that I could do this with DB::table but this is not an option for me. Please note that there is no "->get()" at the end on my pseudocode. It's so, because I'm paginating the users' results set with lib which works only with eloquent queries.
You could use whereHas():
User::whereHas('articles', function ($query) {
$query->withAnyTags('xyz');
})->paginate();
Or if you need to pass a variable of the tags to the closure you could do:
$tag = 'xyz';
User::whereHas('articles', function ($query) use($tag) {
$query->withAnyTags($tag);
})->paginate();

Laravel Eloquent: Get current id from inner function

I know, we can do this in the controller:
User::with('post')->get();
It will get every user's post from the database, based on users.id.
But the problem is, I want to do this:
User::with(['post' => function($query) {
# Throw users.id here...
}])->get();
How to do that?
You should get the users first, and then load related posts with a separate query and merge them manually.
$users = User::get();
$posts = Post::whereIn('user_id', $users->pluck('id'))->get(); // Get your additional data in this query
$users->each(function ($user) use ($posts)
{
$user->posts = $posts->where('user_id', $user->id);
});
Note: I did not test the code above. It's just an example to show you how to accomplish what you are trying to do.

Laravel Eloquent complex join statement

My database structure is the following:
So I've got a Bloggers table (sorry for the typo in the image), which has 3 topic fields, all 3 are foreign keys to the topics' table id.
I made a Blogger view front-end with a simple table showing all blogger columns. At the moment, the id's of the topics are being shown instead of the names though.
How can I change this?
I've already tried the following in my Controller, but that just creates duplicates in the view with different values in the main_topic column.
$bloggers= DB::table('bloggers')
->join('topics', function ($join) {
$join
->on('bloggers.main_topic', '=', 'topics.id')
->orOn('bloggers.subtopic1', '=', 'topics.id')
->orOn('bloggers.subtopic2', '=', 'topics.id');
})
->select('bloggers.*', 'topics.name as main_topic')
->get();
return view('pages.bloggers', compact('bloggers'));
The answer to my specific question was a little different from the answers given, so I'll post it here in case anyone needs it in the future.
So I've got 3 tables: bloggers, topics and a pivot table called blogger_topic. bloggers contains 3 foreign keys (integers) to topics called main_topic, subtopic1, subtopic2. The pivot table contains a blogger_id, and a topic_id.
What I ended up with:
Blogger.php model:
public function mainTopic()
{
return $this->belongsTo('App\Models\Topic', 'main_topic', 'id');
}
public function subtopicOne()
{
return $this->belongsTo('App\Models\Topic', 'subtopic1', 'id');
}
public function subtopicTwo()
{
return $this->belongsTo('App\Models\Topic', 'subtopic2', 'id');
}
Topic.php model:
public function bloggers()
{
return $this->belongsToMany('App\Models\Blogger');
}
View (blogger.blade.php):
#foreach($bloggers as $blogger)
{{ $blogger->mainTopic }}
{{ $blogger->subtopicOne }}
{{ $blogger->subtopicTwo }}
#endforeach
I think you should change your database tables if possible as below (sample link), by creating a pivot table. And then I think you should make relationships in Blogger and Topic model files with many-to-many. Then it would be very easy to fetch all related topics. For eg,
$topics = $blogger->topics;
Here you can reference the sample db table designs
I kindly recommend you to use models in Laravel coz they'll make your life more simple. Hope this help.
If you cant change your database, you could use group_concat on the results. Not sure if it will work but something like:
DB::table('bloggers')
->join('topics', function ($join) {
$join
->on('bloggers.main_topic', '=', 'topics.id')
->orOn('bloggers.subtopic1', '=', 'topics.id')
->orOn('bloggers.subtopic2', '=', 'topics.id');
})
->groupby('bloggers.id')
->select('bloggers.*', 'topics.name as main_topic', DB::raw('group_concat(topics.name)'))
->get();

Use Eloquent to attach array to query

I have a USER table, an ITEMS table, and a LIKES table. I'm randomly taking 9 items from the Items table (which is named 'categories') and I want to get an array of users which liked that item. I'm returning a JSON response so I can't use laravel's ORM relationships as far as I know. I want to be able to look through the results (with javascript) like so:
foreach item
item->price
....
foreach user
(this is how i wish to look through the users with js)
endforeach
endforeach
{{--Im trying to get an output that looks like so--}}
0: {
cost: 409
views: 0
...
user_like_id: {1,5,2,4,5}
}
EDIT: This is what I would like to attach the array to...
$likes = Category::orderByRaw("RAND()")
->leftJoin('likes', 'likes.item_id', '=', 'categories.id')
->take(9)
->get();
I'm relatively new to programming so please dont downnrate this question
Laravel provides in ways to make json from objects. The first and simplest is the eloquent toJson() method:
$likes = Category::orderByRaw("RAND()")
->leftJoin('likes', 'likes.item_id', '=', 'categories.id')
->take(9)
->get()
->toJson();
$likes is now json.
Second method:
If you return a model from a controller or a routecallback function, laravel returns it as json. So:
class MyController extends Controller{
public function getIndex(){
return Category::orderByRaw("RAND()")
->leftJoin('likes', 'likes.item_id', '=', 'categories.id')
->take(9)
->get();
}
}
This example returns a json response which is quite usefull!
I actually got exactly what I wanted with this:
$likes = Category::orderByRaw("RAND()")
->take(9)
->with(array('getLikes' => function($query)
{
$query->with('getUser');
}))
->get();
Works exactly how I want ito :) thanks for the suggestion tho

Resources