Add addToMine functionality to Meetups API for adding meetups to a user's "My Meetups" list

- 🔒 Introduce `addToMine` policy for authenticated users to add existing meetups.
- ✏️ Add `addToMine` method in `MeetupController` with idempotent handling.
-  Include `addMember` utility in `Meetup` model for managing pivot relationships.
- 🛠️ Refactor `AddMeetupToMineTool` to use `addMember` for consistency.
- 🧪 Add feature tests for `addToMine`, covering idempotency, permissions, and unknown slugs.
- 🌐 Register `addToMine` route in API and link it to `MeetupController`.
This commit is contained in:
HolgerHatGarKeineNode
2026-06-15 00:10:21 +02:00
parent ac1abc4435
commit 119deb4f5c
6 changed files with 95 additions and 8 deletions
@@ -122,6 +122,27 @@ class MeetupController extends Controller
return MeetupResource::collection($meetups);
}
/**
* Bestehendes Meetup zu „Meine Meetups" hinzufügen
*
* Fügt ein bereits existierendes Meetup zur „Meine Meetups"-Liste des authentifizierten
* Nutzers hinzu (meetup_user-Pivot als Mitglied, is_leader=false). Idempotent: ein bereits
* hinzugefügtes Meetup bleibt unverändert. Die Stammdaten bleiben dem Ersteller vorbehalten.
*/
#[Response(status: 401, description: 'Nicht authentifiziert.')]
public function addToMine(Request $request, Meetup $meetup): JsonResponse
{
Gate::authorize('addToMine', $meetup);
$wasAdded = $meetup->addMember($request->user());
return MeetupResource::make($meetup)
->response()
->setStatusCode($wasAdded
? \Symfony\Component\HttpFoundation\Response::HTTP_CREATED
: \Symfony\Component\HttpFoundation\Response::HTTP_OK);
}
/**
* Eigenes Meetup anzeigen
*
+4 -8
View File
@@ -39,17 +39,13 @@ class AddMeetupToMineTool extends Tool
}
}
$alreadyMember = $meetup->users()->whereKey($user->getAuthIdentifier())->exists();
$meetup->users()->syncWithoutDetaching([
$user->getAuthIdentifier() => ['is_leader' => false],
]);
$wasAdded = $meetup->addMember($user);
return Response::json([
'meetup' => MeetupResource::make($meetup)->resolve(),
'message' => $alreadyMember
? '„'.$meetup->name.'" war bereits Teil deiner Meetups.'
: '„'.$meetup->name.'" wurde zu deinen Meetups hinzugefügt.',
'message' => $wasAdded
? '„'.$meetup->name.'" wurde zu deinen Meetups hinzugefügt.'
: '„'.$meetup->name.'" war bereits Teil deiner Meetups.',
]);
}
+17
View File
@@ -185,6 +185,23 @@ class Meetup extends Model implements HasMedia
return $this->users()->whereKey($user->id)->exists();
}
/**
* Den Nutzer als Mitglied (nicht Leader) zu „Meine Meetups" hinzufügen.
* Idempotent: ein bereits hinzugefügter Nutzer bleibt unverändert. Gibt
* true zurück, wenn neu hinzugefügt (false = war bereits Mitglied).
* Geteilt von REST-Controller (addToMine) und MCP-Tool (AddMeetupToMineTool).
*/
public function addMember(User $user): bool
{
$wasMember = $this->hasMember($user);
$this->users()->syncWithoutDetaching([
$user->getKey() => ['is_leader' => false],
]);
return ! $wasMember;
}
/**
* RateLimiter-Key für Meetup-Stammdaten-Updates über das Portal-Frontend.
*/
+11
View File
@@ -36,6 +36,17 @@ class MeetupPolicy
return true;
}
/**
* Ein bestehendes Meetup zu „Meine Meetups" hinzufügen (meetup_user-Pivot als
* Mitglied, nicht als Leader). Jeder authentifizierte Nutzer darf das die
* Stammdaten bleiben dem Ersteller vorbehalten (siehe update()). Spiegelt die
* offene Semantik des AddMeetupToMineTool (MCP).
*/
public function addToMine(User $user, Meetup $meetup): bool
{
return true;
}
public function update(User $user, Meetup $meetup): bool
{
return $this->owns($user, $meetup);