mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-app.git
synced 2026-06-17 16:40:31 +00:00
✨ Add RSVP functionality for Meetup Events
- 🏷️ Introduce `RsvpStatus` enum for managing attendance states (`attending`, `maybe`, `none`). - ✏️ Add `MeetupEventController` methods for RSVP actions (`rsvpStatus`, `rsvp`) and payload handling. - ✨ Implement RSVP helpers in `MeetupEvent` model for user-specific attendance management. - 🌐 Register RSVP routes for showing and updating attendance in the API. - 🧪 Add feature tests for RSVP actions, covering validation, idempotency, and correct list handling.
This commit is contained in:
@@ -3,11 +3,13 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\RecurrenceType;
|
||||
use App\Enums\RsvpStatus;
|
||||
use App\Observers\MeetupEventObserver;
|
||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
#[ObservedBy([MeetupEventObserver::class])]
|
||||
class MeetupEvent extends Model
|
||||
@@ -62,4 +64,76 @@ class MeetupEvent extends Model
|
||||
{
|
||||
return $this->belongsTo(Meetup::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eindeutige Kennung eines angemeldeten Nutzers in den Teilnehmer-Listen.
|
||||
* Einträge werden als `id_<userId>|<name>` abgelegt; der angehängte Pipe
|
||||
* grenzt z. B. `id_5` sauber von `id_50` ab.
|
||||
*/
|
||||
public static function rsvpIdentifierFor(User $user): string
|
||||
{
|
||||
return 'id_'.$user->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix, mit dem ein Eintrag des Nutzers in den Listen beginnt — inklusive
|
||||
* Pipe, damit `id_5` nicht auf `id_50` matcht.
|
||||
*/
|
||||
private static function rsvpPrefixFor(User $user): string
|
||||
{
|
||||
return self::rsvpIdentifierFor($user).'|';
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktueller RSVP-Status des Nutzers für diesen Termin.
|
||||
*/
|
||||
public function rsvpStatusFor(User $user): RsvpStatus
|
||||
{
|
||||
$prefix = self::rsvpPrefixFor($user);
|
||||
|
||||
if (collect($this->attendees ?? [])->contains(fn ($entry): bool => str($entry)->startsWith($prefix))) {
|
||||
return RsvpStatus::Attending;
|
||||
}
|
||||
|
||||
if (collect($this->might_attendees ?? [])->contains(fn ($entry): bool => str($entry)->startsWith($prefix))) {
|
||||
return RsvpStatus::Maybe;
|
||||
}
|
||||
|
||||
return RsvpStatus::None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt den RSVP-Status des Nutzers: entfernt ihn zunächst aus beiden Listen
|
||||
* und trägt ihn anschließend in die gewählte Liste ein. `None` sagt nur ab
|
||||
* (kein erneutes Eintragen). Persistiert die Änderung.
|
||||
*/
|
||||
public function setRsvpFor(User $user, RsvpStatus $status, string $name): void
|
||||
{
|
||||
$prefix = self::rsvpPrefixFor($user);
|
||||
|
||||
$attendees = $this->withoutEntry($this->attendees, $prefix);
|
||||
$mightAttendees = $this->withoutEntry($this->might_attendees, $prefix);
|
||||
|
||||
$entry = $prefix.$name;
|
||||
|
||||
match ($status) {
|
||||
RsvpStatus::Attending => $attendees->push($entry),
|
||||
RsvpStatus::Maybe => $mightAttendees->push($entry),
|
||||
RsvpStatus::None => null,
|
||||
};
|
||||
|
||||
$this->update([
|
||||
'attendees' => $attendees->values()->all(),
|
||||
'might_attendees' => $mightAttendees->values()->all(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string>|null $list
|
||||
* @return Collection<int, string>
|
||||
*/
|
||||
private function withoutEntry(?array $list, string $prefix): Collection
|
||||
{
|
||||
return collect($list ?? [])->reject(fn ($entry): bool => str($entry)->startsWith($prefix));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user