$contactIds * @param array $groupIds */ public function resolve(array $contactIds, array $groupIds): RecipientResolutionResult { $directContactIds = $this->cleanIds($contactIds); $selectedGroupIds = $this->cleanIds($groupIds); $groups = ContactGroup::query() ->with(['contacts' => fn ($query) => $query->select('contacts.id')]) ->whereKey($selectedGroupIds) ->orderBy('name') ->get(); /** @var array> $groupNamesByContactId */ $groupNamesByContactId = []; foreach ($groups as $group) { foreach ($group->contacts as $contact) { $groupNamesByContactId[$contact->id][] = $group->name; } } $selectedContactIds = $directContactIds ->merge($groups->flatMap(fn (ContactGroup $group): Collection => $group->contacts->pluck('id'))) ->unique() ->values(); $contacts = Contact::query() ->with([ 'groups', 'phoneNumbers' => fn ($query) => $query ->orderByDesc('is_primary') ->orderBy('id'), ]) ->whereKey($selectedContactIds) ->orderBy('last_name') ->orderBy('first_name') ->get(); $recipients = []; $excluded = []; $seenNumbers = []; foreach ($contacts as $contact) { $selection = $this->selectionLabels($contact, $directContactIds, $groupNamesByContactId[$contact->id] ?? []); if ($contact->sms_opt_out) { $excluded[] = $this->excludedEntry($contact, 'opt_out', null, $selection); continue; } if ($contact->phoneNumbers->isEmpty()) { $excluded[] = $this->excludedEntry($contact, 'missing_phone', null, $selection); continue; } foreach ($contact->phoneNumbers as $phoneNumber) { if (! $phoneNumber->is_valid || $phoneNumber->normalized_number === null) { $excluded[] = $this->excludedEntry($contact, 'invalid', $phoneNumber, $selection); continue; } if (! $phoneNumber->hasDetectedMobileNumber()) { $excluded[] = $this->excludedEntry($contact, 'non_mobile', $phoneNumber, $selection); continue; } if (isset($seenNumbers[$phoneNumber->normalized_number])) { $excluded[] = $this->excludedEntry( $contact, 'duplicate', $phoneNumber, $selection, ['duplicate_of' => $seenNumbers[$phoneNumber->normalized_number]], ); continue; } $entry = [ 'contact_id' => $contact->id, 'contact_name' => $contact->displayName(), 'phone_number_id' => $phoneNumber->id, 'number' => $phoneNumber->normalized_number, 'raw_number' => $phoneNumber->raw_number, 'label' => $phoneNumber->label, 'selection' => $selection, ]; $recipients[] = $entry; $seenNumbers[$phoneNumber->normalized_number] = $entry['contact_name']; } } return new RecipientResolutionResult( recipients: $recipients, excluded: $excluded, selectedContactCount: $contacts->count(), selectedGroupCount: $groups->count(), ); } /** * @param array $ids * @return Collection */ private function cleanIds(array $ids): Collection { return collect($ids) ->map(fn (int|string $id): int => (int) $id) ->filter(fn (int $id): bool => $id > 0) ->unique() ->values(); } /** * @param Collection $directContactIds * @param array $groupNames * @return array */ private function selectionLabels(Contact $contact, Collection $directContactIds, array $groupNames): array { $selection = $groupNames; if ($directContactIds->contains($contact->id)) { array_unshift($selection, 'Selection directe'); } return array_values(array_unique($selection)); } /** * @param array $selection * @param array $extra * @return array */ private function excludedEntry( Contact $contact, string $reason, ?ContactPhoneNumber $phoneNumber, array $selection, array $extra = [], ): array { return array_merge([ 'contact_id' => $contact->id, 'contact_name' => $contact->displayName(), 'contact_source' => $contact->source, 'phone_number_id' => $phoneNumber?->id, 'reason' => $reason, 'number' => $phoneNumber?->normalized_number, 'raw_number' => $phoneNumber?->raw_number, 'label' => $phoneNumber?->label, 'selection' => $selection, ], $extra); } }