src/Controller/Api/ClubLessonsController.php line 74

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Api;
  3. use App\Entity\Club;
  4. use Hateoas\HateoasBuilder;
  5. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Symfony\Component\Routing\Annotation\Route;
  9. use OpenApi\Annotations as OA;
  10. use Psr\Log\LoggerInterface;
  11. use App\Entity\ClubLesson;
  12. use App\Model\ClubLessonView;
  13. use App\Security\Roles;
  14. use Symfony\Component\Serializer\SerializerInterface;
  15. use Symfony\Contracts\Translation\TranslatorInterface;
  16. use App\Util\RequestUtil;
  17. use App\Model\ClubLessonCreate;
  18. use App\Exception\ViolationException;
  19. use App\Util\StringUtils;
  20. use App\Entity\ClubLocation;
  21. use App\Entity\Events;
  22. use App\Model\ClubLessonUpdate;
  23. use App\Security\ClubAccess;
  24. use App\Entity\EntityFinder;
  25. use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
  26. use App\Exception\CRMException;
  27. class ClubLessonsController extends AbstractController
  28. {
  29.     private LoggerInterface $logger;
  30.     
  31.     public function __construct(LoggerInterface $logger)
  32.     {
  33.         $this->logger $logger;
  34.     }
  35.     
  36.     
  37.     /**
  38.      * @Route("/api/club/{club_uuid}/lessons", name="api_get_club_lessons", methods={"GET"}, requirements={"club_uuid"="[a-z0-9_]{2,64}"})
  39.      * @OA\Get(
  40.      *     operationId="getClubLessons",
  41.      *     tags={"Club"},
  42.      *     path="/api/club/{club_uuid}/lessons",
  43.      *     summary="Give some hours",
  44.      *     @OA\Parameter(
  45.      *         description="UUID of club",
  46.      *         in="path",
  47.      *         name="club_uuid",
  48.      *         required=true,
  49.      *         @OA\Schema(
  50.      *             format="string",
  51.      *             type="string",
  52.      *             pattern="[a-z0-9_]{2,64}"
  53.      *         )
  54.      *     ),
  55.      *     @OA\Response(
  56.      *         response="200",
  57.      *         description="Successful",
  58.      *         @OA\MediaType(
  59.      *             mediaType="application/hal+json",
  60.      *             @OA\Schema(
  61.      *                 type="array",
  62.      *                 @OA\Items(ref="#/components/schemas/ClubLesson")
  63.      *             )
  64.      *         )
  65.      *     ),
  66.      *     @OA\Response(response="404", description="Club not found", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error")))
  67.      * )
  68.      */
  69.     public function getLessons($club_uuid)
  70.     {
  71.         $doctrine $this->container->get('doctrine');
  72.         
  73.         $entityFinder = new EntityFinder($doctrine);
  74.         /** @var Club $club */
  75.         $club $entityFinder->findOneByOrThrow(Club::class, ['uuid' => $club_uuid]); // 404
  76.         
  77.         $clubLessons $this->container->get('doctrine')->getManager()
  78.             ->getRepository(ClubLesson::class)
  79.             ->findBy(['club' => $club]);
  80.         $lessonList = array();
  81.         foreach($clubLessons as &$clubLesson) {
  82.             array_push($lessonList, new ClubLessonView($club$clubLesson));
  83.         }
  84.         $hateoas HateoasBuilder::create()->build();
  85.         return new Response(
  86.             $hateoas->serialize($lessonList'json'),
  87.             Response::HTTP_OK// 200
  88.             array('Content-Type' => 'application/hal+json'));
  89.     }
  90.     /**
  91.      * @Route("/api/club/{club_uuid}/lessons/{lesson_uuid}", name="api_get_club_lesson", methods={"GET"}, requirements={"club_uuid"="[a-z0-9_]{2,64}","lesson_uuid"="[a-zA-Z0-9_]{2,64}"})
  92.      * @OA\Get(
  93.      *     operationId="getClubLesson",
  94.      *     tags={"Club"},
  95.      *     path="/api/club/{club_uuid}/lessons/{lesson_uuid}",
  96.      *     summary="Give a lesson for a club",
  97.      *     @OA\Parameter(
  98.      *         description="UUID of club",
  99.      *         in="path",
  100.      *         name="club_uuid",
  101.      *         required=true,
  102.      *         @OA\Schema(
  103.      *             format="string",
  104.      *             type="string",
  105.      *             pattern="[a-z0-9_]{2,64}"
  106.      *         )
  107.      *     ),
  108.      *     @OA\Parameter(
  109.      *         description="UUID of lesson",
  110.      *         in="path",
  111.      *         name="lesson_uuid",
  112.      *         required=true,
  113.      *         @OA\Schema(
  114.      *             format="string",
  115.      *             type="string",
  116.      *             pattern="[A-Za-z0-9_]{2,64}"
  117.      *         )
  118.      *     ),
  119.      *     @OA\Response(
  120.      *         response="200",
  121.      *         description="Successful",
  122.      *         @OA\MediaType(
  123.      *             mediaType="application/hal+json",
  124.      *             @OA\Items(ref="#/components/schemas/ClubLesson")
  125.      *         )
  126.      *     ),
  127.      *     @OA\Response(response="404", description="Club or lesson not found", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error")))
  128.      * )
  129.      */
  130.     public function getLesson(string $club_uuidstring $lesson_uuid): Response
  131.     {
  132.         $doctrine $this->container->get('doctrine');
  133.         
  134.         $entityFinder = new EntityFinder($doctrine);
  135.         /** @var Club $club */
  136.         $club $entityFinder->findOneByOrThrow(Club::class, ['uuid' => $club_uuid]); // 404
  137.         $clubLesson $entityFinder->findOneByOrThrow(ClubLesson::class, ['uuid' => $lesson_uuid'club' => $club]); // 404
  138.         
  139.         $hateoas HateoasBuilder::create()->build();
  140.         return new Response(
  141.             $hateoas->serialize(new ClubLessonView($club$clubLesson), 'json'),
  142.             Response::HTTP_OK// 200
  143.             array('Content-Type' => 'application/hal+json'));
  144.     }
  145.     
  146.     
  147.     /**
  148.      * @Route("/api/club/{club_uuid}/lessons", name="api_create_club_lessons", methods={"POST"}, requirements={"club_uuid"="[a-z0-9_]{2,64}"})
  149.      * @OA\Post(
  150.      *     operationId="createClubLesson",
  151.      *     tags={"Club"},
  152.      *     path="/api/club/{club_uuid}/lessons",
  153.      *     summary="Create a lesson for a club",
  154.      *     security = {{"basicAuth": {}}},
  155.      *     @OA\Parameter(
  156.      *         description="UUID of club",
  157.      *         in="path",
  158.      *         name="club_uuid",
  159.      *         required=true,
  160.      *         @OA\Schema(
  161.      *             format="string",
  162.      *             type="string",
  163.      *             pattern="[a-z0-9_]{2,64}"
  164.      *         )
  165.      *     ),
  166.      *     @OA\Parameter(name="X-ClientId", in="header", required=true, example="my-client-name", @OA\Schema(format="string", type="string", pattern="[a-z0-9_]{2,64}")),
  167.      *     @OA\RequestBody(
  168.      *         description="Lesson object that needs to be added",
  169.      *         required=true,
  170.      *         @OA\JsonContent(ref="#/components/schemas/ClubLessonCreate"),
  171.      *     ),
  172.      *     @OA\Response(
  173.      *         response="201",
  174.      *         description="Successful",
  175.      *         @OA\MediaType(
  176.      *             mediaType="application/hal+json",
  177.      *             @OA\Schema(ref="#/components/schemas/ClubLesson")
  178.      *         )
  179.      *     ),
  180.      *     @OA\Response(response="400", description="Unvalid data", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error"))),
  181.      *     @OA\Response(response="403", description="Forbidden to create a lesson", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error"))),
  182.      *     @OA\Response(response="404", description="Club or location not found", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error")))
  183.      * )
  184.      */
  185.     public function createLesson(string $club_uuidRequest $requestSerializerInterface $serializerTranslatorInterface $translator): Response
  186.     {
  187.         $doctrine $this->container->get('doctrine');
  188.         
  189.         $entityFinder = new EntityFinder($doctrine);
  190.         /** @var Club $club */
  191.         $club $entityFinder->findOneByOrThrow(Club::class, ['uuid' => $club_uuid]); // 404
  192.          
  193.         $clubAccess = new ClubAccess($this->container$this->logger);
  194.         $clubAccess->checkAccessForUser($club$this->getUser()); // 403
  195.         
  196.         $requestUtil = new RequestUtil($serializer$translator);
  197.         $lessonToCreate $requestUtil->validate($requestClubLessonCreate::class); // 400
  198.         
  199.         $uuid $lessonToCreate->getUuid();
  200.         if($uuid == null || trim($uuid) === '') {
  201.             $uuid StringUtils::random_str(16);
  202.         }
  203.         
  204.         $locationUuid $lessonToCreate->getLocationUuid();
  205.         if(empty($locationUuid)) {
  206.             $locations $doctrine->getManager()
  207.                ->getRepository(ClubLocation::class)
  208.                ->findBy(['club' => $club]);
  209.            if(empty($locations)) {
  210.                throw $this->createNotFoundException('Location not found'); // 404
  211.            }
  212.            if(count($locations) > 1) {
  213.                throw new CRMException(Response::HTTP_BAD_REQUEST'Too many locations found, set a \'location_uuid\'');
  214.            }
  215.            $location $locations[0];
  216.         } else {
  217.             $location $entityFinder->findOneByOrThrow(ClubLocation::class, ['uuid' => $locationUuid]); // 404
  218.         }
  219.         
  220.         if($lessonToCreate->getStartTime() > $lessonToCreate->getEndTime()) {
  221.             throw new CRMException(Response::HTTP_BAD_REQUEST'start_time is after end_time !', ['start_time' => 'start_time is after end_time']); // 400
  222.         }
  223.         
  224.         $lesson = new ClubLesson();
  225.         $lesson->setUuid($uuid);
  226.         $lesson->setClubLocation($location);
  227.         $lesson->setClub($club);
  228.         $lesson->setPoint($lessonToCreate->getPoint());
  229.         $lesson->setDiscipline($lessonToCreate->getDiscipline());
  230.         $lesson->setAgeLevel($lessonToCreate->getAgeLevel());
  231.         $lesson->setDayOfWeek($lessonToCreate->getDayOfWeek());
  232.         $lesson->setStartTime($lessonToCreate->getStartTime());
  233.         $lesson->setEndTime($lessonToCreate->getEndTime());
  234.         $lesson->setDescription(StringUtils::defaultOrEmpty($lessonToCreate->getDescription()));
  235.         $doctrine->getManager()->persist($lesson);
  236.         
  237.         $data = ['day' => $lesson->getDayOfWeek(), 'uuid' => $uuid'start' => $lesson->getStartTime(), 'discipline' => $lesson->getDiscipline()];
  238.         Events::add($doctrineEvents::CLUB_LESSON_CREATED$this->getUser(), $request$data);
  239.         $this->logger->debug('Club lesson created: '.json_encode($data));
  240.         
  241.         $hateoas HateoasBuilder::create()->build();
  242.         return new Response(
  243.             $hateoas->serialize(new ClubLessonView($club$lesson), 'json'),
  244.             Response::HTTP_CREATED// 201
  245.             array('Content-Type' => 'application/hal+json'));
  246.     }
  247.     
  248.     
  249.     /**
  250.      * @Route("/api/club/{club_uuid}/lessons/{lesson_uuid}", name="api_update_club_lessons", methods={"PATCH"}, requirements={"club_uuid"="[a-z0-9_]{2,64}","lesson_uuid"="[a-zA-Z0-9_]{2,64}"})
  251.      * @OA\Patch(
  252.      *     operationId="updateClubLesson",
  253.      *     tags={"Club"},
  254.      *     path="/api/club/{club_uuid}/lessons/{lesson_uuid}",
  255.      *     summary="Update a lesson for a club",
  256.      *     security = {{"basicAuth": {}}},
  257.      *     @OA\Parameter(name="X-ClientId", in="header", required=true, example="my-client-name", @OA\Schema(format="string", type="string", pattern="[a-z0-9_]{2,64}")),
  258.      *     @OA\Parameter(
  259.      *         description="UUID of club",
  260.      *         in="path",
  261.      *         name="club_uuid",
  262.      *         required=true,
  263.      *         @OA\Schema(
  264.      *             format="string",
  265.      *             type="string",
  266.      *             pattern="[a-z0-9_]{2,64}"
  267.      *         )
  268.      *     ),
  269.      *     @OA\Parameter(
  270.      *         description="UUID of lesson",
  271.      *         in="path",
  272.      *         name="lesson_uuid",
  273.      *         required=true,
  274.      *         @OA\Schema(
  275.      *             format="string",
  276.      *             type="string",
  277.      *             pattern="[A-Za-z0-9_]{2,64}"
  278.      *         )
  279.      *     ),
  280.      *     @OA\RequestBody(
  281.      *         description="Lesson object that needs to be added",
  282.      *         required=true,
  283.      *         @OA\JsonContent(ref="#/components/schemas/ClubLessonUpdate"),
  284.      *     ),
  285.      *     @OA\Response(response="204", description="Successful"),
  286.      *     @OA\Response(response="400", description="Unvalid data", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error"))),
  287.      *     @OA\Response(response="403", description="Forbidden to update a lesson", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error"))),
  288.      *     @OA\Response(response="404", description="Club or location or lesson not found", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error")))
  289.      * )
  290.      */
  291.     public function updateLesson(string $club_uuidstring $lesson_uuidRequest $requestSerializerInterface $serializerTranslatorInterface $translator): Response
  292.     {
  293.         $doctrine $this->container->get('doctrine');
  294.         
  295.         $requestUtil = new RequestUtil($serializer$translator);
  296.         $lessonToUpdate $requestUtil->validate($requestClubLessonUpdate::class); // 400
  297.         
  298.         $entityFinder = new EntityFinder($doctrine);
  299.         /** @var Club $club */
  300.         $club $entityFinder->findOneByOrThrow(Club::class, ['uuid' => $club_uuid]); // 404
  301.         
  302.         $clubAccess = new ClubAccess($this->container$this->logger);
  303.         $clubAccess->checkAccessForUser($club$this->getUser()); // 403
  304.         
  305.         $lesson $entityFinder->findOneByOrThrow(ClubLesson::class, ['uuid' => $lesson_uuid'club' => $club]); // 404
  306.         
  307.         $uuid $lessonToUpdate->getUuid();
  308.         if( ! empty($uuid) && $uuid !== $lesson->getUuid()) {
  309.             $entityFinder->findNoneByOrThrow(ClubLesson::class, ['uuid' => $uuid],
  310.                 function() use($uuid) {
  311.                     throw new CRMException(Response::HTTP_BAD_REQUEST'Lesson UUID already used: '.$uuid, ['uuid' => 'UUID already used']); // 400
  312.                 });
  313.         }
  314.         
  315.         $locationUuid $lessonToUpdate->getLocationUuid();
  316.         if(! empty($locationUuid)) {
  317.             // check if location exists
  318.             $entityFinder->findOneByOrThrow(ClubLocation::class, ['uuid' => $locationUuid]); // 404
  319.         }
  320.         
  321.         if($lessonToUpdate->getStartTime() !== null && $lessonToUpdate->getEndTime() != null && $lessonToUpdate->getStartTime() > $lessonToUpdate->getEndTime()) {
  322.             throw new CRMException(Response::HTTP_BAD_REQUEST'start_time is after end_time !', ['start_time' => 'start_time is after end_time']); // 400
  323.         }
  324.         
  325.         $location $entityFinder->findOneByOrThrow(ClubLocation::class, ['uuid' => $locationUuid]); // 404
  326.             
  327.         $entityUpdater = new EntityUpdater($doctrine$request$this->getUser(), Events::CLUB_LESSON_UPDATED$this->logger);
  328.         $entityUpdater->update('uuid'$uuid$lesson->getUuid(), function($v) use($lesson) { $lesson->setUuid($v); });
  329.         if(! empty($locationUuid)) {
  330.             $entityUpdater->update('location'$location$lesson->getClubLocation(), function($v) use($lesson) { $lesson->setClubLocation($v); });
  331.         }
  332.         $entityUpdater->update('point'$lessonToUpdate->getPoint(), $lesson->getPoint(), function($v) use($lesson) { $lesson->setPoint($v); });
  333.         $entityUpdater->update('discipline'$lessonToUpdate->getDiscipline(), $lesson->getDiscipline(), function($v) use($lesson) { $lesson->setDiscipline($v); });
  334.         $entityUpdater->update('agelevel'$lessonToUpdate->getAgeLevel(), $lesson->getAgeLevel(), function($v) use($lesson) { $lesson->setAgeLevel($v); });
  335.         $entityUpdater->update('day'$lessonToUpdate->getDayOfWeek(), $lesson->getDayOfWeek(), function($v) use($lesson) { $lesson->setDayOfWeek($v); });
  336.         $entityUpdater->update('start'$lessonToUpdate->getStartTime(), $lesson->getStartTime(), function($v) use($lesson) { $lesson->setStartTime($v); });
  337.         $entityUpdater->update('end'$lessonToUpdate->getEndTime(), $lesson->getEndTime(), function($v) use($lesson) { $lesson->setEndTime($v); });
  338.         $entityUpdater->update('description'$lessonToUpdate->getDescription(), $lesson->getDescription(), function($v) use($lesson) { $lesson->setDescription($v); });
  339.         return $entityUpdater->toResponse($lesson'Club lesson updated', ['id' => $lesson->getId()]);
  340.     }
  341.     
  342.     /**
  343.      * @Route("/api/club/{club_uuid}/lessons/{lesson_uuid}", name="api_delete_club_lessons", methods={"DELETE"}, requirements={"club_uuid"="[a-z0-9_]{2,64}","lesson_uuid"="[a-zA-Z0-9_]{2,64}"})
  344.      * @OA\Delete(
  345.      *     operationId="deleteClubLesson",
  346.      *     tags={"Club"},
  347.      *     path="/api/club/{club_uuid}/lessons/{lesson_uuid}",
  348.      *     summary="Delete a lesson for a club",
  349.      *     security = {{"basicAuth": {}}},
  350.      *     @OA\Parameter(
  351.      *         description="UUID of club",
  352.      *         in="path",
  353.      *         name="club_uuid",
  354.      *         required=true,
  355.      *         @OA\Schema(
  356.      *             format="string",
  357.      *             type="string",
  358.      *             pattern="[a-z0-9_]{2,64}"
  359.      *         )
  360.      *     ),
  361.      *     @OA\Parameter(
  362.      *         description="UUID of lesson",
  363.      *         in="path",
  364.      *         name="lesson_uuid",
  365.      *         required=true,
  366.      *         @OA\Schema(
  367.      *             format="string",
  368.      *             type="string",
  369.      *             pattern="[A-Za-z0-9_]{2,64}"
  370.      *         )
  371.      *     ),
  372.      *     @OA\Parameter(name="X-ClientId", in="header", required=true, example="my-client-name", @OA\Schema(format="string", type="string", pattern="[a-z0-9_]{2,64}")),
  373.      *     @OA\Response(response="204", description="Successful"),
  374.      *     @OA\Response(response="403", description="Forbidden to delete a lesson", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error"))),
  375.      *     @OA\Response(response="404", description="Club or lesson not found", @OA\MediaType(mediaType="application/hal+json", @OA\Schema(ref="#/components/schemas/Error")))
  376.      * )
  377.      */
  378.     public function deleteLesson(Request $requeststring $club_uuidstring $lesson_uuid): Response
  379.     {
  380.         $doctrine $this->container->get('doctrine');
  381.         
  382.         $entityFinder = new EntityFinder($doctrine);
  383.         $club $entityFinder->findOneByOrThrow(Club::class, ['uuid' => $club_uuid]); // 404
  384.         
  385.         $clubAccess = new ClubAccess($this->container$this->logger);
  386.         $clubAccess->checkAccessForUser($club$this->getUser()); // 403
  387.         
  388.         $clubLesson $entityFinder->findOneByOrThrow(ClubLesson::class, ['uuid' => $lesson_uuid'club' => $club]); // 404
  389.         
  390.         $doctrine->getManager()->remove($clubLesson);
  391.         
  392.         $data = ['club_uuid' => $club_uuid'lesson_uuid' => $lesson_uuid'day' => $clubLesson->getDayOfWeek(), 'start' => $clubLesson->getStartTime()];
  393.         Events::add($doctrineEvents::CLUB_LESSON_DELETED$this->getUser(), $request$data);
  394.         $this->logger->debug('Club lesson deleted: '.json_encode($data));
  395.         
  396.         return new Response(''Response::HTTP_NO_CONTENT); // 204
  397.     }
  398. }