Count from each distinct date, fill in missing dates with zero - laravel

I'm trying to create an Eloquent query that gets the total number of posts made each distinct day, and if the date is missing, fill it in with a value of zero.
For example, if my table looks like this:
+----+---------------------+
| id | date |
+----+---------------------+
| 1 | 2015-01-01 00:00:00 |
| 2 | 2015-01-01 01:53:18 |
| 3 | 2015-01-01 02:41:26 |
| 4 | 2015-01-02 12:51:01 |
| 5 | 2015-01-05 08:24:12 |
+----+---------------------+
This would output:
2015-01-01 : 3
2015-01-02 : 1
2015-01-05 : 1
Notice, however, that the days 03-04 are missing. How can I include these dates, but give them the value 0 such that I end up with an output like:
2015-01-01 : 3
2015-01-02 : 1
2015-01-03 : 0
2015-01-04 : 0
2015-01-05 : 1
Here is my current query:
$posts = Post::select(array(
DB::raw('DATE(`created_at`) as `date`'),
DB::raw('COUNT(*)as `count`')
))
->where('created_at', '>', Carbon::today()->subWeek())
->groupBy('date')
->orderBy('date', 'DESC')
->lists('count', 'date');
Thanks!

In your SQL results you can generate some "fake-data" in your rows, but u can not generate "fake-rows", exept joining to some "fake(temporary)-table".
In your case ll be much easier to apply some logic around sql-result.
Replace your code with this:
$order = 'DESC';
$endDate = Carbon::today();
$startDate = Carbon::today()->subWeek();
$dateInc = ($order == 'DESC') ? -1 : 1;
$dateCycleHolder = clone ($dateInc > 0 ? $startDate : $endDate);
$dateCycleEnd = clone ($dateInc > 0 ? $endDate : $startDate);
$posts = Post::select(array(
DB::raw('DATE(`created_at`) as `date`'),
DB::raw('COUNT(*)as `count`')
))
->where('created_at', '>', $startDate)
->groupBy('date')
->orderBy('date', $order)
->lists('count', 'date');
$postsFinal = new \Illuminate\Database\Eloquent\Collection();
while ($dateCycleHolder->ne($dateCycleEnd)) {
$dateCurr = $dateCycleHolder->format('Y-m-d');
$postsFinal->put($dateCurr, $posts->get($dateCurr, 0));
$dateCycleHolder->addDay($dateInc);
}
$dateCurr = $dateCycleHolder->format('Y-m-d');
$postsFinal->put($dateCurr, $posts->get($dateCurr, 0));
$posts = $postsFinal;
its alittle bit flexible, you can change values of this things:
$order = 'DESC';
$endDate = Carbon::today();
$startDate = Carbon::today()->subWeek();

Related

Laravel calculate total balance

Here is my Table:
| id | type | balance
| ----|--------| -------
| 1 | credit | 2400
| 2 | credit | 4800
| 3 | debit | 1200
The calculated amount should be 6000. (2400 + 4800 - 1200) = 6000
How can I do this using Eloquent or collection?
Using laravel collection and one sql query.
return Model::all()->reduce(function ($carry, $item) {
return $item->type == 'credit'
? $carry + $item->balance : $carry - $item->balance;
},0);
You can do by this using Eloquent:
Credits
$totalCredits = Model::where('type', 'credit')->sum('balance');
Debits
$totalDebits = Model::where('type', 'debit')->sum('balance');
Balances
$Total = $totalCredits - $totalDebits
If you want SUM only then do this
DB::table("table")->get()->sum("balance")

Laravel mass update with different value based on condition

I have a table structure like:
id //primary_key
value
changed_value //on update value column should not be updated instead changed value should be updated
status // 0 - default, 1- updated
am_id //foreign_key
Now, to mass update I am doing as follows:
Model::where('am_id',$request->am_id)
->where('value',$request->value)
->update([
'changed_value' => '$request->value',
'status' => 1
]);
However, we should not set status as 1 blindly, as there is one condition. It's if the value of value column is equal to the $request->value the status should be 0 instead.
How this scenario could appear?
Initially, after inserting the first row looks like
+-----+-------+---------------+--------+-------+
| id | value | changed_value | status | am_id |
+-----+-------+---------------+--------+-------+
| 1 | 20 | null | 0 | 1 |
+-----+-------+---------------+--------+-------+
After 1st update
+-----+-------+---------------+--------+-------+
| id | value | changed_value | status | am_id |
+-----+-------+---------------+--------+-------+
| 1 | 20 | 40 | 1 | 1 | // changed_value - 40 , status 1
+-----+-------+---------------+--------+-------+
After 2nd Update (let's say the value is updated to 20), in this case as the value === changed_value the status should be updated as 0 not 1
+-----+-------+---------------+--------+-------+
| id | value | changed_value | status | am_id |
+-----+-------+---------------+--------+-------+
| 1 | 20 | 20 | 0 | 1 | // changed_value - 20 , status 0
+-----+-------+---------------+--------+-------+
This means, during the model update in below-mentioned code. I want to insert a condition, if( value == $request->value) status = 0 else status = 1
Model::where('am_id',$request->am_id)
->where('value',$request->value)
->update([
'changed_value' => '$request->value',
'status' => 1
]);
Alternatively you could always use raw sql if performance was an issue.
DB::unprepared('
update model
set status = case when (changed_value = "'.$request->value.'") then 0 else 1 end
and changed_value = "'.$request->value.'"
where value = "'.$request->value.'";
')
Edit: You can do it in Eloquent with 2 queries with a subquery to filter the ids of the already updated rows.
// get ids of all matching status 1 but don't execute the query
$matches = Model::select('id')->where('am_id',$request->am_id)->where('value',$request->value)
->where('changed_value', $request->value);
// change matching status 1
Model::whereIn('id', $matches)
->update([
'changed_value' => '$request->value',
'status' => 1
]);
// matching not in status 1
Model::where('am_id',$request->am_id)
->where('value',$request->value)
->where('changed_value', '!=', $request->value)
->whereNotIn('id', $matches)
->update([
'changed_value' => '$request->value',
'status' => 0
]);
Try this one.
$value = Model::where('am_id',$request->am_id)->first()->value;
if($request->value == $value){
$status = 1;
}else{
$status = 0;
}
Model::where('am_id',$request->am_id)
->update([
'changed_value' => '$request->value',
'status' => $status
]);
Unfortunately, Eloquent was not designed to handle this kind of mass update (where you want different conditions) with a single query. You would have to step through the models, or resort to the query builder.
$models = Model::where('am_id',$request->am_id)->get();
$models->map(function ($model) use ($request) {
// status
$status = $model->value === $request->value ? 0 : 1;
// update model
$model->update([
'changed_value' => $request->value,
'status' => $status
]);
return $model;
});
EDIT: There are some query builder ideas on this page that might allow you to make something reusable, instead of just writing raw SQL any time you need to do this.
https://github.com/laravel/ideas/issues/575#issuecomment-300731748

Laravel - Combine multiple queries as one and also include GroupBy

I have written different queries that function for different things. I want to combine them as one (Sales for: Today, Current Week, Current Week, Current Month, Current Year and Overall Sales) and Group By a field called store.
$currentYear = date('y');
$currentyearbilling = DB::table("billings")
->select(DB::raw("SUM(amount) as total"))
->whereRaw('YEAR(created_at) = ?',$currentYear)
->get();
$currentMonth = date('m');
$currentmonthbilling = DB::table("billings")
->select(DB::raw("SUM(amount) as total"))
->whereRaw('MONTH(created_at) = ?',$currentMonth)
->get();
$currentWeek = date('w');
$currentweekbilling = DB::table("billings")
->select(DB::raw("SUM(amount) as total"))
->whereRaw('WEEK(created_at) = ?',$currentWeek)
->get();
$currentDay = date('d');
$currentdaybilling = DB::table("billings")
->select(DB::raw("SUM(amount) as total"))
->whereRaw('(created_at) = ?',$currentDay)
->get();
Group By store.
The table name is called billings. I still want to follow this format
DB::table("billings")
How do I achieve this?
Error:
You should try this:
$currentYear = date('y');
$currentMonth = date('m');
$currentWeek = date('w');
$currentDay = date('d');
$currentyearbilling = DB::table("billings")
->select(DB::raw("SUM(amount) as total"))
->whereRaw('YEAR(created_at) = ?',$currentYear)
->orWhereRaw('MONTH(created_at) = ?',$currentMonth)
->orWhereRaw('WEEK(created_at) = ?',$currentWeek)
->orWhereRaw('(created_at) = ?',$currentDay)
->groupBy('store')
->get();
Lets say we have a table billings like this:
| column | type |
|------------|------------|
| id | int |
| store | varchar |
| amount | int |
| created_at | timestamps |
| updated_at | timestamps |
I will get amount of every store in current_day, current_month, current_week, current_year, and overall by using sub query like this:
select
store,
(SELECT SUM(amount) from billings as child where child.store = parent.store and DAY(created_at) = ? and MONTH(created_at) = ? and YEAR(created_at) = ?) as current_day,
(SELECT SUM(amount) from billings as child where child.store = parent.store and WEEK(created_at) = ?) as current_week,
(SELECT SUM(amount) from billings as child where child.store = parent.store and MONTH(created_at) = ? and YEAR(created_at) = ?) as current_month,
(SELECT SUM(amount) from billings as child where child.store = parent.store and YEAR(created_at) = ?) as current_year,
SUM(amount) as overall,
COUNT(id) as total_rows
from billings as parent
group by store
And with Eloquent, it would be just something like this:
$childQuery = \DB::table(\DB::raw('billings as child'))
->select(\DB::raw('SUM(amount) as amount'))
->whereRaw('child.store = parent.store');
$currentDayQuery = (clone $childQuery)->whereDay('created_at', now()->day)->whereMonth('created_at', now()->month)->whereYear('created_at', now()->year);
$currentWeekQuery = (clone $childQuery)->where(\DB::raw('WEEK(created_at)'), now()->weekOfYear);
$currentMonthQuery = (clone $childQuery)->whereMonth('created_at', now()->month);
$currentYearQuery = (clone $childQuery)->whereYear('created_at', now()->year);
$rows = \DB::table(\DB::raw('billings as parent'))
->select([
'store',
\DB::raw('(' . $currentDayQuery->toSql() . ') as current_day'),
\DB::raw('(' . $currentWeekQuery->toSql() . ') as current_week'),
\DB::raw('(' . $currentMonthQuery->toSql() . ') as current_month'),
\DB::raw('(' . $currentYearQuery->toSql() . ') as current_year'),
\DB::raw('SUM(amount) as overall'),
\DB::raw('COUNT(id) as total_rows')
])
->mergeBindings($currentDayQuery)
->mergeBindings($currentWeekQuery)
->mergeBindings($currentMonthQuery)
->mergeBindings($currentYearQuery)
->groupBy('store')
->get();
Hope this helps.

Is it possible to groupBy (month) and sum (each column) in table

+---------+--------+---------+---------+
| date | type_a | type_b | type_zzz|
+---------+--------+---------+---------+
|01-01-18 | 12 | 10 | 1 |
|02-01-18 | 2 | 5 | 1 |
|03-01-18 | 7 | 2 | 2 |
|01-02-18 | 13 | 6 | 55 |
|02-02-18 | 22 | 33 | 5 |
+---------+--------+---------+---------+
Hi,
In above example, I would like to know if it's possible to groupBy month and sum each column when getting results in Laravel (tables are dynamic so there are no models for them and also some tables don't have column 'type_a' other don't have 'type_zzz' etc...).
What I'm looking to get from above table is something like this:
"01" =>
'type_a' : '21',
'type_b' : '17',
'type_zzz': '4'
"02" =>
'type_a' : '35',
'type_b' : '39',
'type_zzz': '60'
I'm using following code to group it by month but I'm not able to find solution to return sum by each column:
DB::table($id)->get()->groupBy(function($date) {
return Carbon::parse($date->repdate)->format('m');;
});
If I understand your question correctly, you can either group and sum the values using an SQL query:
$grouped = DB::table('table_name')
->selectRaw('
SUM(type_a) AS type_a,
SUM(type_b) AS type_b,
SUM(type_z) AS type_z
')
->groupByRaw('MONTH(date)')
->get();
Or if you don't want to have to specify the column names in each query, you can use groupBy, array_column, and array_sum on your collection:
$grouped = DB::table('table_name')
->get()
->groupBy(function ($item) {
return Carbon::parse($item->date)->format('m');
})
->map(function ($group) {
$group = $group->toArray();
$summed = [];
$columns = array_keys($group[0]);
array_shift($columns);
foreach ($columns as $column) {
$summed[$column] = array_sum(array_column($group, $column));
}
return $summed;
});

Laravel Eloquent query fails on Eloquent but success in PhpMyAdmin

Laravel 4.2
Here are my tables
sk__queue :
+----------+----------+---------------------+--------+
| idQueue | idCap | date | status |
+----------+----------+---------------------+--------+
| 1350 | 7 | 2015-10-20 11:20:00 | 9 |
| 1427 | 7 | 2015-10-29 14:20:00 | 4 |
| 1428 | 7 | 2015-10-29 14:19:00 | 4 |
+----------+----------+---------------------+--------+
sk__cloc
+----------+----------+-----------+-----+---------------------+--------------------------+
| idCloc | idCap | cDuration | cNb | begin | end |
+----------+----------+-----------+-----+---------------------+--------------------------+
| 1 | 7 | 10 | 3 | 2015-01-01 08:00:00 | 2015-12-31 22:30:00 |
+----------+----------+-----------+-- --+---------------------+--------------------------+
A spot is available if the sk__queue.status is < 4 or > 7
I want to know how many spots are available between the last 10 minutes and 10 minutes in the future.
My Eloquent query is like that :
//$now = date('Y-m-d H:i:s');
$now = "2015-10-29 14:12:08";
$dispo = DB::table('sk__cloc')->leftJoin('sk__queue', function($join) use ($now) {
$join->on('sk__queue.idCap', '=', 'sk__cloc.idCap');
$join->where('sk__queue.date', '>=', "('$now' - INTERVAL cDuration MINUTE)");
$join->where('sk__queue.date', '<=', "('$now' + INTERVAL cDuration MINUTE)");
$join->where('sk__queue.date', '>=', 'sk__cloc.begin');
$join->where('sk__queue.date', '<=', 'sk__cloc.end');
$join->where('sk__queue.status', '>=', 4);
$join->where('sk__queue.status', '<=', 7);
})
->where('sk__cloc.idCap', '=', 7)
->groupBy('cNb')
->select(DB::raw('cNb*2, count(sk__queue.idCap) AS count'))
->first();
Result : "cNb*2":6, "count":0
Now I'm doing DB::getQueryLog() and this is the laravel request :
select cNb*2, count(sk__queue.idCap) AS count, sk__queue.date
from `sk__cloc` left join `sk__queue`
on `sk__queue`.`idCap` = `sk__cloc`.`idCap`
and `sk__queue`.`date` >= ('2015-10-29 14:12:08' - INTERVAL cDuration MINUTE)
and `sk__queue`.`date` <= ('2015-10-29 14:12:08' + INTERVAL cDuration MINUTE)
and `sk__queue`.`date` <= sk__cloc.end
and `sk__queue`.`status` >= 4
and `sk__queue`.`status` <= 7
where `sk__cloc`.`idCap` = 7
group by cNb
In PhpMyAdmin, the result is : "cNb*2":6, "count":2
Then I commented those lines :
//$now = date('Y-m-d H:i:s');
$now = "2015-10-29 14:12:08";
$dispo = DB::table('sk__cloc')->leftJoin('sk__queue', function($join) use ($now) {
$join->on('sk__queue.idCap', '=', 'sk__cloc.idCap');
$join->where('sk__queue.date', '>=', "('$now' - INTERVAL cDuration MINUTE)");
//$join->where('sk__queue.date', '<=', "('$now' + INTERVAL cDuration MINUTE)");
//$join->where('sk__queue.date', '>=', 'sk__cloc.begin');
$join->where('sk__queue.date', '<=', 'sk__cloc.end');
$join->where('sk__queue.status', '>=', 4);
$join->where('sk__queue.status', '<=', 7);
})
->where('sk__cloc.idCap', '=', 7)
->groupBy('cNb')
->select(DB::raw('cNb*2, count(sk__queue.idCap) AS count'))
->first();
Result : "cNb*2":6, "count":2
My only question is : What is happining right here ?! I'm so confused.
Thanks for your help.
You should keep joining "on":
$join->on('sk__queue.idCap', '=', 'sk__cloc.idCap');
$join->on('sk__queue.date', '>=', "('$now' - INTERVAL cDuration MINUTE)");
$join->on('sk__queue.date', '<=', "('$now' + INTERVAL cDuration MINUTE)");
$join->on('sk__queue.date', '>=', 'sk__cloc.begin');
$join->on('sk__queue.date', '<=', 'sk__cloc.end');
$join->on('sk__queue.status', '>=', 4);
$join->on('sk__queue.status', '<=', 7);

Resources