Publication initiale de RelaisSMS : documentation et sources nettoyees
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Recipients;
|
||||
|
||||
use App\Models\Contact;
|
||||
use App\Models\ContactGroup;
|
||||
use App\Models\ContactPhoneNumber;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class RecipientResolver
|
||||
{
|
||||
/**
|
||||
* @param array<int, int|string> $contactIds
|
||||
* @param array<int, int|string> $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<int, array<int, string>> $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<int, int|string> $ids
|
||||
* @return Collection<int, int>
|
||||
*/
|
||||
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<int, int> $directContactIds
|
||||
* @param array<int, string> $groupNames
|
||||
* @return array<int, string>
|
||||
*/
|
||||
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<int, string> $selection
|
||||
* @param array<string, mixed> $extra
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user