Laravel: Sum daily (groupby date) then retain date with empty rows - laravel

stackoverflow,
I have working laravel function which get the daily sum of sales for the last 30 days.
I will use the data to build a graph, so I need to get the dates even if its empty and give them a value of "0" as their sum.
here's my code (it's working but only returns dates which are not empty)
public function getDaily() {
$startDate = Carbon::now()->subDays(30);
$endDate = Carbon::now();
$all_dates = array();
for($i = 0;$i<=30;$i++)
{
$all_dates[] = $startDate->toDateString();
$startDate->addDay();
$sales=DB::table('sale_details')
->select(DB::raw('sum(amount_due) as daily'),DB::raw('date(created_at) as date'))
->groupBy('date')
->orderBy('date','desc')
->get();
}
return $sales;
}

To get array of objects you may use good Collection's methods:
$sales = DB::table('sale_details')
->whereBetween('created_at', [$startDate, $endDate])
->select([
DB::raw('sum(amount_due) as daily'),
DB::raw('date(created_at) as date'),
])
->groupBy('date')
->get()
->keyBy('date');
$period = new CarbonPeriod($startDate, '1 day', $endDate);
// Fill zeroes
foreach ($period as $date) {
$dateString = $date->toDateString();
if (!$sales->has($dateString)) {
$sales->put($dateString, ['date' => $dateString, 'daily' => 0]);
}
}
// Convert to associative array
$sales = $sales->values()->toArray();

Try this:
$sales = DB::table('sale_details')
->whereBetween('created_at', [$startDate, $endDate])
->select([
DB::raw('sum(amount_due) as daily'),
DB::raw('date(created_at) as date'),
])
->groupBy('date')
->orderBy('date','desc')
->pluck('daily', 'date')
->toArray();
$period = new CarbonPeriod($startDate, '1 day', $endDate);
// Fill zeroes
foreach ($period as $date) {
if (!isset($sales[$date->toDateString()])) {
$sales[$date->toDateString()] = 0;
}
}
Another solution is using database-generated series of dates (pgsql example: Generating time series between two dates in PostgreSQL) and join it with result.

Related

Saving Array ID to Database with value to each id

Good Evening.... Hope i can explain my problem correctly.
I am getting data (ID) in array and value (numbers) in controller. Now i want to save the "numbers" in each "ID".
array ID ["Buffalo-01", "Buffalo-02", "Buffalo-04"]
Numbers - 40.
Want to save 40 to each ID.
Controller
public function addbuffalototalmilk(Request $req )
{
$buffalomilking = Buffalodata::where('avgmilk','<>','0')->Where('status','=','Available')->count(); // MIlking Animal Nos
$getbuffalomilkingid = Buffalodata::where('avgmilk','<>','0')->Where('status','=','Available')->pluck('buffaloID'); // Get Buffalo Details of Milking
$totalmorningmilk = $req->get('morningtotalmilk');
$totaleveningmilk = $req->get('eveningtotalmilk');
$eachmorningmilk = ($totalmorningmilk / $buffalomilking);
$eacheveningmilk = ($totaleveningmilk / $buffalomilking);
return response ();
}
Thanks in Advance
Is this what you are looking for ?
$buffalomilking = Buffalodata::where('avgmilk', '<>', '0')
->Where('status', '=', 'Available')
->count();
$getbuffalomilkingid = Buffalodata::where('avgmilk', '<>', '0')
->Where('status', '=', 'Available')
->pluck('buffaloID');
foreach ($getbuffalomilkingid as $id) {
Buffalodata::where('id', $id)->update([
'number' => $buffalomilking,
]);
}

Laravel groupBy date get total column

I want to report before the selected date and the entered day. Dates that are not before the selected date and the number of days are also coming. I don't want empty dates to appear and I want to show the total column data in the database for that date. Where am I doing wrong?
I want to make a comparison between incoming values to change the table background-color
Database Structure
Table Structure
public function listByDaily(Request $request)
{
$endDate = $request->date('date') ?? today();
$startDate = $endDate
->copy()
->subDays($numberofdays);
$dates = CarbonPeriod::create($startDate, $endDate);
$transactions = Transactions::whereBetween('date', [$startDate, $endDate])
->select('product_id', 'supplier_id', 'total', 'date')
->latest('date')
->get();
$productIds = $transactions
->pluck('product_id')
->unique()
->values()
->toArray();
$supplierIds = $transactions
->pluck('supplier_id')
->unique()
->values()
->toArray();
$productselect = Product::whereIn('id', $productIds)
->select('id', 'product_name')
->pluck('product_name', 'id');
$suppliers = Supplier::whereIn('id', $supplierIds)
->select('id', 'supplier_name')
->get();
$columns = collect($transactions)
->whereNotNull('date')
->groupBy('date','DESC')
->map(function($items) {
return $items
->groupBy('product_id','total');
})
->pluck('total');
//dd($columns);
/*->toArray();*/
$products = Transactions::join('products', 'products.id', '=', 'transaction.product_id')->get()->unique();
return view('transactions.reportbydates', compact('products', 'productIds', 'dates', 'suppliers', 'columns'));
}

Laravel query groubBy condition

I have a dB query where I would like to groupBy() only when conditions are met without using union because of pagination.
Unfortunately groupBy() seems to only work when called on the entire query outside of the loop.
This was made for dynamic filtering from $filterArr. Depending on the array I need to select from different columns of the table.
When the $key=='pattern' I would need the distinct results from its column.
the query looks something like this
select `col_1`, `col_2`, `col_3`
from `mytable`
where (`color` LIKE ? or `pattern` LIKE ? or `style` LIKE ?)
group by `col_2` //<< i need this only for 'pattern' above and not the entire query
Heres the model:
// $filterArr example
// Array ( [color] => grey [pattern] => stripe )
$query = DB::table('mytable');
$query = $query->select(array('col_1', 'col_2', 'col_3'), DB::raw('count(*) as total'));
$query = $query->where(function($query) use ($filterArr){
$ii = 0;
foreach ($filterArr as $key => $value) {
if ($key=='color'){
$column = 'color';
}else if ($key=='style'){
$column = 'style';
}else if ($key=='pattern'){
$column = 'pattern';
$query = $query->groupBy('col_2'); // << !! does not work
}
if($ii==0){
$query = $query->where($column, 'LIKE', '%'.$value.'%');
}
else{
$query = $query->orWhere($column, 'LIKE', '%'.$value.'%');
}
$ii++;
}
});
$query = $query->orderBy('col_2', 'asc')->simplePaginate(30);
I think you can simplify your code a bit:
$query = DB::table('mytable');
$query = $query->select(array('col_1', 'col_2', 'col_3'), DB::raw('count(*) as total'));
$query = $query->where(
collect($filterArr)
->only(['color','style','pattern'])
->map(function ($value, $key) {
return [ $key, 'like', '%'.$value.'%', 'OR' ];
})->all()
)->when(array_key_exists('pattern', $filterArr), function ($query) {
return $query->groupBy('col_2');
});
$query = $query->orderBy('col_2', 'asc')->simplePaginate(30);

Filter model with HasMany relationship

Let's say I have two models: Park and Items. Each Park can have a number of Items, through a HasMany relationship.
In Items table there's a park_id field and a type flag: an Item can be a fountain, or a basketball court, or whatever.
What I need to do is to filter the parks to obtain only those who has ALL of the item types passed in an array. This is what I have:
$parks = Park::whereHas('items', function($q) use ($request) {
$q->where('type', json_decode($request->type_list));
})->get();
But it's not working properly. Any ideas?
Thanks a lot, and happy new year to all!
Edit: I've found a working though really ugly solution:
$types_array = json_decode($request->type_list, true);
$results = [];
// A collection of parks for each item type
foreach($types_array as $type) {
$results[] = Park::whereHas('items', function($q) use ($type) {
$q->where('type', $type);
})->get();
}
// Intersect all collections
$parks = $results[0];
array_shift($results);
foreach ($results as $collection) {
$parks = $collection->intersect($parks);
}
return $parks;
You can use a foreach in whereHas method. It should be something like this:
$array = json_decode($request->type_list, true)
$parks = Park::whereHas('items', function($q) use ($array) {
foreach ($array as $key => $type) {
$q->where('type', $type);
}
})->get();
I think using where clause to match all item's type for the same row will result nothing, it's like doing where id = 1 and id = 2 and this is impossible because id can take only one value, what you should do is to use whereIn to get all items that match at least one type and then use groupBy to group result by park_id, then in the having clause you can check if the group count equal the type_list count:
$types = json_decode($request->type_list, true);
$parks = Park::whereHas('items', function($q) use ($types) {
$q->select('park_id', DB::raw('COUNT(park_id)'))
->whereIn('type', $types)
->groupBy('park_id')
->havingRaw('COUNT(park_id) = ?', [ count($types) ]);
})->get();
You can add multiple whereHas() constraint to one query:
$types_array = json_decode($request->type_list, true);
$query = Park::query();
foreach($types_array as $type) {
$query->whereHas('items', function($q) use($type) {
$q->where('type', $type);
});
}
$parks = $query->get();

Laravel Query Builder: How do I sort results by date - first with ascending order by future dates and then by descending order by past dates?

I have a model tasks with complete tasks with datetime in the past and upcoming tasks with datetime in the future.
While retrieving the tasks, I want to display the upcoming tasks arranged in ascending order (from now to the future) and tasks in the past in descending order (from now to the past).
public function getTasks()
{
$futureTasks = Task::whereDate('datetime', '>', Carbon::now())->orderBy('datetime', 'asc')->get();
$pastTasks = Task::whereDate('datetime', '<', Carbon::now())->orderBy('datetime', 'desc')->get();
$tasks = array_merge($futureTasks, $pastTasks);
$response = ['tasks' => $tasks];
// return...
}
I'm getting the following error:
array_merge(): Argument #1 is not an array
If I reverse the order of arguments for array_push function, I still get the same error.
public function getTasks()
{
$futureTasks = Task::whereDate('datetime', '>', Carbon::now())->orderBy('datetime', 'asc')->get();
$pastTasks = Task::whereDate('datetime', '<', Carbon::now())->orderBy('datetime', 'desc')->get();
$tasks = array_merge($pastTasks, $futureTasks);
$response = ['tasks' => $tasks];
// return...
}
And If I retrieve only the futureTasks or pastTasks without array_merge, I get the desired output.
public function getTasks()
{
$futureTasks = Task::whereDate('datetime', '>', Carbon::now())->orderBy('datetime', 'asc')->get();
$response = ['tasks' => $futureTasks];
// return...
}
What am I doing wrong here? Thanks a lot for your time.
Both the results are a collection. You can use the collection merge method.
public function getTasks()
{
$futureTasks = Task::whereDate('datetime', '>', Carbon::now())
->orderBy('datetime', 'asc')
->get();
$pastTasks = Task::whereDate('datetime', '<', Carbon::now())
->orderBy('datetime', 'desc')
->get();
$tasks = $futureTasks->merge($pastTasks);
$response = compact('tasks');
// return...
}
Since you're using the whereDate condition, you're missing all the data from the present date based on your queries. You might want to check that.

Resources