diff --git a/README.md b/README.md index 0a29c9a0b..0699ceb77 100755 --- a/README.md +++ b/README.md @@ -197,3 +197,7 @@ IDP_CLIENT_SECRET=client-secret * 601: The amount of cash that will be added, has to be more than zero *EventController 7xx* * 701: Event creation parameters are missing +*EventParticipationController 8xx* + * 801: User has no permission to enter or leave + * 802: User could not join the event + * 803: User could not leave the event \ No newline at end of file diff --git a/psalm.baseline.xml b/psalm.baseline.xml index a3f68a853..1516c5bd5 100755 --- a/psalm.baseline.xml +++ b/psalm.baseline.xml @@ -153,8 +153,7 @@ - - getProfile + getProfile diff --git a/src/Mealz/MealBundle/Controller/EventController.php b/src/Mealz/MealBundle/Controller/EventController.php index 5ff2e32e0..221aa3710 100644 --- a/src/Mealz/MealBundle/Controller/EventController.php +++ b/src/Mealz/MealBundle/Controller/EventController.php @@ -4,11 +4,15 @@ namespace App\Mealz\MealBundle\Controller; +use App\Mealz\MealBundle\Entity\Day; use App\Mealz\MealBundle\Entity\Event; +use App\Mealz\MealBundle\Event\EventParticipationUpdateEvent; use App\Mealz\MealBundle\Repository\EventRepositoryInterface; +use App\Mealz\MealBundle\Service\EventParticipationService; use Doctrine\ORM\EntityManagerInterface; use Exception; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -19,13 +23,19 @@ class EventController extends BaseListController { private EntityManagerInterface $em; private EventRepositoryInterface $eventRepo; + private EventDispatcherInterface $eventDispatcher; + private EventParticipationService $eventPartSrv; public function __construct( EntityManagerInterface $entityManager, - EventRepositoryInterface $eventRepository + EventRepositoryInterface $eventRepository, + EventDispatcherInterface $eventDispatcher, + EventParticipationService $eventPartSrv ) { $this->em = $entityManager; $this->eventRepo = $eventRepository; + $this->eventDispatcher = $eventDispatcher; + $this->eventPartSrv = $eventPartSrv; } public function getEventList(): JsonResponse @@ -88,4 +98,38 @@ public function delete(Event $event): JsonResponse return new JsonResponse(['message' => $e->getMessage(), 500]); } } + + public function join(Day $day): JsonResponse + { + $profile = $this->getProfile(); + if (null === $profile) { + return new JsonResponse(['messasge' => '801: User is not allowed to join'], 403); + } + + $eventParticipation = $this->eventPartSrv->join($profile, $day); + if (null === $eventParticipation) { + return new JsonResponse(['messasge' => '802: User could not join the event'], 500); + } + + $this->eventDispatcher->dispatch(new EventParticipationUpdateEvent($eventParticipation)); + + return new JsonResponse(['isParticipating' => true], 200); + } + + public function leave(Day $day): JsonResponse + { + $profile = $this->getProfile(); + if (null === $profile) { + return new JsonResponse(['messasge' => '801: User is not allowed to leave'], 403); + } + + $eventParticipation = $this->eventPartSrv->leave($profile, $day); + if (null === $eventParticipation) { + return new JsonResponse(['messasge' => '802: User could not leave the event'], 500); + } + + $this->eventDispatcher->dispatch(new EventParticipationUpdateEvent($eventParticipation)); + + return new JsonResponse(['isParticipating' => false], 200); + } } diff --git a/src/Mealz/MealBundle/Entity/EventParticipation.php b/src/Mealz/MealBundle/Entity/EventParticipation.php index 4926d9aef..977d42e5a 100644 --- a/src/Mealz/MealBundle/Entity/EventParticipation.php +++ b/src/Mealz/MealBundle/Entity/EventParticipation.php @@ -96,4 +96,14 @@ public function getParticipants(): ArrayCollection return new ArrayCollection($this->participants->toArray()); } + + public function addParticipant(Participant $participant): void + { + $this->participants->add($participant); + } + + public function removeParticipant(Participant $participant): bool + { + return $this->participants->removeElement($participant); + } } diff --git a/src/Mealz/MealBundle/Entity/Participant.php b/src/Mealz/MealBundle/Entity/Participant.php index 4fee8a2c9..a031d1be0 100644 --- a/src/Mealz/MealBundle/Entity/Participant.php +++ b/src/Mealz/MealBundle/Entity/Participant.php @@ -32,9 +32,9 @@ class Participant * @Assert\NotNull() * @Assert\Type(type="App\Mealz\MealBundle\Entity\Meal") * @ORM\ManyToOne(targetEntity="Meal",inversedBy="participants") - * @ORM\JoinColumn(name="meal_id", referencedColumnName="id") + * @ORM\JoinColumn(name="meal_id", referencedColumnName="id", nullable=true) */ - private Meal $meal; + private ?Meal $meal; /** * @Assert\NotNull() @@ -85,10 +85,11 @@ class Participant */ private bool $confirmed = false; - public function __construct(Profile $profile, ?Meal $meal) + public function __construct(Profile $profile, ?Meal $meal, ?EventParticipation $eventParticipation = null) { $this->profile = $profile; $this->meal = $meal; + $this->event = $eventParticipation; $this->combinedDishes = new DishCollection(); } @@ -137,7 +138,7 @@ public function setMeal(Meal $meal): void $this->meal = $meal; } - public function getMeal(): Meal + public function getMeal(): ?Meal { return $this->meal; } diff --git a/src/Mealz/MealBundle/Event/MealOfferAcceptedEvent.php b/src/Mealz/MealBundle/Event/MealOfferAcceptedEvent.php index 214b7fcb7..0db5924d3 100644 --- a/src/Mealz/MealBundle/Event/MealOfferAcceptedEvent.php +++ b/src/Mealz/MealBundle/Event/MealOfferAcceptedEvent.php @@ -30,7 +30,7 @@ public function __construct(Participant $participant, Profile $offerer) $this->offerer = $offerer; } - public function getMeal(): Meal + public function getMeal(): ?Meal { return $this->participant->getMeal(); } diff --git a/src/Mealz/MealBundle/Event/MealOfferCancelledEvent.php b/src/Mealz/MealBundle/Event/MealOfferCancelledEvent.php index 995154a9d..aed51d73d 100644 --- a/src/Mealz/MealBundle/Event/MealOfferCancelledEvent.php +++ b/src/Mealz/MealBundle/Event/MealOfferCancelledEvent.php @@ -20,7 +20,7 @@ public function __construct(Participant $participant) $this->participant = $participant; } - public function getMeal(): Meal + public function getMeal(): ?Meal { return $this->participant->getMeal(); } diff --git a/src/Mealz/MealBundle/Event/MealOfferedEvent.php b/src/Mealz/MealBundle/Event/MealOfferedEvent.php index a37f5b1c6..fac46002f 100644 --- a/src/Mealz/MealBundle/Event/MealOfferedEvent.php +++ b/src/Mealz/MealBundle/Event/MealOfferedEvent.php @@ -17,7 +17,7 @@ public function __construct(Participant $participant) $this->participant = $participant; } - public function getMeal(): Meal + public function getMeal(): ?Meal { return $this->participant->getMeal(); } diff --git a/src/Mealz/MealBundle/Resources/config/routing.yml b/src/Mealz/MealBundle/Resources/config/routing.yml index cdf10aae6..fae823953 100644 --- a/src/Mealz/MealBundle/Resources/config/routing.yml +++ b/src/Mealz/MealBundle/Resources/config/routing.yml @@ -187,6 +187,16 @@ MealzMealBundle_api_events_delete: defaults: { _controller: App\Mealz\MealBundle\Controller\EventController::delete } methods: [ DELETE ] +MealzMealBundle_api_events_join: + path: /api/events/participation + defaults: { _controller: App\Mealz\MealBundle\Controller\EventController::join } + methods: [ POST ] + +MealzMealBundle_api_events_leave: + path: /api/events/participation + defaults: { _controller: App\Mealz\MealBundle\Controller\EventController::leave } + methods: [ DELETE ] + MealzMealBundle_Meal_offers: path: /menu/{date}/{dish}/offers defaults: { _controller: App\Mealz\MealBundle\Controller\MealController::getOffers } diff --git a/src/Mealz/MealBundle/Service/Doorman.php b/src/Mealz/MealBundle/Service/Doorman.php index f1ef7c2c2..5c77570e9 100644 --- a/src/Mealz/MealBundle/Service/Doorman.php +++ b/src/Mealz/MealBundle/Service/Doorman.php @@ -2,6 +2,7 @@ namespace App\Mealz\MealBundle\Service; +use App\Mealz\MealBundle\Entity\EventParticipation; use App\Mealz\MealBundle\Entity\Meal; use App\Mealz\MealBundle\Entity\Participant; use App\Mealz\UserBundle\Entity\Profile; @@ -66,6 +67,15 @@ public function isUserAllowedToJoin(Meal $meal, array $dishSlugs = []): bool && $this->hasAccessTo(self::AT_MEAL_PARTICIPATION, ['meal' => $meal]); } + public function isUserAllowedToJoinEvent(EventParticipation $eventParticipation): bool + { + if (false === $this->security->getUser()->getProfile() instanceof Profile) { + return false; + } + + return $this->isToggleParticipationAllowed($eventParticipation->getDay()->getLockParticipationDateTime()); + } + public function isOfferAvailable(Meal $meal): bool { if (false === $this->security->getUser()->getProfile() instanceof Profile) { diff --git a/src/Mealz/MealBundle/Service/EventParticipationService.php b/src/Mealz/MealBundle/Service/EventParticipationService.php index 6af20fbe5..5e990664e 100644 --- a/src/Mealz/MealBundle/Service/EventParticipationService.php +++ b/src/Mealz/MealBundle/Service/EventParticipationService.php @@ -5,6 +5,7 @@ use App\Mealz\MealBundle\Entity\Day; use App\Mealz\MealBundle\Entity\Event; use App\Mealz\MealBundle\Entity\EventParticipation; +use App\Mealz\MealBundle\Entity\Participant; use App\Mealz\MealBundle\Repository\EventParticipationRepositoryInterface; use App\Mealz\MealBundle\Repository\EventRepositoryInterface; use App\Mealz\UserBundle\Entity\Profile; @@ -12,15 +13,18 @@ class EventParticipationService { + private Doorman $doorman; private EntityManagerInterface $em; private EventParticipationRepositoryInterface $eventPartRepo; private EventRepositoryInterface $eventRepo; public function __construct( + Doorman $doorman, EntityManagerInterface $em, EventRepositoryInterface $eventRepo, EventParticipationRepositoryInterface $eventPartRepo ) { + $this->doorman = $doorman; $this->em = $em; $this->eventPartRepo = $eventPartRepo; $this->eventRepo = $eventRepo; @@ -55,6 +59,36 @@ public function getEventParticipationData(Day $day, Profile $profile): ?array ]; } + public function join(Profile $profile, Day $day): ?EventParticipation + { + $eventParticipation = $day->getEvent(); + if (null !== $eventParticipation && true === $this->doorman->isUserAllowedToJoinEvent($eventParticipation)) { + $participation = $this->createEventParticipation($profile, $eventParticipation); + if (null !== $participation) { + $this->em->persist($participation); + $this->em->flush(); + + $eventParticipation->addParticipant($participation); + + return $eventParticipation; + } + } + + return null; + } + + public function leave(Profile $profile, Day $day): ?EventParticipation + { + $eventParticipation = $day->getEvent(); + $participation = $eventParticipation->getParticipant($profile); + + $eventParticipation->removeParticipant($participation); + $this->em->remove($participation); + $this->em->flush(); + + return $eventParticipation; + } + private function addEventToDay(Day $day, ?Event $event) { // new eventparticipation @@ -74,4 +108,9 @@ private function removeEventFromDay(Day $day) $day->setEvent(null); } } + + private function createEventParticipation(Profile $profile, EventParticipation $eventParticiation): ?Participant + { + return new Participant($profile, null, $eventParticiation); + } } diff --git a/src/Mealz/MealBundle/Service/ParticipationServiceTrait.php b/src/Mealz/MealBundle/Service/ParticipationServiceTrait.php index 204b677ae..157e14f3d 100644 --- a/src/Mealz/MealBundle/Service/ParticipationServiceTrait.php +++ b/src/Mealz/MealBundle/Service/ParticipationServiceTrait.php @@ -104,7 +104,7 @@ private function slotIsAvailable(Slot $slot, DateTime $date): bool */ private function createParticipation(Profile $profile, Meal $meal, ?Slot $slot = null, array $dishSlugs = []): Participant { - $participant = new Participant($profile, $meal); + $participant = new Participant($profile, $meal, null); if (null !== $slot) { $participant->setSlot($slot); }