Laravel retrieve relationship data via another relationship - laravel

I have three tables
Question table:
id
title
description
Answer table:
id
answerbody
question_id
Likes table:
id
answer_id
I want to retrieve single question details with all the answers and answer_count and their respective likes (only like_count)
I tried this
public function show($id)
{
return question::with(['answers','answers.likes'])->withCount(['answers'])->where('id',$id)-
>get();
}
How do I return likes with count?

If you need all answers for a question and along with their likes count you can call a closure inside your with() clause and load their related likes count as
Question::with(['answers'=> function ($query) {
$query->withCount('likes')
->orderBy('likes_count', 'desc');
}])
->withCount(['answers'])
->where('id',$id)
->get();
This way you can also sort the answers based on their likes count

Each question has many answers. So in the Question model add the below code :
public function answer()
{
return $this->hasMany(Answer::class);
}
Each answer has many likes. So in the Answer model add the below code :
public function likes()
{
return $this->hasMany(Like::class);
}
Now you have to define a mutator "getDataAttribute" as below in the Question model:
public function getDataAttribute()
{
return [
'question' => $this,
'answers' => $this->answers()->get(),
'answer_count' => $this->answers()->get()->count(),
'likes' => $this->answers()->likes()->get(),
];
}
Finally, anywhere you needed the question information you just need to invoke :
$question->data
to retrieve question, likes and question count.

Related

Route model binding select specific columns with relation

Route:
Route::get('/posts/{post}', [PostController::class, 'show']);
Controller:
public function show(Post $post){
$postData = $post->load(['author' => function($query){
$query->select('post_id', 'name');
}])
->get(['title', 'desc', 'created_date'])
->toArray();
}
This returns all the posts in the database, While I only want to get the selected post passed to the show function.
So if I visit /posts/3, It should show data related to the post with id = 3, not all posts.
The result should be an array with only the selected post.
When you are inside the Controller you already have the Post you want. If you call get you are getting all posts with a new query.
To select specific columns from a relationship, you can do it like this:
public function show(Post $post)
{
$post->load('author:id,name,age'); // load columns id, name, age from author
}
Notice that including the id is required.
To specify columns for the Post, there's no way to do it per route, the only way is to override how Laravel resolves the implicit binding. For that, you can add this in your Post model class:
public function resolveRouteBinding($value, $field = null)
{
return $this->whereKey($value)->select(['id', 'body', 'author_id'])->firstOrFail();
}
Notice that including the author_id is required to load the Author afterwards inside the Controller method. Also keep in mind this will affect all routes where you implicitly load a Post.
Please don't use get() function on the model.
public function show(Post $post)
{
$post->load(['author' => function ($query) {
$query->select(['post_id', 'name']);
]);
dd($post->toArray());
}
Btw, the author shouldn't have a post_id, because author can have multiple posts. you should update the database structure or you are doing wrong in the controller. (my bad, Thanks #techno)

get posts where relationship count is equel to post's attribute

I have a model "Post" which has a field "daily" in the database.
$table->integer('daily'); // could be anything
The Model "Post" has a relationship to the model "Comments".
public function comments() {
return $this->hasMany(Comment::class);
}
public function completed() {
return $this->hasMany(Comment::class)->completed();
}
Comments model
public function scopeCompleted($query) {
return $query->....?
}
I want to get all user's posts where it's comments count are equal to the post's 'daily' field. For example: if the post's daily field is '5' and there are 5 comments to this posts, I want to see this post in the returned lists, otherwise don't return it.
I know I can get all of them and do it with loop, but I wanna know if there is any way to do this with eloquent only.
Try this
$user=User::with('posts','posts.comments')
->whereHas('posts.comments',function ($query){
//if any additional filters
},'=',DB::raw('posts.daily'))
->get();
or if not needed additional query then
$user=User::with('posts','posts.comments')
->whereHas('posts.comments',null,'=',DB::raw('posts.daily'))
->get();
Got it working like this
public function scopeCompleted($query) {
return $query->has('comments', '=', DB::raw('posts.comments'));
}

Consultation through relationships in laravel

I searched endlessly for a question that answered my question here and I didn't find it. My question is as follows, I have 3 models: User, Post and Comments. Where user has a relationship with one to many posting, and Post has a relationship with one to many comments as well. How can I get all of the user's comments on all posts?
Currently my solution looks like this:
Models Users:
public function comments(){
$comments = array();
foreach ($this->posts()->get() as $el) {
foreach ($el->posts()->get() as $nEl) {
array_push($comments, $nEl);
}
}
return collect($comments);
}
I would like a less expensive and native solution for laravel, if any.
On your User model, something like:
public function getComments()
{
return Comment::whereHas('posts', function ($query) {
$query->where('user_id', $this->id);
})->get();
}
Also see:
https://laravel.com/docs/6.x/eloquent-relationships#has-many-through

Using withCount() on a relationship that has distinct()

I have the following models:
Question: [title, user_id]
Reply: [body, question_id, user_id]
User: [name]
As you can see, a question has many replies, and a reply belongs to a user.
I've added a contributors relationship to the Question model that retrieves all the users who've added a reply (using the replies as the join table):
public function contributors()
{
return $this->belongsToMany(User::class, 'replies')->distinct('user_id');
}
I had to use distinct() to remove duplicates because a user might post many replies on a single question and this works fine.
Now the problem happens when I do:
Question::withCount('contributors')->get()
It ignores the call to distinct() and gives me the total number of users who've added a reply (including duplicates).
Any idea how I can fix this?
Remove distinct() from the relationship and use withCount() with a raw expression:
public function contributors()
{
return $this->belongsToMany(User::class, 'replies');
}
Question::withCount(['contributors' => function ($query) {
$query->select(DB::raw('count(distinct(user_id))'));
}])->get();
u can also use variable here for distinction
public function contributors()
{
return $this->belongsToMany(User::class, 'replies');
}
Question::withCount(['contributors' => function ($query) use ($var) {
$query->select(DB::raw('count(distinct(user_id))'));
}])->get();
You need to continue using the distinct('user_id') in the relationship method for operations like these to work correctly:
$question->contributors()->count();
$question->contributors->count();
$question->contributors()->get()->count();
otherwise in some cases you will get different results.

Ordering data from a table with the help of other table by using relationships with laravel?

I have the controller with the query:
$Comments = Comment::orderBy('id_parent', 'asc')->get();
And I have the Comment model:
class comment extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function votes()
{
return $this->hasMany('App\Vote', 'comment_id', 'id_comment');
}
}
I want to retrieve the comments data sorted in a specific way, every comment has multiply votes voted by different users, so the count('vote') is the number of votes for each comment. The problem is that I am stuck with how to call the specific votes function in the model so that it can count the column vote and order it either asc or desc.
That in the end I can have the $Comments sorted also by the total number of votes.
You can try as:
$Comments = Comment::withCount('votes')->orderBy('votes_count', 'asc')->get();
withCount() method is used when you want to count the number of results from a relationship without actually loading them, which will place a {relation}_count column on your resulting models.
Using sortBy:
$comments=Comment::with('votes')->get()->sortBydesc('votes');
foreach($comments as $comment)
{
echo $comment->votes->count('vote');
}

Resources