I have a query result with the selected DB raw result and I need to add another where between clause to that query with the selected DB raw result. my query is
$products = Product::leftJoin('product_reviews', 'product_reviews.product_id', '=', 'products.id')
->select(
'products.*',
DB::raw("IF(SUM(product_reviews.star_rating) != 0, SUM(product_reviews.star_rating) / COUNT(product_reviews.id), 0) AS rate")
)
->get();
I need to add ->whereBetween('rate', [4, 5]) like this to my query. How can I do that?
You must use ->havingBetween('rate', [4, 5])
$products = Product::leftJoin('product_reviews', 'product_reviews.product_id', '=', 'products.id')
->select(
'products.id',
DB::raw("IF(SUM(product_reviews.star_rating) != 0, SUM(product_reviews.star_rating) / COUNT(product_reviews.id), 0) AS rate")
)
->groupBy('products.id')
->havingBetween('rate', [4, 5])
->get();
Online Laravel query builder test
If whereBetween('rate', [4, 5]) doesn't work, I think you could do one of the following:
You could use a subquery
$sub = DB::query()
->select(
'products.*',
DB::raw("IF(SUM(product_reviews.star_rating) != 0, SUM(product_reviews.star_rating) / COUNT(product_reviews.id), 0) AS rate")
)
->from('products')
->leftJoin('product_reviews', 'product_reviews.product_id', '=', 'products.id');
$products = Product::query() // using Product:: instead of DB:: to cast every result to a model.
->fromSub($sub, 'products')
->whereBetween('rate', [4, 5]);
->get();
Filter after getting the results
$products = Product::query()
->select(
'products.*',
DB::raw("IF(SUM(product_reviews.star_rating) != 0, SUM(product_reviews.star_rating) / COUNT(product_reviews.id), 0) AS rate")
)
->leftJoin('product_reviews', 'product_reviews.product_id', '=', 'products.id')
->cursor()
->filter(fn($product) => $product->rate >= 4 && $product->rate <= 5)
->values()
->collect();
Or use DB::raw as the first argument of whereBetween
$products = Product::query()
->select(
'products.*',
DB::raw("IF(SUM(product_reviews.star_rating) != 0, SUM(product_reviews.star_rating) / COUNT(product_reviews.id), 0) AS rate")
)
->whereBetween(
DB::raw('IF(SUM(product_reviews.star_rating) != 0, SUM(product_reviews.star_rating) / COUNT(product_reviews.id), 0)'),
[4, 5]
)
->leftJoin('product_reviews', 'product_reviews.product_id', '=', 'products.id')
->get();
```
Related
I have this query:
$counts = DB::table('projects')
->join('prject_links', 'prject_links.project_id', '=', 'projects.id')
->join('links', 'links.id', '=', 'prject_links.link_id')
->join('segments', 'segments.id', '=', 'links.segment_id')
->join('hthree_regions', 'hthree_regions.id', '=', 'segments.hthree_id')
->join('areas', 'areas.id', '=', 'hthree_regions.area_id')
->join('zone_regions', 'zone_regions.id', '=', 'areas.zone_id')
->select(
'zone_regions.id as regions',
'areas.id as provinces',
'hthree_regions.id as cities',
'segments.id as segments'
)
->get();
what I'm looking to have as result is something like:
counts => [
regions => 20,
provinces => 10,
cities => 7,
segments => 5
];
What do i get currently is confusing result like:
any idea about how to fix my query?
Use groupBy and count(distinct field) to get the field's count in each project:
->selectRaw(
"CONCAT('project ', projects.id) as project,
COUNT(DISTINCT zone_regions.id) as regions,
COUNT(DISTINCT areas.id) as provinces,
COUNT(DISTINCT hthree_regions.id) as cities,
COUNT(DISTINCT segments.id) as segments"
)
->groupBy('projects.id')
->get();
This might be your solution. Run it and let me know if that helps
$counts = DB::table('projects')
->join('prject_links', 'prject_links.project_id', '=', 'projects.id')
->join('links', 'links.id', '=', 'prject_links.link_id')
->join('segments', 'segments.id', '=', 'links.segment_id')
->join('hthree_regions', 'hthree_regions.id', '=', 'segments.hthree_id')
->join('areas', 'areas.id', '=', 'hthree_regions.area_id')
->join('zone_regions', 'zone_regions.id', '=', 'areas.zone_id')
->groupBy('projects.id')
->selectRaw(
'count(zone_regions.id) as regions',
'count(areas.id) as provinces',
'count(hthree_regions.id) as cities',
'count(segments.id) as segments'
)
->get();
I need to do select as the following
select * from table where (x = 1 OR y = 1) AND starting_at < '2018-05-01'
How to do that please.
DB::table('tableName')
->where(function ($query) {
$query->where('x', '=', 1)
->orWhere('y', '=', '1');
})
->where('starting_at', '<', '2018-05-01')
->get();
I'am trying to create a morrisJS line chart that shows data per month.
So i get the data per month like this:
$intakes = Intakes::whereYear('created_at', '=', 2018)
->orWhere(function ($query) {
$query->whereMonth('created_at', '=', 1)
->whereMonth('created_at', '=', 2)
->whereMonth('created_at', '=', 3)
->whereMonth('created_at', '=', 4)
->whereMonth('created_at', '=', 5)
->whereMonth('created_at', '=', 6)
->whereMonth('created_at', '=', 7)
->whereMonth('created_at', '=', 8)
->whereMonth('created_at', '=', 9)
->whereMonth('created_at', '=', 10)
->whereMonth('created_at', '=', 11)
->whereMonth('created_at', '=', 12);
})
->get();
But that would return all the records, in the range from month 1 to 12. But what i want is the data per month, can this be achieved apart from just creating multiple variables like this ?
$intakesMonth1 = Intakes::whereYear('created_at', '=', 2018)
->whereMonth('created_at', '=', 1)
->get();
$intakesMonth2 = Intakes::whereYear('created_at', '=', 2018)
->whereMonth('created_at', '=', 2)
->get();
I hope that i'm clear enough and someone knows a better solution.
If you want an Eloquent solution, use whereMonth():
for($i = 1; $i < 13; $++) {
$intakes[$i] = Intakes::whereMonth('created_at', $i)->get();
}
You can also, get all intakes for the year and then use where() Laravel Collections method to get intakes for one month only:
$oneMonthData = $collection->where('created_at', '>', Carbon::parse($someDateString)->startOfMonth())->where('created_at', '<', Carbon::parse($someDateString)->endOfMonth());
$intakes = Intakes::whereDate('created_at', '<=', '01-01-2018 00:00:00')->whereDate('created_at', '<=', '01-02-2018 23:59:59')->get();
I'm looking to reduce the my query size in laravel.
My query looks something like this (I shortened it, it's about 10 times this amount of lines):
$users = User::where("interface_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("interface_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("web_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("web_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("illustration_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("illustration_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("brush_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("brush_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("typography_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("typography_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("identity_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("identity_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("vector_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("vector_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orderBy($orderByString, 'desc')
->paginate(1);
As you can see, it's a bit redundant.
For every art type, I'm looking to get users by the role of "2", if their commstatus is equal to "1" or "$unavailable".
At first, I tried to shorten it by not adding "role" or "commstatus" at the end of each "where" clause, and at the bottom writing another $users = $users::where("role", "=", "2"), for example, but I can't seem to be able to find the right syntax that.
Is there any way to shorten this query?
You certainly shouldn't need to duplicate where('role', '=', 2)->where('commstatus', '=', $unavailableCheck) for every single type of art, as they're effectively ANDed conditions; and consider whereIn('commstatus', [$unavailableCheck, 1]) rather than having two equality checks.
Something like:
$users = User::where('role', '=', 2)
->whereIn('commstatus', [$unavailableCheck, 1])
->where("interface_art", '=', 1)
->orWhere("web_art", '=', 1)
->orWhere("illustration_art", '=', 1)
->orWhere("brush_art", '=', 1)
->orWhere("typography_art", '=', 1)
->orWhere("identity_art", '=', 1)
->orWhere("vector_art", '=', 1)
->orderBy($orderByString, 'desc')
->paginate(1);
As others have stated, your schema would likely benefit from refactoring towards normalization. However, I believe you can still refactor your existing query to be less redundant and more readable.
Laravel has the ability to handle Advanced Where Clauses which include nested parameter groupings. From the documentation:
The Closure will receive a query builder instance which you can use to set the constraints that should be contained within the parenthesis group.
With that in mind, you should be able to refactor the query like this:
$users = User::whereIn('commstatus', [$unavailableCheck, 1])
->where('role', 2)
->where(function ($query) {
$query->where("interface_art", 1)
->orWhere("web_art", 1)
->orWhere("illustration_art", 1)
->orWhere("brush_art", 1)
->orWhere("typography_art", 1)
->orWhere("identity_art", 1)
->orWhere("vector_art", 1);
})
->orderBy($orderByString, 'desc')
->paginate(1);
That will create a SQL query that does:
SELECT * FROM users
WHERE commstatus IN ($unavailableCheck, 1)
AND role = 2
AND (interface_art = 1 OR illustration_art = 1 OR ... etc)
I have a table of Weeks that is joined to a property table, Weeks table looking like this:-
PropID, WeekDate, Available
1 , 2015-07-04, Yes
1 , 2015-07-11, Yes
1 , 2015-07-18, No
2 , 2015-07-04, Yes
2 , 2015-07-11, No
2 , 2015-07-18, No
I want to select properties where both the weeks of the 4th and 11th are available. In the example above, I want to return two rows with PropID 1 as both are available and no rows from PropID 2 as only one of the weeks are available.
I've tried various ways, but either get nothing or always return the 1st, 2nd and 4th rows.
I think this is close, but it's still missing something as it is looking for dates that are <= AND >=
$query = Property::whereHas('Week', function($q) use ($arrive)
{
$q->where(function($sub)
{
$sub->where('WeekDate', '>=', '2015-07-04');
$sub->where('WeekDate', '<=', '2015-07-11');
});
$q->where('Available', '=', 'Yes');
})
->get();
Not sure this helps, but the Property table is simply
PropID, PropName
1 , Property 1
2 , Property 2
Just found that this SQL works.
SELECT PropID FROM tblweeks WHERE WeekDate IN ('2015-07-04', '2015-07-11') AND Available = 'yes' GROUP BY PropID HAVING COUNT(*) = 2
This will give your result as Property 1 only:
$weeks = Property::whereHas('Week', function ($q) {
$q->where(function ($sub) {
$sub->whereIn('WeekDate', array('2015-07-04', '2015-07-11'));
$sub->where('Available', '=', 'y');
});
$q->groupBy('property_id');
$q->having('count(*)', '=', '2');
})->get();
There are some changes i have did in your query :
Please check this solution
$query = Property::whereHas('Week', function($q) use ($arrive)
{
$q->where('WeekDate', '=', '2015-07-04');
$q->orWhere('WeekDate', '=', '2015-07-11');
$q->where('Available', '=', 'Yes');
})
->get();
This should work and will return desire output
There are some changes, the query might be using group by query :
Please check this solution
$query = Property::whereHas('Week', function($q) use ($arrive)
{
$q->where('WeekDate', '=', '2015-07-04');
$q->orWhere('WeekDate', '=', '2015-07-11');
$q->where('Available', '=', 'Yes');
$q->group('WeekDate');
})
->get();
This should work and will return desire output
You actually need two whereHas for this:
$query = Property::whereHas('Week', function($q) use ($arrive)
{
$q->where('WeekDate', '>=', '2015-07-04');
$q->where('Available', '=', 'Yes');
})
->whereHas('Week', function($q) use ($arrive)
{
$q->where('WeekDate', '<=', '2015-07-11');
$q->where('Available', '=', 'Yes');
})
->get();
I believe you do not need the second nested query.
$query = Property::whereHas('Week', function($q) use ($arrive)
{
$q->where('WeekDate', '>=', '2015-07-04');
$q->where('WeekDate', '<=', '2015-07-11');
$q->where('Available', '=', 'Yes');
})
->get();
Updated
Have you looked into whereBetween.
$query = Property::whereHas('Week', function($q) use ($arrive)
{
$q->whereBetween('WeekDate', '2015-07-04', '2015-07-11');
$q->where('Available', '=', 'Yes');
})
->get();