src/Controller/Rest/CustomerController.php line 212

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controller\Rest;
  4. use App\Entity\Appointment;
  5. use App\Entity\Customer;
  6. use App\Repository\AppointmentRepository;
  7. use App\Repository\CustomerRepository;
  8. use Doctrine\ORM\EntityManagerInterface;
  9. use Ksante\CoreBundle\Entity\Channel;
  10. use Ksante\CoreBundle\Repository\ChannelRepository;
  11. use Psr\Log\LoggerInterface;
  12. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  13. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  14. use Symfony\Component\HttpFoundation\JsonResponse;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  18. use Symfony\Component\Routing\Annotation\Route;
  19. use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
  20. use Symfony\Component\Validator\Validator\ValidatorInterface;
  21. /**
  22.  * @Route("/api", name="api_")
  23.  */
  24. class CustomerController extends AbstractController
  25. {
  26.     private CustomerRepository $customerRepository;
  27.     private AppointmentRepository $appointmentRepository;
  28.     private ChannelRepository $channelsRepository;
  29.     private EntityManagerInterface $entityManager;
  30.     private ValidatorInterface $validator;
  31.     private LoggerInterface $logger;
  32.     public function __construct(
  33.         CustomerRepository $customerRepository,
  34.         AppointmentRepository $appointmentRepository,
  35.         ChannelRepository $channelsRepository,
  36.         EntityManagerInterface $entityManager,
  37.         ValidatorInterface $validator,
  38.         LoggerInterface $logger,
  39.     ) {
  40.         $this->customerRepository $customerRepository;
  41.         $this->appointmentRepository $appointmentRepository;
  42.         $this->channelsRepository $channelsRepository;
  43.         $this->entityManager $entityManager;
  44.         $this->validator $validator;
  45.         $this->logger $logger;
  46.     }
  47.     /**
  48.      * @Route("/customer", name="get_customer_details", methods={"GET"})
  49.      */
  50.     public function getCurrentAppointment(Request $request): JsonResponse
  51.     {
  52.         $data json_decode($request->getContent(), true);
  53.         if ([] === $data) {
  54.             return $this->json(['errors' => ['form' => 'Invalid data']], Response::HTTP_BAD_REQUEST, [], [AbstractNormalizer::GROUPS => ['read']]);
  55.         }
  56.         $channel $this->channelsRepository->findOneBy(['code' => $data['channel_code']]);
  57.         if (!$channel instanceof Channel) {
  58.             throw new NotFoundHttpException('Channel not found.');
  59.         }
  60.         $customer $this->customerRepository->findOneByChannelAndExternalId($channel$data['customer_external_id']);
  61.         $appointment $this->appointmentRepository->findNextOneByCustomer($customer$channel);
  62.         $canTake $customer->getAppointmentCredit() > || null != $appointment true false;
  63.         $lastAppointment $this->appointmentRepository->findLastOneByCustomer($customer$channel);
  64.         return $this->json(
  65.             [
  66.                 'is_first_appointment' => $this->appointmentRepository->isFirstAppointment($customer),
  67.                 'has_current_appointment' => null === $appointment false true,
  68.                 'current_appointment_date' => $appointment?->getDate(),
  69.                 'current_appointment' => [
  70.                     'id' => $appointment?->getId(),
  71.                     'date' => $appointment?->getDate()->format('Y-m-d H:i:s'),
  72.                 ],
  73.                 'last_appointment' => null !== $lastAppointment $this->formatAppointmentDate($lastAppointment) : '',
  74.                 'has_diet' => $customer->hasDiet(),
  75.                 'diet' => $customer->getDiet(),
  76.                 'appointment_credit' => $customer->getAppointmentCredit(),
  77.                 'cancelled_appointment_count' => $customer?->getCancelledAppointments(),
  78.                 'can_take' => $canTake,
  79.             ],
  80.             Response::HTTP_OK,
  81.             [],
  82.             [AbstractNormalizer::GROUPS => ['customer:read']]
  83.         );
  84.     }
  85.     /**
  86.      * Returns the customer diet details filtered by channel Id
  87.      *
  88.      * @Route("/customer/{customerId}/diet-details", name="get_customer_diet_details", methods={"GET"})
  89.      *
  90.      * @IsGranted("ROLE_API")
  91.      */
  92.     public function getDietDetailsByCustomerId(int $customerId): JsonResponse
  93.     {
  94.         $customer $this->customerRepository->findOneBy(['customerId' => $customerId]);
  95.         if (!$customer instanceof Customer || $customer->getAppointments()->isEmpty()) {
  96.             return new JsonResponse(nullResponse::HTTP_NOT_FOUND);
  97.         }
  98.         $dietDetails = [];
  99.         foreach ($customer->getAppointments() as $appointment) {
  100.             $diet $appointment->getDiet();
  101.             $channel $appointment->getChannel();
  102.             $lastAppointment $this->appointmentRepository->findLastOneByCustomer($customer$channel);
  103.             $nextAppointment $this->appointmentRepository->findNextOneByCustomer($customer$channel);
  104.             $appointmentCredit $this->appointmentRepository->countByCustomer($customer$channel);
  105.             $dietDetails[$channel->getCode()] = [
  106.                 'diet' => null !== $diet $diet->getFullName() : '',
  107.                 'last_appointment' => null !== $lastAppointment $this->formatAppointmentDate($lastAppointment) : '',
  108.                 'next_appointment' => null !== $nextAppointment $this->formatAppointmentDate($nextAppointment) : '',
  109.                 'appointment_credit' => $appointmentCredit,
  110.             ];
  111.         }
  112.         return $this->json($dietDetailsResponse::HTTP_OK);
  113.     }
  114.     /**
  115.      * Cancel point for a customer on a specific channel
  116.      *
  117.      * @Route("/customer/cancel-point", name="cancel_customer_point", methods={"POST"})
  118.      *
  119.      * @IsGranted("ROLE_API")
  120.      */
  121.     public function cancelCustomerPoint(Request $request): JsonResponse
  122.     {
  123.         $data json_decode($request->getContent(), true);
  124.         $point = (int) $data['points'];
  125.         $channelCode $data['channelCode'];
  126.         $channel $this->channelsRepository->findOneBy(['code' => $channelCode]);
  127.         if (!$channel instanceof Channel) {
  128.             throw new NotFoundHttpException('Channel not found.');
  129.         }
  130.         if (=== (int) $data['customerExternalId'] || >= $point) {
  131.             return new JsonResponse('Invalid data provided'Response::HTTP_BAD_REQUEST);
  132.         }
  133.         $customer $this->customerRepository->findOneByChannelAndExternalId($channel$data['customerExternalId']);
  134.         if (!$customer instanceof Customer) {
  135.             return new JsonResponse('Customer not found.'Response::HTTP_NOT_FOUND);
  136.         }
  137.         if (=== $customer->getAppointmentCredit() || $customer->getAppointmentCredit() < $point) {
  138.             return new JsonResponse('Points can\'t be a negative number.'Response::HTTP_UNAUTHORIZED);
  139.         }
  140.         $customer->setAppointmentCredit($customer->getAppointmentCredit() - $point);
  141.         $this->entityManager->persist($customer);
  142.         $this->entityManager->flush();
  143.         return $this->json(['customer' => $customer], Response::HTTP_OK, [], [AbstractNormalizer::GROUPS => ['read']]);
  144.     }
  145.     /**
  146.      * @Route("/customers/{externalId}/add-credit", name="customer_add_appointments_credit", methods={"PATCH"})
  147.      *
  148.      * @IsGranted("ROLE_API")
  149.      */
  150.     public function addAppointmentCredit(Request $requeststring $externalId): JsonResponse
  151.     {
  152.         $payload json_decode($request->getContent(), true);
  153.         if (null === $payload || === \count($payload) || !\array_key_exists('channel_code'$payload) || null === $payload['channel_code'] || '' === trim($payload['channel_code'])) {
  154.             return new JsonResponse([
  155.                 'code' => Response::HTTP_BAD_REQUEST,
  156.                 'message' => 'The channel_code is required',
  157.             ], Response::HTTP_BAD_REQUEST);
  158.         }
  159.         $channel $this->channelsRepository->findOneBy(['code' => $payload['channel_code']]);
  160.         if (null === $channel) {
  161.             return new JsonResponse([
  162.                 'code' => Response::HTTP_NOT_FOUND,
  163.                 'message' => 'Channel not found',
  164.             ], Response::HTTP_NOT_FOUND);
  165.         }
  166.         $customer $this->customerRepository->findOneByChannelAndExternalId($channel$externalId);
  167.         if (null === $customer) {
  168.             $customer = new Customer();
  169.             $customer->setExternalId((string) $externalId);
  170.             $customer->setChannel($channel);
  171.             $this->entityManager->persist($customer);
  172.         }
  173.         $customer->setFirstName($payload['first_name'] ?? $customer->getFirstName());
  174.         $customer->setLastName($payload['last_name'] ?? $customer->getLastName());
  175.         $customer->setEmail($payload['email'] ?? $customer->getEmail());
  176.         $customer->setPhone($payload['phone_number'] ?? $customer->getPhone());
  177.         $violations $this->validator->validate($customer);
  178.         if (!== \count($violations)) {
  179.             $errors = [];
  180.             foreach ($violations as $violation) {
  181.                 $input mb_strtolower(preg_replace('/(?<!^)[A-Z]/''_$0'$violation->getPropertyPath()));
  182.                 $errors[$input] = $violation->getMessage();
  183.             }
  184.             $this->logger->error('Couldn t save customer data'$errors);
  185.             return new JsonResponse([
  186.                 'code' => Response::HTTP_BAD_REQUEST,
  187.                 'message' => $errors,
  188.             ], Response::HTTP_BAD_REQUEST);
  189.         }
  190.         $points \array_key_exists('points'$payload) ? (int) $payload['points'] : 1;
  191.         $customer->setAppointmentCredit($customer->getAppointmentCredit() + $points);
  192.         $this->entityManager->flush();
  193.         return new JsonResponse('Credit has been successfully added');
  194.     }
  195.     /**
  196.      * Delete customer data ( GDPR )
  197.      *
  198.      * @Route("/customer/{customerId}/data", name="delete_customer_data", methods={"DELETE"})
  199.      *
  200.      * @IsGranted("ROLE_API")
  201.      *
  202.      * @param int $customerId
  203.      *
  204.      * @return JsonResponse
  205.      */
  206.     public function deleteCustomerData(int $customerId): JsonResponse
  207.     {
  208.         $customers $this->customerRepository->findBy(['customerId' => $customerId]);
  209.         foreach ($customers as $customer) {
  210.             $customerAppointments $this->appointmentRepository->findBy(['customer' => $customer->getId()]);
  211.             foreach ($customerAppointments as $customerAppointment) {
  212.                 $this->entityManager->remove($customerAppointment);
  213.             }
  214.             $this->entityManager->remove($customer);
  215.         }
  216.         $this->entityManager->flush();
  217.         return new JsonResponse('Customer data deleted successfully'Response::HTTP_OK);
  218.     }
  219.     /**
  220.      * Delete the assigned diet and cancel any upcoming appointment
  221.      *
  222.      * @Route("/customers/{externalId}/delete-diet", name="customer_delete_assigned_diet", methods={"PATCH"})
  223.      *
  224.      * @IsGranted("ROLE_API")
  225.      */
  226.     public function deleteAssignedDiet(Request $requeststring $externalId): JsonResponse
  227.     {
  228.         $payload json_decode($request->getContent(), true);
  229.         if (null === $payload || === \count($payload) || !\array_key_exists('channel_code'$payload) || null === $payload['channel_code'] || '' === trim($payload['channel_code'])) {
  230.             return new JsonResponse([
  231.                 'code' => Response::HTTP_BAD_REQUEST,
  232.                 'message' => 'The channel_code is required',
  233.             ], Response::HTTP_BAD_REQUEST);
  234.         }
  235.         $channel $this->channelsRepository->findOneBy(['code' => $payload['channel_code']]);
  236.         if (null === $channel) {
  237.             return new JsonResponse([
  238.                 'code' => Response::HTTP_NOT_FOUND,
  239.                 'message' => 'Channel not found',
  240.             ], Response::HTTP_NOT_FOUND);
  241.         }
  242.         $customer $this->customerRepository->findOneByChannelAndExternalId($channel$externalId);
  243.         if (null === $customer) {
  244.             return new JsonResponse([
  245.                 'code' => Response::HTTP_NOT_FOUND,
  246.                 'message' => 'Customer not found',
  247.             ], Response::HTTP_NOT_FOUND);
  248.         }
  249.         if (null === $customer->getDiet()) {
  250.             return new JsonResponse([
  251.                 'code' => Response::HTTP_BAD_REQUEST,
  252.                 'message' => 'Invalid request',
  253.             ], Response::HTTP_BAD_REQUEST);
  254.         }
  255.         $appointments $this->appointmentRepository->findUpcomingByDietCustomerAndChannel($customer->getDiet(), $customer$channel);
  256.         if ([] !== $appointments) {
  257.             foreach ($appointments as $appointment) {
  258.                 $appointment->setStatus(Appointment::STATUS_CANCELED);
  259.                 $appointment->setReminderStatus(Appointment::EMAIL_REMINDER_CANCELED);
  260.                 $customer->incrementAppointmentCredit();
  261.             }
  262.         }
  263.         $customer->setDiet(null);
  264.         $this->entityManager->flush();
  265.         return new JsonResponse('Diet successfully unassigned');
  266.     }
  267.     /**
  268.      * Concat and format appointment date and time
  269.      *
  270.      * @param Appointment $appointment
  271.      *
  272.      * @return string
  273.      */
  274.     private function formatAppointmentDate(Appointment $appointment): string
  275.     {
  276.         return date(\DATE_ISO8601strtotime($appointment->getDate()->format('Y-m-d H:i:s')));
  277.     }
  278. }