How do I order the results of a query by a field in a related table?
I have two tables,
table users:
id, first_name, last_name, etc
table videos:
id, user_id, title, etc
Model: Video
public function user()
{
return $this->belongsTo('App\Models\User');
}
For example I am querying all videos where title = "video 1" and using skip & take for pagination/pager.
I now want to order this list of videos by the users first name.
$query = \App\Models\Video::where('title','=','Video 1')->skip(0)->take(10);
What is the best/efficient way of doing this? I can't order the array/collection after the query as then the pagination won't work.
Thanks.
Why don't you use:
$query = \App\Models\Video::where('title','=','Video 1')
->join("users","users.id","=","user_id")
->orderBy("users.first_name")->skip(0)->take(10);
Related
I have a table like this now what i want to do is count the records for each ID where STATUS is COMPLETED
.
Something like SELECT COUNT(*) FROM TABLE WHERE ID= foreach(ID)
AND STATUS=1
select id, count(id) as count from table where status='COMPLETED' group by id;
Basically you need to use group by clause in MySQL.
You can do it using Active Record as this:
I assuming you have you table model named like Job, then you can get a needed value as this:
$number = Job::find()->where(['STATUS' => 'Completed'])->count();
or it is always better to store constant properties in the model like this:
class Job extends ActiveRecord {
const STATUS_COMPLETED = 'Completed';
then your ActiveQuery will look like this:
$number = Job::find()->where(['STATUS' => Job::STATUS_COMPLETED])->count();
Also here is the full description: Querying Data
$any_var = Your_Table::find()->where(['status' => 'Cimpleted'])->select('id')->orderBy('id')->groupBy('id')->count ();
I have 4 tables in my DB. User , user_details, tags and taggables. By using the user table I am getting user along with user detail and tags. Following is table schema
Table user:
id, name, email, password
Table user_details
user_id, about, vision, picture
Table tags:
id, name
Table taggables
user_id, tag_id
here is my query:
User::with('userDetails','tags')->get();
I want to use where like in user name or tag name, How can I use multiple where like on user name and tag name????
Do you want something like this?
$users= User::with('userDetails','tags')
->where('name', 'LIKE',"%{$search}%")
->orWhereHas('tags', function($query) use($search) {
$query->where('name','LIKE',"%{$search}%");
})
->get();
$search is a variable here.
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;
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.
Dear Sir/Mam iam trying to sort duplicates from a table.
I want to check if a name has teh same streetname and then only show the first result of those, resulting in unique names ommitting the doubles.
I tried the distinct(name) and the group by statements but to no avail.
Group by ended up limmiting my results
$klantquery = "SELECT name, ID, street, tel, email FROM customers where name LIKE '%$search%' ORDER BY name ASC";
This query works but shows all records i need to sift out the extra ones? in order to show only unique results.
iam using mysql and php
So now i used this query:
$klantquery = "SELECT DISTINCT naam,straat,email,huisnummer,plaats,date FROM ".$GLOBALS["klanten"]." where naam LIKE '%$klantsearch%' ORDER BY naam ASC";
Works like a charm but it omits the ID row in the results so its useless??
How do include the ID field in the results?
Where is the logic?
Got it first i selected the records i needed with distinct then later added the IDs with a subquery, elegant not realy, works though.
$klantquery = "SELECT Distinct naam,straat,email,huisnummer,plaats,date FROM ".$GLOBALS["klanten"]." where naam LIKE '%$klantsearch%' ORDER BY naam ASC";
while ($result=$klantpaging->result_assoc()) {
$subquery = "SELECT ID,naam,straat FROM ".$GLOBALS["klanten"]." where naam='".addslashes($result['naam'])."' AND straat='".$result['straat']."'";
$subresult = mysql_query($subquery) or die("Query failed : " . mysql_error());
while ($subline = mysql_fetch_assoc($subresult)) {
$result[ID] = $subline[ID];
}
$klantrecords[] = $result;
}