with([ 'checkoutable' => function (MorphTo $morph) { $morph->morphWith([ Asset::class => ['model.category', 'assignedTo', 'adminuser', 'company', 'checkouts'], Accessory::class => ['category', 'company', 'checkouts'], LicenseSeat::class => ['user', 'license', 'checkouts'], Component::class => ['assignedTo', 'company', 'checkouts'], Consumable::class => ['company', 'checkouts'], ]); }, 'assignedTo', ]) ->whereHasMorph( 'checkoutable', [Asset::class, Accessory::class, LicenseSeat::class, Component::class, Consumable::class], fn ($q) => $q->whereNull('accepted_at') ->whereNull('declined_at') ) ->pending() ->get(); $count = 0; $unacceptedAssetGroups = $pending ->map(function ($acceptance) { return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance]; }) ->groupBy(function ($item) { return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : ''; }); $no_email_list = []; foreach ($unacceptedAssetGroups as $unacceptedAssetGroup) { // The [0] is weird, but it allows for the item_count to work and grabs the appropriate info for each user. // Collapsing and flattening the collection doesn't work above. $acceptance = $unacceptedAssetGroup[0]['acceptance']; $locale = $acceptance->assignedTo?->locale; $email = $acceptance->assignedTo?->email; if (! $email) { $no_email_list[] = [ 'id' => $acceptance->assignedTo?->id, 'name' => $acceptance->assignedTo?->display_name, ]; } else { $count++; } $item_count = $unacceptedAssetGroup->count(); if ($locale && $email) { Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))->locale($locale)); } elseif ($email) { Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))); } } $this->info($count.' users notified.'); $headers = ['ID', 'Name']; $rows = []; foreach ($no_email_list as $user) { $rows[] = [$user['id'], $user['name']]; } if (! empty($rows)) { $this->info('The following users do not have an email address:'); $this->table($headers, $rows); } return 0; } }