Count from hasManyThrough relationship with eager loading (need only Count) - laravel

I only need the count, don't want to retrive the results or perform query each time for each row. This is why I want eager loading.
I have 3 tables like the following:
Admins
id
Posts
id
admin_id
Comments
id
user_id //nullable (null if comment from admin)
admin_id //nullable (null if comment from user)
news_id
Now I want to retrieve all the posts from a single admin and all the comments count from those posts, without retrieving all the comments for posts, ONLY COUNT of comments,
With eager loading to avoid n+1 query issue;
Here I think we should make a relation to be used with eager loading like the following:
//admin model
public function commentsCountRelation()
{
return $this->hasManyThrough(Comment::class, News::class, 'admin_id', 'news_id')
->selectRaw('news_id, count(*) as count')
->groupBy('news_id');
}
--Look Here I used hasManyThrough relation because, news_id is not in Admins table.
Then I should make an attribute, to access the count easyly, like:
public function getCommentsCountAttribute()
{
return $this->commentsCountRelation->first()->count ?? 0;
}
Then access it like:
$admin = Admin::with('commentsCountRelation')->findOrFail($id);
$admin->commentsCount;
But it always returns null, why is that?
The following works for hasMany & belongsTo, I've used it on my other models like:
//relation
public function ordersCountRelation()
{
return $this->hasOne(Order::class)->selectRaw('user_id, count(*) as count')
->groupBy('user_id');
}
//attribute
public function getOrdersCountAttribute()
{
return $this->ordersCountRelation->count ?? 0;
}
//Then accessed like:
$user = User::with('ordersCountRelation')->find($id);
$user->ordersCount; //return only count
Any help will be highly appreciated

there is no need for using hasManyThrough.
just use some relations and withCount method:
$admin->posts->withCount('comments')->get();
then you can access it with: $comments_count

Related

Get only one column from relation

I have found this: Get Specific Columns Using “With()” Function in Laravel Eloquent
but nothing from there did not help.
I have users table, columns: id , name , supplier_id. Table suppliers with columns: id, name.
When I call relation from Model or use eager constraints, relation is empty. When I comment(remove) constraint select(['id']) - results are present, but with all users fields.
$query = Supplier::with(['test_staff_id_only' => function ($query) {
//$query->where('id',8); // works only for testing https://laravel.com/docs/6.x/eloquent-relationships#constraining-eager-loads
// option 1
$query->select(['id']); // not working , no results in // "test_staff_id_only": []
// option 2
//$query->raw('select id from users'); // results with all fields from users table
}])->first();
return $query;
In Supplier model:
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id')
//option 3 - if enabled, no results in this relation
->select(['id']);// also tried: ->selectRaw('users.id as uid from users') and ->select('users.id')
}
How can I select only id from users?
in you relation remove select(['id'])
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id');
}
now in your code:
$query = Supplier::with(['test_staff_id_only:id,supplier_id'])->first();
There's a pretty simple answer actually. Define your relationship as:
public function users(){
return $this->hasMany(User::class, 'supplier_id', 'id');
}
Now, if you call Supplier::with('users')->get(), you'll get a list of all suppliers with their users, which is close, but a bit bloated. To limit the columns returned in the relationship, use the : modifier:
$suppliersWithUserIds = Supplier::with('users:id')->get();
Now, you will have a list of Supplier models, and each $supplier->users value will only contain the ID.

Why does this Eloquent relationship return null in a foreach loop?

I have a relationship that's acting weird. If I get the results and dd(), it has the correct data. However, if I run the collection through a foreach loop it's like the relationship disappears.
The users table has a field called referred_by, which stores the user ID of the person who referred the user. In my instance I'm looking for all the new users who were referred by the user with the ID of 3.
Here's the relationship in User.php
public function referrer()
{
return $this->belongsTo(User::class, 'referred_by');
}
Here's the code which is returning funky results
$users = User::where('referred_by', 3)
->with('referrer')
->get();
// doing dd() here returns a collection with full referrer relationship;
// the returned data is as expected
dd($users);
foreach($users as $user)
{
// dd($user) here returns the relationship, as it should
// dd($user->referrer) here returns null, like the relationship doesn't exist
}
Try with
foreach($users as $user)
{
// dd($user) here returns the relationship, as it should
dd($user['referrer'])
}
Rather then
// dd($user->referrer)
Turns out I had a database column on the users table named referrer that was causing an issue. I changed the relationship's method name from referrer() to referredBy() and it worked. *eyeroll*

Laravel Relationships - Select rows with same column value in blade

Let's say I have a post with many comments and I properly defined $post->comments relation in my post model. Comments have a column named confirmed with the value of 0 or 1. How can I select confirmed (rows which have confirmed value of 1) rows in my blade template?
This can help you
In your Post model
public function comments()
{
return $this->hasMany(Comment:class);
}
public function confirmedComments()
{
return $this->comments()->whereConfirmed(1);
}
And from your controller
$comments = $post->confirmedComments;
If in blade, you want select confirmed comments, it's so easy to do it with
#if($comment->confirmed)
//confirmed comment
#else
//
#endif
Hope it'll be helpful !
There are many ways to do this.
If you already have the comments eager loaded, then you can use the where() method on the comments collection:
$confirmedComments = $post->comments->where('confirmed', 1);
This will go through all the comments in the collection and only return those that are confirmed.
If you don't have the existing collection, you can add the where clause to the relationship query. This way, you're only getting confirmed comments from the database, and you're not wasting resources retrieving and building models for comments you're not using
$confirmedComments = $post->comments()->where('confirmed', 1)->get();
And, another option, would be to create a new relationship for just confirmed comments, and that way you can eager load the new relationship:
public function comments()
{
return $this->hasMany(Comments::class);
}
public function confirmedComments()
{
return $this->comments()->where('confirmed', 1);
}
$confirmedComments = $post->confirmedComments;

Laravel Eloquent Joins use of belongs to

I have 2 table for get inner join query records in laravel eloquent, but is not working with combine data get.
Table :category
id
name
cat_status
Table:image
id
title
desc
img_status
image controller
//get all active images
ImageModel::where('status',true)->get();
public function getCategory() {
return $this->belongsTo('\App\Api\CategoryModel','cat_id');
get category active only
public function getActiveCategory() {
return $this->getCategory()->where('cat_status','=', 1);
}
I need to get only which category is active, that images only.
if cat 1,2,3 3(is inactive)
image table
title=a1,cat_id=1,
title=a2,cat_id=2,
title=a3,cat_id=3
Now I get all 3 images , i need only first 2, because cat_id 3 is inactive .
Any ideas how I can join the status condition.
Thanks in advance.
Well relation methods doesn't provides joins. You have to do it manually.
If You want to get IMAGES from active categories then You''ll have to query like this:
// method from image model
public function getImagesFromActiveCategory() {
return $this->join('category')
->select('image.*')
->join('category', 'category.id', '=', 'image.cat_id')
->where('cat_status','=', 1)
->get();
}
Thank you for your giving the quick responses,
I tried to use other but is solved now with the use of belongsToMany Method.
Below functions gives exact records from the DB, which match with each category and images table, and category status active records only.
#Image Controller
$images= ImageModel::where('status',true)->get();
foreach($images as $img){
#get data
$onlyactiveCategoryImages=$img->getActiveCategory()->get();
}
#image Model
#get category data
public function getCategory() {
return $this->belongsToMany('\App\Api\CategoryModel','images','id','cat_id');
}
#get category active only
public function getActiveCategory() {
return $this->getCategory()->where('cat_status','=', 1);
}
Thank you once again for all :).

laravel - eloquent - get sum of related model specific column

assuming that I have the table
orders
with fields
id, userId, amount, description
and the table
user
with various fields
how if I wand to get all the users (with all its fields) and also the sum of the "amount" column of the orders related to that user?
assuming that I have:
user:{id:15,firstName:jim,lastName:morrison,gender:male}
and
order:{id:1,userId:15,amount:10,description:"order xxx"},
order:{id:3,userId:15,amount:40,description:"order yyy"}
I would like to receive:
user:{id:15,firstName:jim,lastName:morrison,gender:male,orderAmount:50}
Of course I would like to avoid the foreach statement.
I've setted this on my user model
public function userOrder (){
return $this->hasMany('Order', 'userId');
}
And I've tryed this:
return $this->hasMany('Order', 'userId')->sum('amount');
without any luck...
Some thaughts and hopefully an answer to your question:
I would rename the user table to users to stick to laravel conventions.
http://laravel.com/docs/4.2/eloquent#basic-usage
I would name the method in the User model orders
public function orders()
{
return $this->hasMany('Order', 'userId');
}
To query a user, his orders and sum afterwards his orders amount values:
$userdata = User::with( 'orders' )->where( 'userId', 15 )->first();
$sum = $userdata[ 'orders' ]->sum( 'amount' );

Resources