laravel 4 - how to Limit (Take and Skip) for Eloquent ORM? - mysql

TL;DR
Can you limit an Eloquent ORM query like using take() and skip() so that the resulting mysql query is also limited, and it doesn't have to return the entire dataset?
If so, how would you modify:
$test = User::find(1)->games->toArray();
To include limit 3 offset 2?
Tables:
users games userGames
-- id -- id -- user_id
-- name -- name -- game_id
-- steam_id
Models:
class User extends Eloquent {
public function games() {
return $this->belongsToMany('Game', 'userGames', 'user_id', 'game_id');
}
}
class Game extends Eloquent {
public function users() {
return $this->belongsToMany('User', 'userGames', 'user_id', 'game_id');
}
}
Limit in Query Builder
Using the regular Laravel Query Builder I can get all games that belong to user of id 1, and limit the result with take() and skip():
$test = DB::table('games')
->join('userGames', 'userGames.game_id', '=', 'games.id')
->where('userGames.user_id', '=', '1')->take(3)->skip(2)->get();
By listening to the illuminate.query event I can see that the query generated by this is:
select * from `games`
inner join `userGames`
on `userGames`.`game_id` = `games`.`id`
where `userGames`.`user_id` = ?
limit 3 offset 2
Limit in Eloquent ORM
When I try to recreate the same query with Eloquent:
$test = User::find(1)->games->take(2)->toArray();
I'm able to use take but adding skip causes an error. Also the resulting query does not actually contain the limit:
select `games`.*, `userGames`.`user_id` as `pivot_user_id`,
`userGames`.`game_id` as `pivot_game_id` from `games`
inner join `userGames`
on `games`.`id` = `userGames`.`game_id`
where `userGames`.`user_id` = ?
So it seems that the entire result is being queried first, which is not ideal when dealing with large data sets.
Question:
Is it possible to limit an Eloquent ORM query so that at the MYSQL Query level it also limits the result, equivalent to limit 3 offset 2?

User::find(1)->games()->take(3)->skip(2)->get();
I think this should give you your collection. :)
->games will give you a collection, where ->games() will offer a query builder instance.
Enjoy Laravel!

Related

How to count all records if use alias in select query?

I use Sphinx with Yii2 and need to query with filter by jSON field.
$query = new \yii\sphinx\Query();
$query->from('announcements');
$query->addSelect("*");
$query->addSelect(new Expression("IN(filters['color'], 'blue', 'red', 'green') AS f_color"));
$query->where("is_active = 1");
$query->andWhere("f_color = 1");
$announces = $query->all();
There is jSON field filters in my Sphinx index. For example:
[filters] => {"brand":"Toyota","model":"Prius","color":"red","price":"12000"... etc]
It works OK. But now I need to make a pagination... and there is a problem when I try to count records before $query->all()
$count = $query->count(); // Return error "no such filter attribute 'f_color'"
Generated query was:
SELECT COUNT(*) FROM announcements WHERE ( is_active = 1 ) AND ( f_color = 1 )
count() by default replaces the select part with * and this is where your alias is defined hence the error.
There are different ways to achieve it like:
use ActiveDataProvider like described here,
use META information like described here
Since you want to make a pagination I would go with the first example.

Laravel 7 how to perform a query sql with join in mongoDB

I have 2 databases(one in mySql and the other one in MongoDB) in my project and I need to perform a query like this
public function getPosts() {
$user = Auth::user();
$follow = DB::connection("mongodb")
->collection("followers")
->where("user_id", $user->id)->get();
/*$res = DB::select("
SELECT *
FROM posts
WHERE user_id = ? OR user_id IN
(SELECT user_id from $follows)
ORDER BY created_at DESC", [$user->id, $user->id]);
*/
return response()->json($res);
}
This is a query which returns the posts from the logged user and from people the user follow
The followers table (the one in MongoDB) contains "user_id" and "follows_id"
The commented line is the original query (every table in one single database on mySql
Thank you
Edit: I solved through a query in mongodb, then I edited the result to get an array which I incorporated into the sql query through orWhereIn Thank you for your answers :)
I don't think so... you may try a true multimodel database such as Oracle XE (it's free) to achieve your goal here...

How to perform an update containing a sub query on an Eloquent Relationship in laravel 5

I am new to laravel. I'm working on this laravel 5 app but got stuck on trying to perform a query like below.
update applications
set application_status = 'sa'
where id in (select application_id from application_cart where cart_id = 12);
Appreciate help on how to translate this query.
The query below does the trick
return Application::whereIn('id', function($query) use ($id){
$query->from('application_cart')
->select('application_id')->where('cart_id', $id);
})->update(['application_status' => 'sa']);

Convert SQL query to Doctrine 1.2 dql

I have a select query in Mysql table to fetch related table with a duplicate values in a specific column(date).This will successfully display columns and its foreign keys if it has duplicate values in a column.Example two rows same value of (2014-11-10) in a date column
mysql>select man_id,date_created,count(date_created) as count
from collections
group by man_id,date_created
having count(date_created) > 1;
I want this query to convert to Doctrine query since I am using symfony 1.4 as a framework
public function getDuplicateDatePayment() {
$q = $this->createQuery()
->select('man_id','date_created','count(date_created) as count')
->from('Collections')
->groupBy('man_id','date_created')
->having('COUNT(c.date_created) > 1');
return $q->execute();
}
SELECT c.id AS c__id, c.man_id AS c__man_id FROM collections c GROUP BY c.man_id HAVING count(c.date_created) > 1 //result 1 row
why does doctrine query does not display results as expected?How to convert said doctrine query so that it will display result similar to SQL?
//result 1141 rows
update
Collections table is related to Man table in a one to many relationship.Do i have to use innerJoin for this?
After two days of trying,searching for an answer, I finally found a solution to my problem.
SELECT c.id AS c__id, c.man_id AS c__man_id FROM collections c GROUP BY c.man_id HAVING count(c.date_created) > 1
The problem of above sql result from doctrine is ,it does not include 'c.loan_id' in groupBy clause,even though I include it in groupBy clause.The solution is ,just add a separated addGroupBy clause ..And now its working.cheers..
public function getDuplicateDatePayment() {
$q = $this->createQuery()
->select('c.man_id','c.date_created','count(c.date_created) as count')
->from('Collections')
->groupBy('c.man_id')
->addGroupBy('c.date_created')
->having('count(c.date_created) > 1');
return $q->execute();
}

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;