I want to paginate users and count likes, comments and pictures for each. This is what I do now, but it is slow. (the select pics takes about 80ms on 6000 pics and 30 users)
public function index()
{
$users = User::with('pics', 'comments', 'likes')
->groupBy('users.displayname')
->paginate(30);
return View::make('usersoverview')->with('users',$users);
}
How can I just count the rows?
(each table has a 'user'/'user_id' with the id of the user in it)
The relationships of Model 'User':
public function comments()
{
return $this->hasMany('Comment','user');
}
public function pics()
{
return $this->hasMany('Pic','user_id');
}
public function likes()
{
return $this->hasMany('Like','user');
}
When you are using eloquent and getting results, you get them as a Collection type, or how other people call it, Arrays on steroids.
If you take a look at the Collection class you will see a method called count which should do the job.
I have a faster query now. Not perfect, but 8ms - instead of 80ms.
$users = DB::table('users')
->select(DB::raw('(SELECT COUNT(*)
FROM pics
WHERE user_id = users.id) AS countpics,
(SELECT COUNT(*)
FROM comments
WHERE user = users.id) AS countcomments,
(SELECT count(*)
FROM likes
WHERE user = users.id) AS countlikes,
username,
displayname'))
->groupBy('username')
->paginate(30);
Related
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.
So i have a query to make but i don`k know how best to it.
I have 2 class like below:
User.class
class User {
function posts(){
return $this->hasMany(Post::class, 'user_id');
}
}
Post.class
class Post {
function user(){
return $this->belongsTo(User::class, 'user_id');
}
}
and in my controller i want to get posts per User with a limit for each user NOT for all results. So here is what i have this in my controller:
function getPosts(Request $request){
$user_ids = [1,2,3,4];
$posts = Post::whereIn('user_id', $user_ids)->latest()->take(10)->get();
return $posts;
}
So the above get will get me just 10 entries from all of them yet i want to get 10 for each user nomatter how many user IDs
You can simply limit the relationship with a sub-query:
User::with(['posts' => function($query) {
return $query->limit(10);
}]);
May be something like
DB::table('posts as p1')->leftJoin('posts as p2', function($join){
$join->on('p1.id', '=', 'p2.id')
})->whereIn(p1.user_id, $user_ids)->groupBy('p1.id')->having(COUNT(*) < 10)->orderBy([id, created_at]);
will work for you. for the reference question
The query was
SELECT user_comments.* FROM user_comments
LEFT OUTER JOIN user_comments user_comments_2
ON user_comments.post_id = user_comments_2.post_id
AND user_comments.id < user_comments_2.id
where user_comments.post_id in (x,x,x)
GROUP BY user_comments.id
HAVING COUNT(*) < 3
ORDER BY user_id, created_at
Roughly in DB query builder, it will be like
DB::table('user_comments as uc1')->leftJoin('user_comments as uc2', function($join){
$join->on('uc1.post_id', '=', 'uc2.post_id')->andOn(uc1.id < uc2.id);
})->whereIn(uc1.post_id, [x,x,x])->groupBy('uc1.id')->having(COUNT(*) < 3)->orderBy([user_id, created_at]);
I hope this is helpful for you and give you good idea to get it.
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
I have a 3 models: Phone, Product and Store.
A phone has many Product which belongs to a Store.
I am trying to add a golbal scope so that every time I load a phone, products and stores counts are loaded automatically.
products_count works fine, however stores_count is a bit tricky since store is not a Phone's relationship but a Product's.
I have tried the following but it gives me an error "Method getRelated does not exist.", I assume because stores() now returns a collection.
Any ideas as to how I could add the stores_count?
public static function boot(){
parent::boot();
static::addGlobalScope('products', function ($builder){
$builder->withCount('products');
$builder->withCount('stores'); <----- gives error
});
}
public function products(){
return $this->hasMany(Product::class);
}
public function stores(){
$store_ids = $this->products()->get(['store_id'])->unique();
return Store::find($store_ids);
}
Update after #Sandeesh answer.
I tried to use hasManyThrough but it returns an empty collection which is wrong.
When I dd($phone->products); I can see 7 products which have 3 different stores.
public function stores(){
return $this->hasManyThrough(Store::class, Product::class,
'store_id', 'id');
}
Database schema
Phone
-id
Product
-id
-phone_id
-product_id
-store_id
Store
-id
Update 2
So I managed to get the produced query from the stores() method above.
select `phones`.*,
(select count(*) from `products` where `phones`.`id` = `products`.`phone_id`) as `products_count`,
(select count(*) from `stores` inner join `products` on `products`.`id` = `stores`.`id` where `phones`.`id` = `products`.`store_id`) as `stores_count`
from `phones` where `slug` = ? limit 1
The problem is in the third line. The query is messed up, not sure what is wrong with the relationship though.
You can using hasManyThrough
https://laravel.com/docs/5.4/eloquent-relationships#has-many-through
public function stores()
{
return $this->hasManyThrough(Store::class, Product::class);
}
Edit
This should give you what you need. But eager loading is always better
protected $appends = [
'productCount',
'storeCount'
];
public function getProductCountAttribute()
{
return $this->products()->count();
}
public function getStoreCountAttribute()
{
return Store::whereIn('id', $this->products()->pluck('store_id')->toArray())->count();
}
I'm trying to achieve this
SELECT *
FROM pending
LEFT JOIN users ON pending.user_id = users.id
WHERE pending.school_id = '1'
Which produces two results of the two tables combined with the users details output.
Pending Model
public function users(){
return $this->belongsTo('User');
}
public function school(){
return $this->belongsTo('School');
}
User model
public function pending(){
return $this->hasMany('Pending','user_id');
}
School Model
public function pending(){
return $this->hasMany('Pending','school_id');
}
Controller
$pending_user = Pending::with('users')->where('school_id', '=', '1')->get();
This retrieves an array of the two records but a null result in the user array.
"users":null
Any help appreciated.
I have decided to go with this
$pending_user = Pending::select('first_name','last_name','user_id','how')->where('school_id', '=', '1')->leftjoin('users','pending.user_id','=','users.id')->get();