Laravel GroupBy and Count using eloquent model - mysql

I have a table
'new_comments'
with fields
id,user_id,
title,
comment_description
I have another table named
'comments_upvote'
having
field user_id,
post_id,
likes
id of new_comments and post_id of comments_upvote table are same. we have to take those comments which have the most likes. how we fetch that data.
$ud = Comments_upvote::select('post_id')->groupby('post_id')-
>orderby(DB::raw('count(likes)'), 'desc')->get();
$postid = array();
foreach ($ud as $key => $value) {
$postid[] = $value->post_id;
}
$data = DB::table('new_comments')->whereIn('id',$postid)->get();
but the problem is that i have to count all likes whose value = 1 how can we do that.

If you showed us some code you'd get a more concrete answer, but here's a quick outline of what you need: define your relationships (make sure you use your custom foreign key), use whereHas, group by post_id count, and sort by count descending.
So I'm guessing you have at least two models NewComment and CommentUpvote (you should). In the NewComment define a 1:n relationship:
public function upvotes(){
$this->hasMany('App\CommentUpvote', 'post_id');
}
Then in your controller (again, guessing, since you didn't show any code):
$bestComments = NewComment::whereHas('upvotes', function($query){
$query->select('post_id', DB::raw('count(*) as total'))->groupBy('post_id')->orderBy('total', 'desc');
})->get();
Disclaimer: this is untested and off the top of my head, but should nugde you in the right direction.

Related

Count quantity of books by category, separate tables

I'm using Laravel 5 and I need to know the number of books by category, these data I will use on google charts.
I have the code below that I use to find out the number of users by sex. However, the data is in the same table.
$data = DB::table('books')
->select(
DB::raw('category_id as category'),
DB::raw('count(*) as number'))
->groupBy('category')
->get();
$array[] = ['Category', 'Number'];
foreach($data as $key => $value)
{
$array[++$key] = [$value->category->name, $value->number];
}
$test = json_encode($array);
Using the same logic as above, how can I get the number of books by category?
I have the Books table:
ID | Name | ID_CATEGORY
1 Laravel 20
2 Java 20
Category table :
ID | Name
20 Programming
Could you help me in this situation?
You should just group by your ID_CATEGORY column instead of sex.
If you want to construct such array as before, you probably want the name of category. That would be easy if you used Eloquent. You would have $value->category->Name_Category available. If you want those preloaded instead of a query for every value, just add ->with('category') to the query and you'll have it.
If you are not using Eloquent, you should just load the category table separately, key it, and use $categories[$value->ID_CATEGORY]->Name_Category.
Other comments
DB::raw('sex as sex') is equivalent to sex.
++$key seems redundant, you could just push to the end by assigning to $array[].
Array with a header row is not very JSON. Typically you'd have key:value pairs all of the time.
You can construct the array using collection methods.
If you don't have good reasons for the opposite, you should stick to Laravel naming conventions.
I would probably not create an array myself, but do your example as something like this:
$stats = User::select('sex', DB::raw('count(*) as number'))
->groupBy('sex')
->get();
$stats->setVisible(['sex', 'number']);
return $stats;

Laravel Eloquent Where Condition from 2 tables

I have a list of books titles that appear where a user can leave feedback.
I have 2 tables Books table & feedback table.
The tables are for a site where a user gives feedback on a book, once the user has left feedback for that book, I no longer want the book to appear in the book results for that user, there are many users that will be leaving feedback for the same book.
A book can have many feedback
I want to do somthing like if user already left a feedback in the feedback table, then that book they left feedback for should not appear in the results.
I'm not sure how to go about doing this?
my feedback table looks like this:
id
book_id
rating
feedback
user_id
my books table looks like this:
id
user_id
title
I am pulling the books results as follows.
$books = Books::where('user_id', $id)->get();
I need something that will check condition if the user has left feedback for the book yet, then don't show the books the user has already left feedback for in the results.
Just do a query that where the feedback is empty and if its empty it will have a result and if not of course it will not have a result so use if to ask the feedback if its empty
$feedback = Feedback::where('user_id', $id)->get();
$feed = '';
if(!empty($feedback->feedback)){
$feed = $feedback;
}
return view('yourview')->with('feed', $feed);
if you have join table you can do this also. on your upper controller put
use DB;
$feedback = DB::table('feedback as f')
->join('books as b', 'b.id', '=', 'f.book_id')
->join('user as u', 'user.id', '=', 'u.id')
->where('u.id',$id)
->select('u.*','f.*','b.*')->get();
$feed = '';
if(!empty($feedback->feedback)){
$feed = $feedback;
}
return view('yourview')->with('feed', $feed);
Assuming your have relation in your Books Modal like this
public function feedback(){
return $this->hasMany(Feedback::class,'book_id','id');
}
For getting all the books with user id $id which has atleast one feedback your query should be like this
$books = Books::where('user_id', $id)->has('feedback')->get();
If you need more filters you can use whereHas instead of has which will allow you to run more queries.
Update
To get Books that doesn't have feedbacks from user with userid $id will like this.
$books = Books:: whereDoesntHave('feedback',function($q) use($id){
$q->where('user_id',$id);
})->get();
But if you like to get all the books of a user with id $id and also he hasn't and also doesn't have any feedback from him
$books = Books::where('user_id', $id)-> whereDoesntHave('feedback',function($q) use($id){
$q->where('user_id',$id);
})->get();

How can I get all entities without a specific related entity?

I have these tables:
users
id
name
events
id
name
entries
id
user_id
event_id
How can I get all users that do not have an entry with event_id 4?
With this code:
$users = User::query();
$users->leftJoin('entries', 'users.id', '=', 'entries.user_id')
->where('event_id','!=', $event_id)->get();
I still get users that already have an entry for that specific event.
This is what I want:
Get all entries which have event_id 4
Get the user_id from those entries
Remove other entries which have that user_id.
$entries = Entry::where(event_id, '=', 4)->get();
foreach ($entries as &$entry) {
//remove all entries from $entries array which "user_id = $entry->user_id"
}
How can I do the above with just a query?
Going by your question following is the answer but i guess this is not what you finally want. so elaborate more and specify the sample input and output datasets
select * from your_table where event_id <> 4;
The SQL you want is:
select user_id
from t
group by user_id
having sum(event_id = 4) = 0;
select u.*
from User u
left join entries e
on e.user_id = u.id and e.event_id = 4
where e.id is null
You're looking for <>, not !=. Also you're not letting it know which table event_id is on.
Try
$users = User::query();
$users->leftJoin('entries', 'users.id', '=', 'entries.user_id')
->where('entries.event_id','<>', $event_id)->get();
More information can be found at https://laravel.com/docs/5.4/queries#where-clauses
You can use the whereDoesntHave builder method.
$users = User::query()->whereDoesntHave("entry", function($q) use ($event_id) {
$q->where('id', $event_id);
})->get();
The method takes a closure in which you can define criteria for the related entities that must not exist.
If you need the users and the entries, I think you can still join the entries using with.
$users = User::query()
->with('entries')
->whereDoesntHave("entry", function($q) use ($event_id) {
$q->where('id', $event_id); })
->get();

laravel 4 - join latest result in group by query

This query gives a pagination of all 'albums' with a picture and description for each. Now I am trying to get always the latest picture of each album.
I have tried to add a second orderBy('pics.created_at') , but that did not work. I think I need some kind of subquery but don't know how.
$query = AlbumPic::select(DB::raw('COUNT(pics.id) as picscount,
pics.url,
pics.user_id,
pics.created_at,
albums.id as album_id,
albums.title,
albums.text,
users.username'))
->join('albums','albums.id','=','album_pic.album_id')
->join('pics','pics.id','=','album_pic.pic_id')
->join('users','users.id','=','pics.user_id');
if(!is_null($user_id))
$query->where('album_pic.user_id',$user_id);
$albums = $query->groupBy('albums.id')
->orderBy('albums.created_at','desc')
->paginate(20);
edit
I made a mistake. I don't have created_at and updated_at in the album_pic table .
So my 'Album' - model/relations are now like this:
public function pics()
{
return $this->belongsToMany('Pic');
}
public function latestPic()
{
return $this->belongsToMany('Pic')->latest('pics.created_at');
}
And the query now looks like this:
$q = Album::with('pics')->with('latestPic.users');
if(!is_null($user_id))
$q->where('albums.user_id',$user_id);
$albums = $q->orderBy('albums.created_at','desc')
->paginate(20);
This works. Only thing I would like to improve is the way, the pictures per album are counted. Now I get all with with('pics') and then do a count($album->pics) in the view. If there is a way to not load everything, but only count the pictures, it would be nice.
You need to get the MAX(created_at) inside a subquery; see MySQL select MAX(datetime) not returning max value for example.
Really, though, if you're doing this in Laravel, it would be better to set these all up as relations and leverage the power of Eloquent. Then, you can define a relationship for pictures that uses ->latest() to return the most recent. See laravel eloquent query group by last id for an example (which uses one table, but the principle is the same for multiple tables).
Here's how you could set this up using Eloquent relations:
User model (User.php)
class User extends Eloquent {
public function albums()
{
return $this->hasMany('Album');
}
}
Album model (Album.php)
class Album extends Eloquent {
public function pics()
{
return $this->belongsToMany('Pic');
}
public function latestPic()
{
return $this->belongsToMany('Pic')->latest('album_pic.created_at');
}
}
Because you have a many-to-many relationship between albums and pics, in the latestPic() relation, you must specify the album_pic.created_at field for latest()—since we are actually interested in the order of entries in the pivot table, rather than in the pics table.
Finally, link this all together. For example, for a user with id of 1:
$albums = User::find(1)->albums()->with('pics')->with('latestPic')->paginate(20);
foreach($albums as $album) {
echo('<br>Album:');
var_dump($album->title);
echo('All pics:');
foreach($album->pics as $pic) {
var_dump($pic->url);
}
echo('Latest pic:');
$latestPic = $album->latestPic->first();
if ($latestPic) {
var_dump($latestPic->url);
}
}
Note that we are eager loading the pics and latestPic to reduce the number on calls to the database. Also note that accessing the $latestPic->url is wrapped in an if statement, otherwise albums that do not have any photos will throw an error since $album->latestPic would return null.
As #cedie correctly noted, Laravel doesn't handle pagination all that efficiently when using a groupBy statement, but that shouldn't be a problem in this case. The underlying queries do not use groupBy, so you should be save to use ->paginate(20).
Try using this in your select query:
max(pics.created_at) as created_at
instead of this:
pics.created_at
So your code should look like this:
AlbumPic::select(DB::raw('COUNT(pics.id) as picscount,
pics.url,
pics.user_id,
max(pics.created_at) as created_at,
albums.id as album_id,
albums.title,
albums.text,
users.username'))
Perhaps ypu can figure out how to adapt this for your purposes...
SELECT ap.*
, p.*
FROM album_pic ap
JOIN pics p
ON p.id = ap.pic_id
JOIN
( SELECT ap.*
, MAX(p.created_at) max_created_at
FROM album_pics ap
JOIN p.*
ON p.id = ap.pic_id
) x
ON x.album_id = ap.album_id
AND x.max_created_at = p.created_at;

Brain boiling from MySQL - How to do a complicated select from multiple tables?

I have two tables: Users and Groups
In my table "Users", there is a column called "ID" for all the user ids.
In my table "Groups" there is a column called "Participants", fields in this column are filled with all the user ids like this "PID_134,PID_489,PID_4784," - And there is a column "ID" that identifies a specific group.
Now what i want to do, i want to create a menu that shows all the users that are not yet in this particular group.
So i need to get all the user ids, that are not yet in the Participants column of a group with a particular ID.
It would be cool if there was a single mysql query for that - But any PHP + MySQL solutions are okay, too.
How does that work? Any guesses?
UPDATE:
i know, that's not code, but is there a way I could do something like this that would return me a list of all the users?
SELECT *
FROM users, groups
WHERE groups.participants NOT LIKE '%PID_'users.id'%' AND groups.id = 1;
Something like this. You just get rid of "PID_" part of ID.
SELECT * FROM [users] WHERE [id] NOT IN
(SELECT replace(id,'PID_','') FROM groups WHERE group_name='group1')
Group1 would be your variable - group id/name of menu that you've opened.
You can select from multiple tables as shown below:
SELECT * from users, groups WHERE users.id != groups.participants AND groups.id = 1;
This will list all users who are not in group id 1; A more elegant solution can be found by using joins, but this is simple and will do the trick.
I believe something like that should help:
SELECT * FROM users WHERE users.id NOT IN (SELECT groups.participants FROM groups)
But this works only if your DB is normalized. So for your case I see only PHP + MySQL solution. Not very elegant, but it does the job.
<?php
$participants_array = mysql_query("SELECT participants FROM groups");
$ids = array();
while ($participant = mysql_fetch_assoc($participants_array))
{
$id = explode(',', $participant['participant']);
foreach ($id as $instance)
{
if (!in_array($instance, $ids)) $ids[] = $instance;
}
}
$participants = implode(',', $ids);
$result = mysql_query("SELECT * FROM users WHERE id NOT IN ( $participants )");
But I highly recommend normalizing the database.