Eloquent One-to-Many OrderBy - laravel

I'm working with Eloquent on a One-to-Many relationship.
I want to order my Users by using their last post DateTime (created_at) but I can't figure out how to make this work.
Table Users :
id | name
1 | John
2 | Doe
3 | Foo
4 | ...
Table Posts :
id | title | body | user_id | created_at
1 | Title 1| Body1 | 1 | 2014-03-06 14:00:00
2 | Title 2| Body2 | 1 | 2014-03-04 14:00:00
3 | Title 3| Body3 | 2 | 2014-03-03 14:00:00
4 | Title 4| Body4 | 3 | 2014-03-05 14:00:00
Example of final output desired :
name | title | created_at
John | Title 1 | 2014-03-06 14:00:00
Foo | Title 4 | 2014-03-05 14:00:00
Doe | Title 3 | 2014-03-03 14:00:00
The closer I could get was :
$users = User::with(['posts' => function($query){
$query->orderBy('created_at', 'desc');
}])->get();
But this code extracts all the posts for each user and I just want the last one.
Can you help me please? Thanks.
UPDATE : I finally found what I was looking for : Retrieve users' last post and sort the users in ascending order (of this last post's timestamp). Feel free to improve this query!
$users = DB::table('posts')
->join('users', 'posts.user_id', '=', 'users.id')
->select(DB::raw('posts.id, posts.user_id, MAX(created_at) as created_at'))
->groupBy('posts.user_id')
->orderBy('created_at', 'asc')
->get();

You may try this:
$users = User::with(array('posts' => function($query){
$query->orderBy('created_at', 'desc')->groupBy('user_id');
}))->get();
Update: You may try this:
$users = User::join('posts', 'users.id', '=', 'posts.user_id')
->orderBy('posts.created_at', 'desc')
->groupBy('posts.user_id')
->select('users.*', 'posts.created_at as postTime')
->get();
I've only selected created_at from posts table but you may add more fields in select like:
->select('users.*', 'posts.created_at as postTime', 'posts.updated_at as postUpTime', 'posts.id as pid', 'posts.title')

I believe you will either have to use usort() which is a little bit more complicated, or you can use joins, but with that method, you'd also lose the way Eloquent sets up the relations.
Using usort()...
private function cmp($a, $b)
{
if($a->posts->created_at == $b->posts->created_at) {
return 0;
}
return (strtotime($a->posts->created_at) < strtotime($b->posts->created_at)) ? -1 : 1;
}
$users = User::with(array('posts' => function($query){
$query->orderBy('created_at', 'desc')->groupBy('user_id')->first();
}))->get();
$users = usort($users, array($this, 'cmp'));
Or if you would prefer to use joins, I think this should work for you.
$users = DB::table('posts')
->select(DB::raw('MAX(`posts`.`created_at`) AS `created_at`, `user_id`, `users`.*'))
->orderBy('posts.created_at', 'desc')
->groupBy('posts.user_id')
->join('users', 'users.id', '=', 'posts.user_id')
->get();

Related

Get data & Ignore null value when using group by in laravel [duplicate]

This question already has answers here:
GROUP BY - do not group NULL
(6 answers)
Closed 1 year ago.
I want to group by the data based on content_related_group. But there's a condition where content_related_group is null, and I don't want it to group the data with null value.
I've tried like this:
$products = DB::table('product')
->select('*', DB::raw('count(*) as total'))
->orderBy('created_at', 'desc')
->groupBy('content_related_group')
->paginate(9);
It's working, but it grouping the null data into one. I don't want it, what I want is to get all data from database and only group the the data with same content_related_groupinto one.
Example data:
id | content_related_group
1 | content_1
2 | content_1
3 | null
4 | null
Result (the null data keep separated from each other & the same content_related_group is grouped:
id | content_related_group
1 | content_1
2 | null
3 | null
It is possible? Thanks
try this if you want to remove the null rows:
$products = DB::table('product')
->select('*', DB::raw('count(*) as total'))
->whereNotNull('content_related_group')
->orderBy('created_at', 'desc')
->groupBy('content_related_group')
->paginate(9);
try this if you want to keep null value grouped to one row
$x_products = DB::table('product')->whereNull('content_related_group');
$products = DB::table('product')
->whereNotNull('content_related_group')
->union($x_products)
->select('*', DB::raw('count(*) as total'))
->orderBy('created_at', 'desc')
->groupBy('content_related_group')
->paginate(9);

Laravel - Eloquent select with count some related data

I have following query:
Product::where('shop_id', $shop->id)->with('orderItems')->get()
Product: id | title
OrderItem: id | product_id | sell_price | sold_quantity | sold_date
And i would like to count total income (what is sell_price * sold_quantity) and the same for last 30 days.
How to get all products for shop and calculate that data for every single one.
Thank you.
DB::table('products as p')
->join('orderItems as oi','p.id','oi.product_id')
->where('p.shop_id', $shop->id)
->whereDate('created_at', '>=', Carbon::now()->subDays(30)->toDateTimeString())
->selectRaw('p.id,p.name, sum(oi.sell_price * oi.sold_quantity) as total,
(select sum(sell_price * sold_quantity) from orderItems
where product_id = p.id) as grand_total')
->groupByRaw('p.id,p.name')
->get();

Laravel leftJoin only last record of right table and order by it

I have two tables. 1) products 2) prices
-------------------------
- products -
-------------------------
- id | int -
- name | varchar -
- created_at | datetime -
- updated_at | datetime -
-------------------------
----------------------------
- prices -
----------------------------
- id | int -
- product_id | int -
- sale_price | int -
- regular_price | int -
- created_at | datetime -
- updated_at | datetime -
-----------------------------
I want to search through products and get the last price of each product from prices table. I use this :
class Product extends Model
{
public function lastPrice()
{
return $this->hasOne(Price::class)->orderBy('id', 'DESC');
}
}
And I get products list with the last price using :
$products=Product::with('lastPrice')->paginate(9);
My question is: I want to order the result by highest/lower price. How I will do that?
You can select latest row from prices table along with product data there are 2 ways which I can think of right now as
// Approach 1
Product::join('prices as a', 'products.id', '=', 'a.product_id')
->leftJoin('prices as a1', function ($join) {
$join->on('a.product_id', '=', 'a1.product_id')
->whereRaw(DB::raw('a.id < a1.id'));
})
->whereNull('a1.product_id')
->select('products.*', 'a.*')
->orderBy('sale_price','desc')
->orderBy('regular_price','desc')
->get();
// Approach 2 with whereExists
Product::join('prices as a', 'products.id', '=', 'a.product_id')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('prices as b')
->whereRaw(DB::raw('a.product_id = b.product_id'))
->havingRaw('max(b.id) = a.id');
})
->select('products.*', 'a.*')
->orderBy('sale_price','desc')
->orderBy('regular_price','desc')
->get();
I believe using laravel 6 or above you can use correlated sub queries in addSelect() clause as
Product::addSelect(['latest_price' =>
Price::select('price')
->whereColumn('product_id', 'products.id')
->orderBy('id', 'desc')
->limit(1)
])->orderBy('latest_price','desc')
->paginate(9);
So from above we will get products data along with latest price column from database so you can apply sorting on latest_price
Edit If you have limited columns to select like sale_price you can add another sub query but if you want to select whole row from price table then see another approach using join/exists
Product::addSelect(['sale_price' =>
Price::select('sale_price')
->whereColumn('product_id', 'products.id')
->orderBy('id', 'desc')
->limit(1),
'regular_price' =>
Price::select('regular_price')
->whereColumn('product_id', 'products.id')
->orderBy('id', 'desc')
->limit(1),
])->orderBy('sale_price','desc')
->orderBy('regular_price','desc')
->get();

Filter query in Meta table Laravel

users Tabel
**id|name**
1|xx
users_meta Table
id | user_id | metaKey | metavalue
1 | 1 | city | kolkata
2 |2 |city | london
3 |8 |city |london
My moto is to return users belongs to city London.How can i achive this??
You can query using whereHas:
User::whereHas('users_meta', function ($query) {
$query->where('metavalue', 'london');
})->get();
Lets say u have a relationship with Users and user_meta
Than simply
User::with(['userMeta' => function($query){
$query->where('metaKey', 'city')
$query->where('metavalue', 'london')
}]);
Hope this helps

Laravel running multiple queries instead of join in Eloquent

I am trying to run join between my pages table and users table. Means one user can create multiple pages and in pages table created_by column belongs to my users table column id.
Table Structure:
---------------------------------------------
id | page_title | page_desc | created_by |
---------------------------------------------
1 | Testing | Descripti | 1 |
---------------------------------------------
2 | New Page | Desc | 2 |
User table
-------------------------------------------
id | name | email | pass | created_at |
-------------------------------------------
1 | A | a#g.c | 123 | 2017-10-21 |
-------------------------------------------
2 | B | b#g.c | 123 | 2017-10-21 |
in my Page model i used:
public function createdBy(){
return $this->belongsTo('App\User','created_by','id');
}
now when i am trying to get the data with:
$model = Page::all()
foreach($model as $key => $value){
echo $value->createdBy->name."<br/>";
}
laravel generating multiple queries to get name of each user is that any way to run the join instead of multiple queries?
You're running into the N+1 problem. You need to eager load your relationship to resolve this. You can read more on eager loading here.
Use the with() method on the page query to eager load all the related user records for each page:
$model = Page::with('createdBy')->get();
foreach ($model as $key => $value) {
echo $value->createdBy->name."<br/>";
}
This will now only run 2 queries: one to get all the pages, and one to get all the users related to each page.
For that reason Laravel provides eager Loading
Eager Loading
When accessing Eloquent relationships as properties, the relationship data is "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model
For more info just check this link.
OR Just Use Laravel's database query builder
You can perform join directly on your Table and fetch directly the data
$users = DB::table('pages')
->join('users', 'pages.created_by', '=', 'users.id')
->select('users.name','users.email', 'pages.*') //Specify what parameters you want.
->get();
To Join queries you can use laravel query builder like below
$pages = DB::table('pages')
->join('users', 'pages.created_by', '=', 'users.id')
->select('users.*', 'pages.*')
->get();

Resources