Files
mareike/app/Repositories/EventParticipantRepository.php
T
2026-08-12 17:08:21 +02:00

341 lines
13 KiB
PHP

<?php
namespace App\Repositories;
use App\Enumerations\EatingHabit;
use App\Models\Event;
use App\Models\EventParticipant;
use Illuminate\Http\Request;
class EventParticipantRepository {
public function getForList(Event $event, Request $request, bool $signedOffParticipants = false) : array {
$participants = [];
if (!$signedOffParticipants) {
$allParticipants = $event->participants()->whereNull('unregistered_at');
} else {
$allParticipants = $event->participants()->whereNotNull('unregistered_at');
}
foreach ($allParticipants->orderBy('lastname')->orderBy('firstname')->get() as $participant) {
$participants[] = $participant->toResource()->toArray($request);
};
return $participants;
}
public function getByIdentifier(string $identifier, EventRepository $eventRepository, bool $withPermissionCheck = true) : ?EventParticipant {
$participant = EventParticipant::where('identifier', $identifier)->first();
if ($participant === null) {
return null;
}
if ($withPermissionCheck) {
$event = $eventRepository->getById($participant->event_id);
if ($event === null) {
return null;
}
}
return $participant;
}
public function groupByLocalGroup(Event $event, Request $request, ?string $filter = null) : array {
$allParticipants = $this->getForList($event, $request);
$participants = [];
foreach ($allParticipants as $participant) {
if ($filter === null || $participant['localgroup'] === $filter) {
$participants[$participant['localgroup']][] = $participant;
}
}
return $participants;
}
public function groupByParticipationType(Event $event, Request $request, ?string $filter = null) : array {
$allParticipants = $this->getForList($event, $request);
$participants = [];
foreach ($allParticipants as $participant) {
if ($filter === null || $participant['participationType'] === $filter) {
$participants[$participant['participationType']][] = $participant;
}
}
return $participants;
}
/**
* Gruppiert die (aktiven) Teilnehmenden nach ihren gewählten Teilnahmeoptionen. Je am Event definierter
* Frage entsteht eine Sektion mit Gruppen „Frage: Option" (in Definition-Reihenfolge); Teilnehmende ohne
* Antwort landen unter „Frage: (keine Angabe)". Ein Teilnehmer erscheint je Frage einmal, kann also über
* mehrere Fragen hinweg in mehreren Gruppen auftauchen. `$filter` liefert nur die eine passende Gruppe
* (für die E-Mail-Auflösung in ByGroupController).
*/
public function groupByParticipationOption(Event $event, Request $request, ?string $filter = null): array
{
$definitions = $event->participation_options ?? [];
$allParticipants = $this->getForList($event, $request);
$groups = [];
foreach ($definitions as $question) {
$questionKey = $question['key'] ?? null;
if ($questionKey === null) {
continue;
}
// Überschrift: bevorzugt der Titel, sonst die (längere) Frage bzw. der Key als Fallback.
$questionLabel = trim((string)($question['title'] ?? '')) !== ''
? $question['title']
: ($question['label'] ?? $questionKey);
$noAnswerKey = $questionLabel . ': (keine Angabe)';
// Vordefinierte Optionen in Reihenfolge: value => Gruppen-Key.
$definedGroupKeys = [];
foreach ($question['options'] ?? [] as $option) {
$definedGroupKeys[$option['value']] = $questionLabel . ': ' . ($option['label'] ?? $option['value']);
}
$collected = [];
foreach ($allParticipants as $participant) {
$answer = null;
foreach ($participant['participationOptions'] ?? [] as $selectedOption) {
if (($selectedOption['questionKey'] ?? null) === $questionKey) {
$answer = $selectedOption;
break;
}
}
if ($answer === null || ($answer['value'] ?? '') === '') {
$collected[$noAnswerKey][] = $participant;
continue;
}
// Bevorzugt die definierte Option; falls sie zwischenzeitlich entfernt wurde, den Snapshot nutzen.
$groupKey = $definedGroupKeys[$answer['value']] ?? ($questionLabel . ': ' . ($answer['valueLabel'] ?? $answer['value']));
$collected[$groupKey][] = $participant;
}
// Ausgabe je Frage: definierte Optionen zuerst (in Reihenfolge), dann Snapshot-Reste, „(keine Angabe)" ans Ende.
foreach ($definedGroupKeys as $groupKey) {
if (!empty($collected[$groupKey])) {
$groups[$groupKey] = $collected[$groupKey];
unset($collected[$groupKey]);
}
}
foreach ($collected as $groupKey => $participants) {
if ($groupKey === $noAnswerKey) {
continue;
}
$groups[$groupKey] = $participants;
}
if (!empty($collected[$noAnswerKey])) {
$groups[$noAnswerKey] = $collected[$noAnswerKey];
}
}
if ($filter !== null) {
return array_key_exists($filter, $groups) ? [$filter => $groups[$filter]] : [];
}
return $groups;
}
/**
* Gruppiert die (aktiven) Teilnehmenden nach ihren gebuchten Zusätzen (Event-Addons). Da Addons Ja/Nein
* sind, entsteht eine Gruppe je Zusatz (Überschrift = Addon-Titel) mit den Teilnehmenden, die ihn gebucht
* haben; ein Teilnehmer kann in mehreren Gruppen auftauchen. Nur nicht-leere Gruppen. Die Reihenfolge folgt
* der Event-Definition; nachträglich entfernte Addons werden über den Snapshot-Titel weiterhin abgedeckt.
*/
public function groupByAddon(Event $event, Request $request, ?string $filter = null): array
{
$allParticipants = $this->getForList($event, $request);
// Gruppen-Reihenfolge aus der Event-Definition vorbelegen (leere Gruppen werden am Ende verworfen).
$groups = [];
foreach ($event->addons ?? [] as $addon) {
$title = $addon['title'] ?? ($addon['key'] ?? null);
if ($title !== null) {
$groups[$title] = [];
}
}
foreach ($allParticipants as $participant) {
foreach ($participant['selectedAddons'] ?? [] as $selectedAddon) {
$title = $selectedAddon['title'] ?? ($selectedAddon['addonKey'] ?? null);
if ($title === null) {
continue;
}
$groups[$title][] = $participant;
}
}
$groups = array_filter($groups, fn($group) => count($group) > 0);
if ($filter !== null) {
return array_key_exists($filter, $groups) ? [$filter => $groups[$filter]] : [];
}
return $groups;
}
public function getSignedOffParticipants(Event $event, Request $request, ?string $filter = null) : array {
$allParticipants = $this->getForList($event, $request, true);
$participants = [];
foreach ($allParticipants as $participant) {
if ($filter === null || $participant['participationType'] === $filter) {
$participants[$participant['participationType']][] = $participant;
}
}
return $participants;
}
public function getParticipantsWithMissingPayments(Event$event, Request $request) : array {
$participants = [];
foreach ($event->participants()->whereNull('unregistered_at')
->whereColumn('amount', '<>', 'amount_paid')
->get() as $participant) {
$participants[] = $participant;
};
return $participants;
}
public function getParticipantsWithIntolerances(Event $event, Request $request) : array {
$participants = [];
foreach ($event->participants()->orderBy('lastname')->orderBy('firstname')->whereNotNull('intolerances')->whereNot('intolerances' , '=', '')->get() as $participant) {
$participants[] = $participant->toResource()->toArray($request);
};
return $participants;
}
public function getKitchenOverview(Event $event) : array {
$data = [];
$participants = $event->participants()->orderBy('lastname')->orderBy('firstname')->get();
for ($cur_date = $event->start_date; $cur_date <= $event->end_date; $cur_date->modify('+1 day')) {
$dateKey = $cur_date->format('d.m.Y');
$data[$dateKey] = [
'1' => [],
'2' => [],
'3' => [],
];
foreach (EatingHabit::all() as $eatingHabit) {
$data[$dateKey]['1'][$eatingHabit->slug] = 0;
$data[$dateKey]['2'][$eatingHabit->slug] = 0;
$data[$dateKey]['3'][$eatingHabit->slug] = 0;
}
foreach ($participants as $participant) {
$eatingHabitSlug = $participant->eating_habit;
if ($eatingHabitSlug === null) {
continue;
}
$anreise = $participant->arrival_date;
$abreise = $participant->departure_date;
if ($anreise->getTimestamp() === $cur_date->getTimestamp()) {
switch ((int)$participant->arrival_eating) {
case 1:
$data[$dateKey]['3'][$eatingHabitSlug]++;
break;
case 2:
$data[$dateKey]['2'][$eatingHabitSlug]++;
$data[$dateKey]['3'][$eatingHabitSlug]++;
break;
case 3:
$data[$dateKey]['1'][$eatingHabitSlug]++;
$data[$dateKey]['2'][$eatingHabitSlug]++;
$data[$dateKey]['3'][$eatingHabitSlug]++;
break;
}
} elseif ($abreise->getTimestamp() === $cur_date->getTimestamp()) {
switch ((int)$participant->departure_eating) {
case 1:
$data[$dateKey]['1'][$eatingHabitSlug]++;
break;
case 2:
$data[$dateKey]['1'][$eatingHabitSlug]++;
$data[$dateKey]['2'][$eatingHabitSlug]++;
break;
case 3:
$data[$dateKey]['1'][$eatingHabitSlug]++;
$data[$dateKey]['2'][$eatingHabitSlug]++;
$data[$dateKey]['3'][$eatingHabitSlug]++;
break;
}
} else {
$data[$dateKey]['1'][$eatingHabitSlug]++;
$data[$dateKey]['2'][$eatingHabitSlug]++;
$data[$dateKey]['3'][$eatingHabitSlug]++;
}
}
}
return $data;
}
public function getMailAddresses(array $participants) : array {
$mailAddresses = [];
foreach ($participants as $participant) {
if (!in_array($participant['email_1'], $mailAddresses)) {
$mailAddresses[] = $participant['email_1'];
}
if ($participant['email_2'] !== null && !in_array($participant['email_2'], $mailAddresses)) {
$mailAddresses[] = $participant['email_2'];
}
}
return $mailAddresses;
}
public function getMyParticipations(?int $maxEvents = null) : array {
$participations = [];
$user = auth()->user();
if ($user === null) {
return $participations;
}
$request = new \Illuminate\Http\Request();
$query = EventParticipant::where('user_id', $user->id)
->whereNull('unregistered_at')
->whereHas('event', function ($q) {
$q->where('end_date', '>=', now());
})
->with(['event'])
->join('events', 'event_participants.event_id', '=', 'events.id')
->orderBy('events.start_date', 'asc')
->select('event_participants.*');
if ($maxEvents !== null) {
$query->limit($maxEvents);
}
foreach ($query->get() as $participant) {
$participations[] = $participant->toResource()->toArray($request);
}
return $participations;
}
public function getMyParticipationByIdentifier(string $identifier) : ?EventParticipant {
$user = auth()->user();
if ($user === null) {
return null;
}
return EventParticipant::where('identifier', $identifier)
->where('tenant', app('tenant')->slug)
->where('user_id', $user->id)
->whereNull('unregistered_at')
->first();
}
}