I have many projects and each has many orders, some completed, some not.
I want to order them by the amount of completed orders like this:
$products = $products->orderBy(function($product) {
return $product->orders->where('status', 2)->count();
})->paginate(15);
I know the order call doesn't work like this but this is the best was show the problem. SortBy doesn't work because I want to use pagination.
Finally found a good solution for the problem:
$products = $products->join('orders', function ($join) {
$join->on('orders.product_id', '=', 'products.id')
->where('orders.status', '=', 2);
})
->groupBy('products.id')
->orderBy('count', $order)
->select((['products.*', DB::raw('COUNT(orders.product_id) as count')]))->paginate(50);
Try this if it works:
$categories = Prodcuts::with(array('orders' => function($query) {
$query->select(DB::raw('SELECT * count(status) WHERE status = 2 as count'))
$query->orderBy('MAX(count)')
}))->paginate(15);
return View::make('products.index', compact('categories'));
Note: This is not tested.
From 5.2 on wards you can use the withCount for counting relationship result.
In this case the
$products = $products->withCount(['orders' => function ($query) {
$query->where('status', 2);
}]);
Reference : https://laravel.com/docs/5.2/eloquent-relationships
Related
here I'd like to find the solution to simplify my query to get data using eloquent in Laravel.
$room_id = Booking::whereBetween('from', [$request->from, $request->to])
->orWhereBetween('to', [$request->from, $request->to])
->where('from', '<=', $request->from, )
->where('to', '>=', $request->from)
->pluck('room_id');
$rooms = Room::whereNotIn('id', $room_id )->get();
So here I have 2 Eloquent operations to get Rooms which not included in the Booking Table with specified requirements. So far I have no problem with it, but can you guys give me best practice to simplify from what I do? Thank you.
Make sure that 'bookings' relation is written on your Room model.
$rooms = Room::whereDoesntHave('bookings', use($request) function($q){
$q->whereBetween('from', [$request->from, $request->to])
$q->orWhereBetween('to', [$request->from, $request->to])
$q->where('from', '<=', $request->from, )
$q->where('to', '>=', $request->from)
})->get();
Your can refer laravel relationship to add it in model and after that using whereHas to query join table:
https://laravel.com/docs/9.x/eloquent-relationships
Example:
With options
protected $with = [
'product_savour'
];
Relationship
public function product_savour()
{
return $this->hasMany(ProductSavour::class, 'product_id');
}
Query
$productQuery->whereHas('product_savour', function ($query) use ($filters) {
$query->whereHas('savour', function ($query) use ($filters) {
$query->whereHas('type', function ($query) use ($filters) {
$query->whereIn('id', $filters['savour']);
});
});
});
Right now I have a subquery to get the count of payments for the current month and then getting the 4 products with the highest payment_count. This query works fine but I'm wondering if there's a more simple way to do the same since its getting difficult to read.
$latestPayments = DB::table('payments')
->select('product_id', DB::raw('COUNT(*) as payments_count'))
->whereMonth('created_at', Carbon::now()->month)
->groupBy('product_id');
$trendingProducts = DB::table('products')
->joinSub($latestPayments, 'latest_payments', function ($join) {
$join->on('products.id', '=', 'latest_payments.product_id');
})->orderBy('payments_count', 'DESC')->take(4)->get();
This did it!
$trendingProducts = Product::withCount(['payments' => function($query) {
$query->whereMonth('created_at', Carbon::now()->month);
}])->orderBy('payments_count', 'DESC')->take(4)->get();
If you are using eloquent query with relational database you can do like this:
$latestPaymentWithTrendingProduct = App\Payment::with(['products', function($product) {
$product->orderBy('payments_count', 'DESC')->take(4);
}])->whereMonth('created_at', date('m'))->get()->groupBy('product_id');
This will lessen the code but still do the same thing.
I have created many-to-many relation using belongsToMany function:
class Doctor extends Model
{
...
public function categories()
{
return $this->belongsToMany('App\Category', 'doctors_to_categories', 'doctor_id', 'category_id');
}
...
}
Now I want to create query with many-to-many condition. In SQL in would be:
SELECT *
FROM `doctors`
JOIN `doctors_to_categories`
ON `doctors_to_categories`.`doctor_id` = `doctors`.`id`
WHERE `doctors_to_categories`.`category_id` = 1
I have tried to achieve this like:
$doctors = Doctor::with(['categories' => function($query) {
$query->where('category_id', '=', 1);
}])->get();
Or
$doctors = Doctor::with(['categories' => function($query) {
$query->where('categories.id', '=', 1);
}])->get();
But it is not working. Any ideas how it should be? Thanks for any help.
The with() function does not actually introduce a join in your query, it just loads the relation of all models as a second query. So the with() function couldn't possibly change the original result set.
What you are looking for is whereHas(). This will add a WHERE EXISTS clause to the existing query.
$doctors = Doctor::with('categories')->whereHas('categories', function ($query) {
$query->where('categories.id', 1);
})->get();
Using ->with() doesn't actually limit the results of the Doctor::...->get() query; it simply tells Laravel what to return in the relationships attribute. If you actually want to enforce returning only Doctors that have a category 1 relationship, you need to use whereHas():
$doctors = Doctor::whereHas('categories', function($query) {
$query->where('categories.id', '=', 1);
// `id` or `categories.id` should work, but `categories.id` is less ambigious
})->get();
You can add whereHas condition for this. Try code below:
$doctors = Doctor::with('categories')->whereHas('categories', function($query) {
$query->where('id', 1);
})->get();
I have 2 models: TheSeries and TheEpisodes.
TheSeries has many TheEpisodes and TheEpisodes has one TheSeries.
I am trying to list all TheSeries and display latestEpisode in each, by using TheEpisodes.addDate.
The code I have right now is this:
$TheSeries = TheSeries::with('TheEpisodes');
What should I do to display only latest 1 episode for each TV serial?
EDIT
->take(1) and ->limit(1) do not work for TheEpisodes
EDIT (Latest Semi-Working Code)
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc');
}])->get()
This works, it returns the episodes in correct order but I am unable to limit the results to 1. This following codes don't work:
// Example 1
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc')->take(1);
}])->get()
// Example 2
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc')->limit(1);
}])->get()
// Example 3
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc')->first();
}])->get()
Those are the column names of the tables:
TheSeries - id, originalTitle, aliasTitle, description, imageURL, startingDate, endingDate, activeBool
TheEpisodes: id, seriesID, authorID, addDate, episodeVersion
Define a TheLatestEpisode hasOne relation on your TheSeries model:
class TheSeries extends Model
{
public function TheLatestEpisode()
{
return $this->hasOne(TheEpisode::class, 'seriesID')->orderBy('id', 'desc');
}
}
Then you can easily do:
$series = TheSeries::with('TheLatestEpisode')->get();
You can try it as:
$TheSeries = TheSeries::with(['TheEpisodes' => function($q) {
$q->orderBy('created_at', 'desc')->take(1);
}])
->get();
Or try with limit as:
$TheSeries = TheSeries::with(['TheEpisodes' => function($q) {
$q->orderBy('created_at', 'desc')->limit(1);
}])
->get();
$TheSeries = TheSeries::with(['TheEpisodes' => function($q) {
$q->orderBy('created_at', 'desc')->first();
}])
->get();
can't work?
Why don't you use the DB statement ?
DB::table('TheEpisodes')
->leftjoin('TheSeries','TheEpisodes.SeriesId','=','TheSeries.id')
->select('TheEpisodes.*','TheSeries.id as sId','TheSeries.name as sName',...)
->orderBy('TheEpisodes. addDate','desc')
->take(1)
->get();
You can try something like this in your TheSeries model: (it is easier)
public function getLatestEpisodeAttribute(){
$episode = TheEpisodes::where('series_id',$this->attributes['id'])
->latest()
->first();
if(!$episode){
return "no episodes for this Series";
}
return $episode;
}
On your controller just do the query normally without including anything related to TheSeries and you can access it values in your blade file like this:
//lets suppose there is a title attibute in the episodes
{{$TheSeries->latest_episode->title}}
//or a duration attribute
{{$TheSeries->latest_episode->duration}}
The best solution I found, was to create a one-to-one relationship with orderBy('addDate'), it works!!!
Reference: https://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/
$TheSeries = TheSeries::with('TheEpisodes')->first();
Or
$TheSeries = TheSeries::with('TheEpisodes')->firstOrFail();
I have two model User and Profile in one to one relationship.
I want to retrieve all user where profile.status == TRUE using following code.
$users = User::with(['profile' => function ($query) {
$query->where('status', TRUE);
}])->get();
dd(count($users)); //50
I have 50 users and only among of them only 3 has status == TRUE. But always it display 50.
You are getting 50 users because you are applying condition to profile. dd($user->profile) you will get only the records of the profile whose status is true.
Use whereHas():
$users = User::whereHas('profile', function ($query) {
$query->where('status', TRUE);
})->get();
dd(count($users));
If you want to make it work with single Query, you can use Query Builder join like
\DB::table('users')->join('profile', function ($join){
$join->on('users.id', '=', 'profile.user_id')->where('profile.status', '=',TRUE);
})->get();
You said you're having N+1 problem, so you need to use both whereHas() and with() like this to get users with profiles and to solve N+1 problem:
$users = User::whereHas('profile', function ($query) {
$query->where('status', TRUE);
})
->with('profile')
->get();