laravel collection groupBy() / include empty results - laravel

For a chart API I would need to provide the count of registered users per day.
//fetch all created_at dates of users from the last week
$signUpsLastWeek = User::whereDate('created_at', '>=', now()->subDays(7))->select('created_at')->get();
//group them now by date now, using collection operations
dd($signUpsLastWeek->groupBy(function($item) {
return $item->created_at->format('m.d');
}));
//now manipulate the collection a bit, so we get the date with the amount of new registered users
$signUpsLastWeek->mapWithKeys(function ($userGroup, $key) {
return [$key => $userGroup->count()];
})
Returns:
Illuminate\Database\Eloquent\Collection {#774 ▼
#items: array:1 [▼
"01.19" => 4
]
}
This works fine, a question is left.
In the example code above there are 0 new signups on the other days, meaning the collection should look something like:
Illuminate\Database\Eloquent\Collection {#774 ▼
#items: array:1 [▼
"01.25" => 0,
"01.24" => 0,
"01.23" => 0,
"01.22" => 0,
"01.20" => 0,
"01.19" => 4,
...,
]
}
Any idea how to include the 0 amounts too?

I think you can use CarbonPeriod to create the calendar and set the default count 0 for every date.
And then reset the value from your users' count:
$start_date = explode(' ', User::whereDate('created_at', '>=', now()->subDays(7))->min('created_at'))[0];
$end_date = \Carbon\Carbon::now()->format('Y-m-d');
$period = \Carbon\CarbonPeriod::create($start_date, $end_date);
$all_dates = [];
foreach ($period as $date) {
$all_dates = array_merge($all_dates, [$date->format('m.d') => 0]);
}
$all_dates = array_reverse($all_dates);
collect($all_dates)->mapWithKeys(function($v, $date) use ($signUpsLastWeek) {
if (in_array($date, array_keys($signUpsLastWeek))) {
return [$date => $signUpsLastWeek[$date]->count()];
} else {
return [$date => 0];
}
})->all();

Related

Unable to register extra fileds in a pivot table using sync function with Laravel

I'm unable to register extra fields in a pivot table:
This my scheme:
Buyer Model:
public function qualities(){
//many-to-many relation
return $this->belongsToMany(Quality::class);
}
Quality Model:
public function qualities(){
//many-to-many relation
return $this->belongsToMany(Quality::class);
}
Product Model:
public function buyers(){
//many-to-many relation
return $this->belongsToMany(Buyer::class);
}
Before of send the data to the sync function, I'm combining the data:
public function store(createBuyerRequest $request){
if($request->validated()){
try{
//Register buyer
$buyer = new Buyer;
$buyer->ruc = $request->ruc;
$buyer->companyName = $request->companyName;
$buyer->contact = $request->contact;
$buyer->address = $request->address;
$buyer->phone = $request->phone;
$buyer->email = $request->email;
$buyer->save();
$arrayQualitiesIds = $request->get('qualitiesProductCheckbox');
//Build arrayMap
$extra = array_map(function($qualityId) use($request){
return ['quality_id' => $qualityId, 'product_id' => $request->product];
}, $arrayQualitiesIds);
// Combine the array
$data = array_combine($arrayQualitiesIds, $extra);
$buyer->qualities()->sync($data);
return redirect()->route('admin.buyers.index')
->with('status_success','Comprador registrado correctamente!');
}catch(Exception $e){
return redirect()->route('admin.buyers.index')
->with('cancel','No se pudo registrar el comprador. '.$e->getMessage());
}
}
}
The output of $data = array_combine($arrayQualitiesIds, $extra) of one example was this:
Output:
1 => array:2 [▼
"quality_id" => "1"
"product_id" => "5"
]
2 => array:2 [▼
"quality_id" => "2"
"product_id" => "5"
]
3 => array:2 [▼
"quality_id" => "3"
"product_id" => "5"
]
The combination was successfully done! however was unable of register the data in the pivot table, only was registered the buyer.
This is view from the form to register a buyer:
Basically a buyer is being registered and selection a product of interest and his qualities.. The business wants register a buyer with his desired product.
The code $buyer->qualities()->sync($data); should register the buyer_id automatically using the relation and fill the pivot table with array combination putted in $data.
Any Idea for fix this problem guys I will appreciate, thanks so much.
Fixed
I just modified the relation inside of the Buyer model:
public function qualities(){
return $this->belongsToMany(Buyer::class, 'product_interested','quality_id', 'product_id','buyer_id')->withTimestamps();;
}
I also needed of add the buyer_id inside of the array_map:
$extra = array_map(function($qualityId) use($request, $buyer){
return ['quality_id' => $qualityId,
'product_id' => $request->product,
'buyer_id' => $buyer->id];
}, $arrayQualitiesIds);
//combine arrays
$data = array_combine($arrayQualitiesIds, $extra);
/*
dd($data);
array:1 [▼
4 => array:3 [▼
"quality_id" => "4"
"product_id" => "6"
"buyer_id" => 19
]
]
*/
$buyer->qualities()->sync($data);
I don't know the reason why $buyer->qualities()->sync($data) not inserted automatically the buyer_id
You have to specify the name of the pivot table in your relation methods like this:
Buyer Model:
public function qualities(){
//many-to-many relation
return $this->belongsToMany(Quality::class, 'product_interested');
}

Sum collection variables

i have the next collection:
It is grouped by zone and fundo and I need to sum all the sup_ha that each fundo contains
$transporFundo = $tansporCollect->groupBy(['zona','fundo']);
foreach ($transporFundo as $fundo) {
$supFundo = $fundo->sum('sup_ha');
dd($supFundo);
}
that is to say that for example of my collection for zona 1 and fundo 805 I would have to add the sup_ha field of the 364 records that are seen.
I tried to do it but I don't know how to access the field, as seen in my code I tried and it returns 0.
I hope you can help me, thanks
**
UPDATE
**
ok, remove the group by, for example show a collection like the following, the original has 700k of records
Illuminate\Support\Collection {#778646 ▼
#items: array:4 [▼
0 => array:3 [▼
"zona" => 1
"sup_ha" => 20
"fundo" => 805
]
1 => array:3 [▼
"zona" => 1
"sup_ha" => 10
"fundo" => 805
]
2 => array:3 [▼
"zona" => 2
"sup_ha" => 5
"fundo" => 800
]
3 => array:3 [▼
"zona" => 2
"sup_ha" => 10
"fundo" => 900
]
]
}
so, I need the sum of the sup_ha of equal fundo, like this:
fundo | sum
805 | 30
800 | 5
900 | 10
And I need the sum of the sup_ha of equal zona, like this:
zona | sum
1 | 30
2 | 15
Given this, that's why I had made a group by to calculate the sum of the fundo's. so now I don't know whether to think if it was right to do it like this
I hope you have clarified
Assuming your collection is assigned as $collection
$zonaArray = [];
$fundoArray = [];
$sum = 0;
foreach ($collection as $key => $t) {
if (!in_array($t['zona'], $zonaArray)) {
$zonaArray[$key]['zona'] = $t['zona'];
$zonaArray[$key]['sum'] = (float) $t['sup_ha'];
} else {
$zonaArray[$key]['sum'] = (float) $t['sup_ha'];
}
if (!in_array($t['fundo'], $fundoArray)) {
$fundoArray[$key]['fundo'] = $t['fundo'];
$fundoArray[$key]['sum'] = (float) $t['sup_ha'];
} else {
$fundoArray[$key]['sum'] = (float) $t['sup_ha'];
}
}
$checkzona = [];
$value = 0;
$finalZone = [];
$i = 0;
foreach ($zonaArray as $key => $val) {
if (in_array($val['zona'], $checkzona)) {
unset($finalZone[$i - 1]);
$val['sum'] = $value + (float) $val['sum'];
$finalZone[$i] = $val;
$i++;
} else {
$checkzona[] = $val['zona'];
$value = $val['sum'];
$finalZone[$i] = $val;
$i++;
}
}
Your zone wise sum output is looking like below.
array:2 [▼
1 => array:2 [▼
"zona" => 1
"sum" => 30.0
]
3 => array:2 [▼
"zona" => 2
"sum" => 15.0
]
]
For fundo sum:
$checkfundo = [];
$valuefundo = 0;
$finalFundo = [];
$k = 0;
foreach ($fundoArray as $key => $fundo) {
if (in_array($fundo['fundo'], $checkfundo)) {
unset($finalFundo[$k - 1]);
$fundo['sum'] = $valuefundo + (float) $fundo['sum'];
$finalFundo[$k] = $fundo;
$k++;
} else {
$checkfundo[] = $fundo['fundo'];
$valuefundo = $fundo['sum'];
$finalFundo[$k] = $fundo;
$k++;
}
}
Your fundo wise sum output is looking like below.
array:3 [▼
1 => array:2 [▼
"fundo" => 805
"sum" => 30.0
]
2 => array:2 [▼
"fundo" => 800
"sum" => 5.0
]
3 => array:2 [▼
"fundo" => 900
"sum" => 10.0
]
]
I hope it's working for you.
Assuming your collection is assigned to $collection.
so, I need the sum of the sup_ha of equal fundo.
High order functions
return $collection->groupBy('fundo')->map->sum('sup_ha');
Alternative
return $collection
->groupBy('fundo')
->transform(function (Collection $sub) {
return $sub->sum('sup_ha');
});
It prints
{
"805": 30,
"800": 5,
"900": 10
}
And I need the sum of the sup_ha of equal zona
High order functions
return $collection->groupBy('zona')->map->sum('sup_ha');
Alternative
return $collection
->groupBy('zona')
->transform(function (Collection $sub) {
return $sub->sum('sup_ha');
});
It prints
{
"1": 30,
"2": 15
}

Remove Nulls From Query Builder Laravel

Im having a little trouble with some return data that I'm getting when running a Query Builder then paginating the data
I was wondering if anyone knew how I could remove Null Results before I paginate the data.
// So Before I hit this
$return = $tld->paginate($request->get('limit'))->toArray();
This is the issue after I paginate
array:12 [
"current_page" => 1
"data" => array:12 [
0 => null
1 => array:3 [
I need to get rid of those Null values, I know how to do it after I paginate however I want to get rid of them before I paginate...
Im hoping that some of you laravel Gods can help me ..
Added this extra logic for $tld
private function newest(Request $request)
{
$this->validate($request, [
'timescale' => [
'required',
Rule::in(['today', 'this_week', 'this_month', 'all_time'])
]
]);
$tld = TimelineItem::where('timeline_items.created_at', '>', $this->timescaleToHours($request->get('timescale')));
if ($request->search_content) {
$tld = $this->searchContent($tld, $request->search_content, 0.4);
} else {
$tld = $tld->orderBy('timeline_items.created_at', 'DESC');
}
if ($request->types) {
$tld = $this->filters($tld, $request->types);
}
if ($request->tags) {
$tld = $this->tags($tld, $request->tags);
}
return $tld;
}
You can use filter() to remove null. Laravel Collection filter()
$response = $tld->paginate($request->get('limit'))->toArray();
$response['data'] = collect($response['data'])->filter()->toArray();
return $response;

Laravel5.5 adding columns to a collection from a second one

I have a sql request concerning admin :
$admin = Admin::findOrFail(Auth::guard('admin')->user()->id);
from this sql request I also manage to get the users of the admin ...
$users = $admin->users;
Each users must follow training sessions. and I must calculate it by using SQL requests for each users ... So i wrote this foreach statement
foreach ($users as $user) {
$todo = User::select() .....where('users.id' = $user->id)->get();
$done = User::select() .....where('users.id' = $user->id)->get();
$totalTimes = $todo->toBase()->sum('dureeformation');
$spendTimes = $done->toBase()->sum('dureeformation');
$remainingTimes = $totalTimes - $spendTimes;
$timeData[] = ['id' => $user->id, 'totalTime' => $totalTimes, 'spendTime' => $spendTimes, 'remainingTime' => $remainingTimes];
}
the totalTimes, spendTimes and remainingTimes are operations on Collections and i get expected results ...
$users->each(function ($record) use ($timeData) {
$record['totalTime'] = $timeData[$record['id']]['totalTime'];
$record['spendTime'] = $timeData[$record['id']]['spendTime'];
$record['remainingTime'] = $timeData[$record['id']]['remainingTime'];
//dd($record['totalTime']);
});
After this :
$timeData = collect($timeData);
$timeData= $timeData->keyBy('id');
$users = collect($users->toArray());
I have an issue here :
$users->each(function ($record) use ($timeData) {
$record['totalTime'] = $timeData[$record['id']]['totalTime'];
$record['spendTime'] = $timeData[$record['id']]['spendTime'];
$record['remainingTime'] = $timeData[$record['id']]['remainingTime'];
//var_dump($record);
});
$record in the function gives me what i expect ... the var_dump($records) added columns where it was expected but i can't take those results out of the function.
I tried to do this :
$variable = $users->each(function ($record) use ($timeData)...
dd($variable)
but unsuccessfully ...
Looks to me like your $timeData array is built differently than you access it. In your code you are doing
$timeData[] = [
'id' => $user->id,
'totalTime' => $totalTimes,
'spendTime' => $spendTimes,
'remainingTime' => $remainingTimes,
];
which will produce an array like this
[
0 => [
'id' => 17,
'totalTime' => 5,
'spendTime' => 4,
'remainingTime' => 3
],
1 => [
'id' => 17,
'totalTime' => 7,
'spendTime' => 5,
'remainingTime' => 2
]
]
but what you actually want is to have the id as index, right? Then you'd have to build the array this way:
$timeData[$user->id] = [
'totalTime' => $totalTimes,
'spendTime' => $spendTimes,
'remainingTime' => $remainingTimes,
];
or, as an alternative, perform a different lookup:
$users->each(function ($record) use ($timeData) {
$times = array_first($timeData, function ($value, $key) use ($record) {
return $value['id'] === $record['id'];
});
$record['totalTime'] = $times['totalTime'];
$record['spendTime'] = $times['spendTime'];
$record['remainingTime'] = $times['remainingTime'];
});
Both solutions have pros and cons, though. On the one hand, using the user identifier as the index will produce a huge array that is only filled partially. On the other hand, the second solution is a bit slower. As long as you are not going to deal with thousands of entries in the $timeData array, this won't be an issue, though.

Convert an array of array onto Collection Laravel

I have an array of array which results from several sql requests.
First I make a sql request in a foreach in order to have all the datas i need
foreach ($users as $user) {
// Two different Eloquent requests
$todo = User::select().....where('users.id' = $id);
$done = User::select().....where('users.id' = $id);
// I have operation on Collection that work fine ...
$totalTimes = $todo->toBase()->sum('dureeformation') ;
$spendTimes = $done->toBase()->sum('dureeformation') ;
$remainingTimes = $totalTimes - $spendTimes;
$all[] = ['user_id' => $id, 'totalTime' => $totalTimes, 'spendTime' => $spendTimes,'remainingTime' => $remainingTimes ];
}
My issue is the following... Outside the foreach i display the values and i have this array of array ...
array:16 [▼
0 => array:4 [▼
"user_id" => 1
"totalTime" => 465
"spendTime" => 0
"remainingTime" => 465
]
1 => array:4 [▼
"user_id" => 3
"totalTime" => 375
"spendTime" => 0
"remainingTime" => 375
]
I would need to have a Collection instead ... I tried to make $all = Collect(....) but i doesn't give me the expected result.
I need a collection because i have to create a Collection with this created collection and another one from another request.
Concerning this part i already had this case and i can solve it.
Thanks for your help
try this helper function :
$collection = collect($all);
This function will convert your array to collection.

Resources