First i would pull $users = User::orderByName()->get(); outside the loop. I dont see reason why it is inside. Second flip the condition if ($date->isWeekday()) to if (!$date->isWeekday()) { continue } or filter weekdays before loop. And where do you use your relations ?
Is it missing in code , or i am not seeing it. If you have relations between users and covid test, you should use it when getting users. And go from there.
Aug 29, 2020
4
Level 1
Nested Query Help
I have a Laravel app that allows users to login and take a COVID-19 self-attestment survey to record their temperature and answer questions recommended by the CDC, the HR manager would like a report of all users missing surveys between the dates she selects(omitting weekends).
I have the "hasMany" and "belongsTo" relationships defined. I'm just not sure how to nest the query and have run efficiently, I'm trying to hack through it with loops and obviously it runs like garbage.
public function missingSurveys($start = null, $end = null)
{
$dates = [];
$dates_missing = [];
if(!$start)
{
$start = Carbon::today()->subDays(14);
}
if(!$end)
{
$end = Carbon::today();
}
$period = CarbonPeriod::create($start, $end);
foreach($period as $date)
{
if ($date->isWeekday())
{
array_push($dates, $date);
$users = User::orderByName()->get();
$missing = [];
foreach($users as $user)
{
$surveys = CovidSurvey::where('user_id', '=', $user->id)
->whereDate('attestment_date', $date)
->first();
if ($surveys === null)
{
array_push($missing, $user->name);
}
}
array_push($dates_missing, [$date, $missing]);
}
}
return Inertia::render('Reports/MissingSurveys', [
'start' => $start,
'end' => $end,
'dates' => $dates,
'users' => $dates_missing,
]);
}
Level 1
I was able to use collections to reduce the entire process to just two queries
public function missingSurveys($start = null, $end = null)
{
// If request start date is NULL get date two weeks ago.
if(!$start)
{
$start = Carbon::today()->subDays(14)->toDateString();
}
// If request end date is NULL use today.
if(!$end)
{
$end = Carbon::today()->toDateString();
}
// Get all survey models between $start and $end.
$surveys = CovidSurvey::whereBetween('attestment_date', [$start, $end])
->get(['user_id', 'attestment_date'])
->transform(function ($survey) {
return [
'id' => $survey->user_id,
'date' => $survey->attestment_date,
];
});
// Convert $surveys to collection.
collect($surveys);
// Convert $start and $end to CarbonPeriod for iteration.
$period = CarbonPeriod::create($start, $end);
// Create $dates collection.
$dates = collect([]);
// Iterate through CarbonPeriod and push to $dates collection if isWeekday.
foreach($period as $date)
{
if ($date->isWeekday())
{
$dates->push($date);
}
}
// Get all users models ordered by last_name and then first_name.
$users = User::orderByName()
->get(['id', 'first_name', 'last_name', ])
->transform(function ($user) use ($surveys) {
return [
'id' => $user->id,
'name' => $user->last_name . ', ' . $user->first_name,
'surveys' => $surveys->where('id', $user->id)->pluck('date'),
];
});
$users = collect($users);
// Collection to hold final filtered data.
$filtered = collect([]);
foreach($dates as $date)
{
// Get all users that have no surveys.
$usersWithoutSurveys = $users->filter(function ($user) {
return $user['surveys']->count() === 0;
});
// Get all users that have surveys.
$usersWithSurveys = $users->filter(function ($user) {
return $user['surveys']->count() != 0;
});
// Iterate through surveys and add to $usersWithoutSurveys
// collection if $date does not exist in $user['surveys']
foreach($usersWithSurveys as $user)
{
$userSurveys = collect($user['surveys']);
if (!$userSurveys->contains($date->toDateString()))
$usersWithoutSurveys->push($user);
}
// Re-order alphabetically.
$usersWithoutSurveys = $usersWithoutSurveys->sortBy(function ($user, $key) {
return $user['name'];
});
// Reduce collection to just the users name.
$usersWithoutSurveys = $usersWithoutSurveys->pluck('name')->flatten();
// Push filtered data to final collection
$filtered->push([Carbon::parse($date)->format('m/d/Y'), $usersWithoutSurveys]);
}
return Inertia::render('Reports/MissingSurveys', [
'start' => $start,
'end' => $end,
'surveys' => $filtered,
]);
}
Please or to participate in this conversation.