Laravel Eloquent counts with multiple ranges of data - laravel

I'm trying to figure out how to create Laravel query that will produce me with the following mysql query.
select
count(CASE WHEN points BETWEEN 0 AND 1.99 THEN 1 END) as count0_199,
count(CASE WHEN points BETWEEN 2 AND 3.99 THEN 1 END) as count2_399,
count(CASE WHEN points BETWEEN 4 AND 5.99 THEN 1 END) as count4_599
from
stats
Is it possible to make it in Eloquent? Haven't seen it in the documentation.
Thanks

From my knowledge using eloquent to perform this will involve some formatting of returned data:
$results = [];
$stats = Stat::where('points', '>=', '0')
->where('points', '<=', 5.99)
->get()
->each(function ($stat, $key) use (&$results) {
if ($stat->points >= 0 && $stat->points <= 1.99) {
$results['count0_199'][] = $stat;
}
if ($stat->points >= 2 && $stat->points <= 3.99) {
$results['count2_399'][] = $stat;
}
if ($stat->points >= 4 && $stat->points <= 5.99) {
$results['count4_599'][] = $stat;
}
});
return $results;
In the code above, all stats where points are between 0 and 5.99 are returned (as they will be returned anyway). The returned collection is then looped through to populate a $results array which will group the returned data.
There is also the DB Facade:
$query = [
'count(CASE WHEN points BETWEEN 0 AND 1.99 THEN 1 END) as count0_199',
'count(CASE WHEN points BETWEEN 2 AND 3.99 THEN 1 END) as count2_399',
'count(CASE WHEN points BETWEEN 4 AND 5.99 THEN 1 END) as count4_599'
];
$results = DB::select(implode(',', $query) . ' from stats');

Related

how to make orderBy ignore value 0 in eloquent laravel

My problem is that I have my collection that has a position field and I want it to be sorted in ascending order, but the fields that have the value null or 0 by default are detected as smaller than those that have an index.
My question is how can I make orderBy ignore the value 0 or null.
$listing = Product::get();
$listing = $listing->orderBy('order','ASC');
You could use CASE in your orderBy as a hack to "ignore" 0 (place it last).
$listing = Product::query()
->orderByRaw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END DESC, "order" ASC')
->get();
You can also split it if you prefer.
$listing = Product::query()
->orderByRaw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END DESC')
->orderBy('order')
->get();
$listing = Product::query()
->orderByDesc(DB::raw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END'))
->orderBy('order')
->get();
$listing = Product::orderByRaw('-order DESC')->get();
There is a minus sign before the column.
Instead of asc, we are now sorting as desc, this is because we have inverted the values in #2 and so the sorting must also be inverted now to get the right results.
this working fine for me.

How to take count based on different where conditions in laravel?

I am trying to count tickets based on different where conditions . For which I am using four different queries but same model. Can I do that in one query?
$openTickets = Ticket::where('status',1)->count();
$pending = Ticket::where('status',2)->count();
$unAssigned = Ticket::where('agent_id',null)->count();
$unResolved = Ticket::whereNotIn('status',[3,4])->count();
Ticket::selectRaw('COUNT(CASE WHEN status = 1 THEN 1 END) AS open_tickets')
->selectRaw('COUNT(CASE WHEN status = 2 THEN 1 END) AS pending_tickets')
->selectRaw('COUNT(CASE WHEN agent_id IS NULL THEN 1 END) AS unassigned_tickets')
->selectRaw('COUNT(CASE WHEN status NOT IN (3,4) THEN 1 END) AS unresolved_tickets')
->first();
You can of course resolve the multiple queries with this query. We can use conditional cases and count.
You can sum up conditionals but will need lots of raw parts in your query:
$result = DB::table('tickets')->whereIn('status', [1,2])->orWhereNull('agent_id')->orWhereNotIn('status', [3,4]) // This is to filter out the things we don't care about
->select([
DB::raw('SUM(IF(status = 1, 1,0)) as countOpen'),
DB::raw('SUM(IF(status = 2, 1,0)) as countPending'),
DB::raw('SUM(IF(agent_id IS NULL, 1,0)) as countUnassigned'),
DB::raw('SUM(IF(status NOT IN (3,4), 1,0)) as countUnresolved'),
])->first()
$openTickets = $result->countOpen;
$pending = $result->countPending;
$unAssigned = $result->countUnassigned;
$unResolved = $result->countUnresolved;

How to get last 7 days records with 0 counts

I have an eloquent query that gets the total count records (created_at) of the last 7 days. But the problem is if one of these days have 0 records, this doesn't appear in the final data.
My query:
$data = Data::whereBetween('created_at', [Carbon::now()->subDays(6)->format('Y-m-d')." 00:00:00", Carbon::now()->format('Y-m-d')." 23:59:59"])
->groupBy('date')
->orderBy('date')
->get([
DB::raw('DATE(created_at) as date'),
DB::raw('count(*) as total')
])
->pluck('total', 'date')->toArray();
What I get:
[
"2020-04-14" => 1
"2020-04-16" => 1
"2020-04-18" => 1
"2020-04-19" => 1
]
What I expected:
[
"2020-04-14" => 1
"2020-04-15" => 0
"2020-04-16" => 1
"2020-04-17" => 0
"2020-04-18" => 1
"2020-04-19" => 1
"2020-04-20" => 0
]
Any suggestions?
SOLUTION:
-Based on Gary Houbre's proposal:
$results = Data::whereBetween('created_at', [Carbon::now()->subDays(6)->format('Y-m-d')." 00:00:00", Carbon::now()->format('Y-m-d')." 23:59:59"])
->groupBy('date')
->orderBy('date')
->get([
DB::raw('DATE_FORMAT(created_at, "%Y-%m-%d") as date'),
DB::raw('count(*) as total')
])
->keyBy('date')
->map(function ($item) {
$item->date = Carbon::parse($item->date);
return $item;
});
$period = new DatePeriod(Carbon::now()->subDays(6), CarbonInterval::day(), Carbon::now()->addDay());
$graph = array_map(function ($datePeriod) use ($results) {
$date = $datePeriod->format('Y-m-d');
return $results->has($date) ? $results->get($date)->total : 0;
}, iterator_to_array($period));
Looking directly Sql : How to include "zero" / "0" results in COUNT aggregate?
Into a same table : How to get the record if Count is zero in Laravel
You need to add an outer join into your request with Eloquent.
My idea is to create a for loop to check the days.
If there is no record on a date then print 0
Loop Iteration:
Catch the first Day (Suppose 14)
Catch the last Day
Then check in every iteration it is greater than one or many
Thus, I hope you will get normally.
We had a similar problem while trying to put back-end data into the chart. Since some of the days were missing it didn't look well. Our solution was;
Create a function like this;
public function generateDates(Date $startDate, Date $endDate, $format = 'Y/m/d'): Collection
{
$dates = collect();
$startDate = $startDate->copy();
for ($date = $startDate; $date->lte($endDate); $date->addDay()) {
$dates->put($date->format($format), 0);
}
return $dates;
}
In your case it's going to be (today and today - six days) and you will union returning collection with your query collection. What it does is; it create a date range from the keys and fill them with zero. When your query collection has some value other than zero - it is going to overwrite it.

Query Condition if nothing add 0

I have a Laravel 5.7 query
$captains = DB::table('pickup_results')
->select(DB::raw("playerID"),
DB::raw("COUNT(CASE WHEN gameResult = 'Win' THEN 1 END) AS wins"),
DB::raw("COUNT(CASE WHEN gameResult = 'Loss' THEN 1 END) AS loss"))
->where('pickOrder', '=', 0)
->where('playerID', '=', $playerID)
->groupBy('playerID')
->orderBy('wins','DESC')
->get();
If the playerID doesn't have a "Win" or a "Loss" it wont add the key of wins or loss to the query results -- thus resulting in errors when a user has no wins or no loss's in the database.
I Would like to be able to default wins or loss to 0 when a value doesn't exist in the database.
Can someone help me understand how I would achieve?
Assuming that there isn't going to be a row in the table if the player hasn't had a win or a loss then your query isn't ever going to return any results.
One option (assuming you have a players table) would be to query the players table and then left join the pickup_results:
$captains = DB::table('players')
->leftJoin('pickup_results', 'players.id', 'pickup_results.playerID')
->select('users.id as user_id',
DB::raw("COUNT(CASE WHEN results.gameResult = 'Win' THEN 1 END) AS wins"),
DB::raw("COUNT(CASE WHEN results.gameResult = 'Lose' THEN 1 END) AS loss"))
->where('players.id', $playerID)
->where('pickup_results.pickOrder', 0)
->groupBy('players.id')
->orderBy('wins', 'DESC')
->get();
Alternatively, since you're using get() with your query a collection will be returned regardless of any results so you could chain on the whenEmpty() method instead:
$captains = DB::table('pickup_results')
->select('playerID',
DB::raw("COUNT(CASE WHEN gameResult = 'Win' THEN 1 END) AS wins"),
DB::raw("COUNT(CASE WHEN gameResult = 'Loss' THEN 1 END) AS loss"))
->where('pickOrder', 0)
->where('playerID', $playerID)
->groupBy('playerID')
->orderBy('wins', 'DESC')
->get()
->whenEmpty(function ($items) use ($playerID) {
return $items->push((object)[
'playerID' => $playerID,
'wins' => 0,
'loss' => 0,
]);
});
You can use SUM() instead of COUNT() and add an ELSE 0 to the query. Example for the first occurance:
DB::raw("SUM(CASE WHEN gameResult = 'Win' THEN 1 ELSE 0 END) AS wins")
Or you can use COALESCE(query, 0):
DB::raw("COALESCE(COUNT(CASE WHEN gameResult = 'Win' THEN 1 END), 0) AS wins")
This works because COUNT() returns NULL if all counted elements are NULL.
It will give you wins and loss in your result. Just add an else condition in your case and put an extra count condition for draw as well. Add more than 1 orderBy for better representation of results.
$captains = DB::table('pickup_results')
->select(DB::raw("playerID"),
DB::raw("SUM(CASE WHEN gameResult = 'Win' THEN 1 ELSE 0 END) AS wins"),
DB::raw("SUM(CASE WHEN gameResult = 'Loss' THEN 1 ELSE 0 END) AS loss")),
DB::raw("SUM(CASE WHEN gameResult != 'Win' and gameResult != 'Loss' THEN 1 ELSE 0 END) AS draws"))
->where('pickOrder', '=', 0)
->where('playerID', '=', $playerID)
->groupBy('playerID')
->orderBy('wins','DESC')
->orderBy('draws','DESC')
->orderBy('loss','ASC')
->get();

Laravel eloquent query order by customized field

I have this raw mysql query
SELECT campaign_influencers.*,
CASE influencer_status
WHEN 'PENDING' THEN 0
WHEN 'ACTIVE' THEN 1
WHEN 'LIVE' THEN 2
WHEN 'ACCEPTED' THEN 3
ELSE 5
end AS order_field
FROM campaign_influencers
WHERE campaign_id = 612
ORDER BY order_field
How can this be converted to eloquent query builder?
This is what I have done so far.
$sql = "SELECT campaign_influencers.*,
CASE influencer_status
WHEN 'PENDING' THEN 3
WHEN 'ACTIVE' THEN 1
WHEN 'LIVE' THEN 2
WHEN 'ACCEPTED' THEN 0
ELSE 5
end AS order_field
FROM campaign_influencers
WHERE campaign_id = :campaignId
ORDER BY order_field";
$campaignInfluencers = DB::select( DB::raw($sql), array(
'campaignId' => $id
));
Only issues is that the relationship object is gone.
foreach ($campaignInfluencers as $campaignInfluencer) {
$user = $campaignInfluencer->user; //will not work
}
Use this:
CampaignInfluencer::select('*', DB::raw('CAST influencer_status ... AS order_field'))
->where('campaign_id', $campaign_id)
->orderBy('order_field')
->get();
You could shorten your query by using FIND_IN_SET
SELECT campaign_influencers.*,
FIND_IN_SET(influencer_status,'ACCEPTED,LIVE,ACTIVE,PENDING') AS order_field
FROM campaign_influencers
WHERE campaign_id = 612
ORDER BY order_field DESC
Using eloquent as #Jonas Staudenmeir already suggested you need to use raw method for raw sql expressions
YourModel::select('*', DB::raw("FIND_IN_SET(influencer_status,'ACCEPTED,LIVE,ACTIVE,PENDING') AS order_field"))
->where('campaign_id', 612)
->orderBy('order_field', 'desc')
->get();

Resources