src/Controller/Front/CourseController.php line 146

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Front;
  3. use DateTime;
  4. use App\Entity\Alert;
  5. use App\Entity\Course;
  6. use App\Entity\Trainee;
  7. use App\Entity\SearchCourse;
  8. use App\Entity\FeaturedCourse;
  9. use App\Form\SearchCourseType;
  10. use App\Service\CourseService;
  11. use App\Service\GoogleService;
  12. use App\Entity\CourseViewsStat;
  13. use App\Entity\CourseAvailability;
  14. use App\Entity\SearchAvailability;
  15. use App\Repository\AlertRepository;
  16. use App\Form\SearchAvailabilityType;
  17. use App\Repository\BookingRepository;
  18. use App\Repository\CourseRepository;
  19. use App\Service\InternationalService;
  20. use Doctrine\Persistence\ObjectManager;
  21. use Doctrine\ORM\EntityManagerInterface;
  22. use Knp\Component\Pager\PaginatorInterface;
  23. use Symfony\Component\Serializer\Serializer;
  24. use App\Repository\CourseViewsStatRepository;
  25. use App\Repository\WebsiteLanguageRepository;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use App\Repository\TranslatedCourseRepository;
  28. use Symfony\Component\HttpFoundation\Response;
  29. use Symfony\Component\Routing\Annotation\Route;
  30. use App\Repository\CourseAvailabilityRepository;
  31. use Symfony\Component\HttpFoundation\JsonResponse;
  32. use Symfony\Component\HttpFoundation\Session\Session;
  33. use Symfony\Component\Serializer\Encoder\JsonEncoder;
  34. use Symfony\Contracts\Translation\TranslatorInterface;
  35. use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
  36. use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
  37. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  38. use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
  39. #[Route(path'{_locale}/courses')]
  40. class CourseController extends AbstractController
  41. {
  42.     private $googleApiKeyBack;
  43.     public function __construct(
  44.         private AlertRepository $alertRepository
  45.         private EntityManagerInterface $entityManager
  46.         private CourseAvailabilityRepository $courseAvailabilityRepository
  47.         private PaginatorInterface $paginator
  48.         private TranslatorInterface $translator
  49.         private InternationalService $internationalService
  50.         private TranslatedCourseRepository $translatedCourseRepository
  51.         private WebsiteLanguageRepository $websiteLanguageRepository
  52.         private GoogleService $googleService,
  53.         private CourseService $courseService,
  54.         private ContainerBagInterface $params
  55.     ){
  56.         $this->googleApiKeyBack $this->params->get('api_key_google_back');
  57.     }
  58.     function isSearchNull(SearchCourse $search) {
  59.         if ($search->getPlace() == null && count($search->getSports()) == && $search->getBegin() == null && count($search->getAges()) == && count($search->getLevels()) == && count($search->getLanguages()) == && $search->getHasLabels() == false) {
  60.             return true;
  61.         }
  62.         return false;
  63.     }
  64.     /*
  65.     #[Route(path: '/translate/courses', name: 't_courses')]
  66.     public function t_courses(EntityManagerInterface $manager, CourseRepository $courseRepository)
  67.     {
  68.         $activeCourses = $courseRepository->findBy(['status' => 'active']);
  69.         foreach ($activeCourses as $course) {
  70.             
  71.             $translatedCourses = $course->getTranslatedContents();
  72.             $baseTranslatedCourse = $translatedCourses->first();
  73.             $websiteLanguages = $this->websiteLanguageRepository->findAll();
  74.             foreach ($websiteLanguages as $language) {
  75.                 $languageSlug = $language->getSlug();
  76.                 
  77.                 $translatedCourse = $this->translatedCourseRepository->findOneBy(['course' => $course, 'language' => $languageSlug]);
  78.                 if ($translatedCourse == null) {
  79.                     $translatedCourse = new TranslatedCourse;
  80.                     try {
  81.                         $nameTranslate = $this->googleService->translateWithGoogle($baseTranslatedCourse->getName(), $languageSlug);
  82.                         $descriptionTranslate = $this->googleService->translateWithGoogle($baseTranslatedCourse->getDescription(), $languageSlug);
  83.     
  84.                         if($baseTranslatedCourse->getOthers()) {
  85.                             $othersTranslate = $this->googleService->translateWithGoogle($baseTranslatedCourse->getOthers(), $languageSlug);
  86.                         }
  87.     
  88.                         foreach($baseTranslatedCourse->getPlannings() as $planning){
  89.     
  90.                             $planningTranslate = new Planning;
  91.     
  92.                             $titlePlanningTranslate = $this->googleService->translateWithGoogle($planning->getTitle(), $languageSlug);
  93.                             $descriptionPlanningTranslate = $this->googleService->translateWithGoogle($planning->getDescription(), $languageSlug);
  94.     
  95.                             $planningTranslate->setTitle($titlePlanningTranslate)
  96.                                               ->setDescription($descriptionPlanningTranslate);
  97.     
  98.                             $translatedCourse->addPlanning($planningTranslate);
  99.     
  100.                         }
  101.     
  102.                         $translatedCourse->setName($nameTranslate)
  103.                             ->setDescription($descriptionTranslate)
  104.                             ->setOthers($othersTranslate ?? "")
  105.                             ->setLanguage($language->getSlug())
  106.                             ->setCourse($course);
  107.     
  108.                         $this->entityManager->persist($translatedCourse);
  109.     
  110.                     }
  111.                     catch(Exception $e) {
  112.                         // Créer une exeption;
  113.                     }
  114.                 }
  115.             }
  116.         }
  117.         $this->entityManager->flush();
  118.         dd("fait");
  119.     }
  120.     */
  121.     
  122.     #[Route(path'/{view}'name'courses'defaults: ['view' => 'list'])]
  123.     public function listCourses($viewCourseRepository $courseRepositoryPaginatorInterface $paginatorRequest $requestEntityManagerInterface $managerTranslatorInterface $translatorGoogleService $googleService): Response
  124.     {
  125.         $search = new SearchCourse();
  126.         $startingDate null;
  127.         $noSearchResult false;
  128.         $otherResult false;
  129.         $form $this->createForm(SearchCourseType::class, $search);
  130.         $form->handleRequest($request);
  131.         if ($request->query->get('search') && $this->isSearchNull($search)) {
  132.             return $this->redirectToRoute('courses', [
  133.                 'view' => $view
  134.             ]);
  135.         }
  136.         
  137.         if ($form->isSubmitted() && $search->getPlace() != "undefined") {
  138.             if ($view == 'map' && $search->getPlace() == null) {
  139.                 return $this->redirectToRoute('courses', [
  140.                     'view' => 'list'
  141.                 ]);
  142.             }
  143.             // Create alert for trainee
  144.             if ($request->query->get('search') == null) {
  145.                 try {
  146.                     $this->createAlert($search);
  147.                 } catch (\Throwable $th) {
  148.                     //throw $th;
  149.                 }
  150.             }
  151.             $startingDate $search->getBegin();
  152.             $coursesSearch $courseRepository->searchCourses($search);
  153.             if(count($coursesSearch) == 0) {
  154.                 $countryCode null;
  155.                 $noSearchResult true;
  156.                 if ($search->getPlace() != null && $googleService->getPlaceCountryCode($search->getPlace())) {
  157.                     $countryCode $googleService->getPlaceCountryCode($search->getPlace());
  158.                     
  159.                     $coursesSearch $courseRepository->searchCourses($search$countryCode);
  160.                     if (count($coursesSearch) > 0) {
  161.                         $otherResult true;
  162.                     }
  163.                 }
  164.                 $searchBooster $courseRepository->findFeaturedCourses(null'research'$countryCode);
  165.             }
  166.             
  167.             $courses $this->sortCourseByNextDate($coursesSearch);
  168.         } 
  169.         
  170.         else {
  171.             $courses $this->sortCourseByNextDate($courseRepository->findLatestCourses());
  172.             $search null;
  173.         }
  174.         $courses $paginator->paginate(
  175.             $courses,
  176.             $request->query->getInt('page'1),
  177.             24
  178.         );
  179.         $template 'front/course/search-course.html.twig';
  180.         if ($view == 'map') {
  181.             $template 'front/course/search-course-map.html.twig';
  182.         }
  183.         $response $this->render($template, [
  184.             'menu' => 'courses',
  185.             'courses' => $courses,
  186.             'noSearchResult' => $noSearchResult,
  187.             'otherResult' => $otherResult,
  188.             'form' => $form->createView(),
  189.             'startingDate' => $startingDate,
  190.             'criteria' => $search,
  191.             'searchBooster' => $searchBooster ?? 0,
  192.             'view' => $view
  193.         ]);
  194.     
  195.         if ($search != null) {
  196.             // Ajouter l'en-tête X-Robots-Tag
  197.             $response->headers->set('X-Robots-Tag''noindex, nofollow');
  198.         }
  199.     
  200.         return $response;
  201.         
  202.     }
  203.     
  204.     #[Route(path'-details/{token}'name'view_course')]
  205.     public function courseDetails(Course $courseRequest $requestCourseViewsStatRepository $courseViewsStatRepositoryEntityManagerInterface $managerBookingRepository $bookingRepository): Response
  206.     {
  207.         
  208.         if ($course->getOwner()->getStatus() != 'online') {
  209.             $this->addFlash(
  210.                 'info',
  211.                 $this->translator->trans("flashes.course_controller.unknown_course")
  212.             );
  213.             return $this->redirectToRoute('homepage');
  214.         }
  215.         if ($course->getStatus() == 'pending' && $course->getOwner() != $this->getUser()) {
  216.             return $this->redirectToRoute('homepage');
  217.         }
  218.         $session $request->getSession();
  219.         $translatedCourse $course->getTranslatedContent($request->getLocale());
  220.         if ($session->get('seenCourses')) {
  221.             $seenCourses $session->get('seenCourses');
  222.         } else {
  223.             $seenCourses = [];
  224.         }
  225.         $seenCourses[] = $course->getToken();
  226.         
  227.         $session->set('seenCourses'$seenCourses);
  228.         $page $request->query->getInt('page'1);
  229.         $search = new SearchAvailability();
  230.         $startingDate null;
  231.         $form $this->createForm(SearchAvailabilityType::class, $search);
  232.         $form->handleRequest($request);
  233.         if ($form->isSubmitted() && $form->isValid()) {
  234.             $startingDate $search->getBegin();
  235.         }
  236.         $availabilities $this->getAvailabilities($course$page$startingDate);
  237.         $encoder = new JsonEncoder();
  238.         $defaultContext = [
  239.             AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object$json$context) {
  240.                 return $object;
  241.             },
  242.         ];
  243.         $normalizer = new ObjectNormalizer(nullnullnullnullnullnull$defaultContext);
  244.         $serializer = new Serializer([$normalizer], [$encoder]);
  245.         $encodedAvailabilities $serializer->serialize($availabilities->getItems(), 'json');
  246.         if ($request->query->getInt('page')) {
  247.             $response = new Response();
  248.             $response->setContent($encodedAvailabilities);
  249.             $response->headers->set('Content-Type''application/json');
  250.             return $response;
  251.         }
  252.         //View stat
  253.         $ip $_SERVER['REMOTE_ADDR'];
  254.         if ($ip) {
  255.             $courseViewsStat $courseViewsStatRepository->findByIp($ip$course);
  256.             
  257.             if($courseViewsStat) {
  258.                 $origin = new \DateTime();
  259.                 $target = new \DateTime($courseViewsStat->getDate());
  260.                 $interval $origin->diff($target)->format('%a');
  261.                 
  262.                 if($interval 2){
  263.                     $courseViewsStat->setViews($courseViewsStat->getViews() + 1)
  264.                                     ->setDate(date_format(new \DateTime(), 'Y-m-d'));
  265.                 }
  266.             }
  267.             else {
  268.                 $courseViewsStat = new CourseViewsStat();
  269.                 $courseViewsStat->setCourse($course)
  270.                                 ->setDate(date_format(new \DateTime(), 'Y-m-d'))
  271.                                 ->setIp($ip)
  272.                                 ->setViews(1);
  273.                 $manager->persist($courseViewsStat);
  274.             }
  275.             $manager->flush();
  276.         }
  277.         $traineeHasPendingBooking false;
  278.         if ($this->getUser() && $this->getUser() instanceof Trainee) {
  279.             $trainee $this->getUser();
  280.             $traineePendingBooking $bookingRepository->traineeCoursePendingBooking($trainee$course);
  281.             if ($traineePendingBooking) {
  282.                 $traineeHasPendingBooking true;
  283.             }
  284.         }
  285.         return $this->render('front/course/course-details.html.twig', [
  286.             'course' => $course,
  287.             'translatedCourse' => $translatedCourse,
  288.             'availabilities' => $availabilities,
  289.             'encodedAvailabilities' => $encodedAvailabilities,
  290.             'traineeHasPendingBooking' => $traineeHasPendingBooking,
  291.             'form' => $form->createView(),
  292.             'startingDate' => $startingDate
  293.         ]);
  294.     }
  295.     public function getAvailabilities(Course $course$page$startingDate null)
  296.     {
  297.         
  298.         $nextAvailabilities $this->courseService->getAvailabilities($course'multiple'$startingDate);
  299.         usort($nextAvailabilities, array($this"cmp"));
  300.         $sortedAvailabilities $this->paginator->paginate(
  301.             $nextAvailabilities,
  302.             $page,
  303.             8
  304.         );
  305.         return $sortedAvailabilities;
  306.     }
  307.     function cmp($a$b)
  308.     {
  309.         $aBegin date_create_from_format('d.m.y'$a->getBegin());
  310.         $bBegin date_create_from_format('d.m.y'$b->getBegin());
  311.         return ($aBegin $bBegin);
  312.     }
  313.     #[Route(path'/add-course-to-favorite/{id}'name'add_course_favorite'methods: ['POST'])]
  314.     public function addToFavorite(Course $courseObjectManager $managerRequest $request)
  315.     {
  316.         $user $this->getUser();
  317.         $data json_decode($request->getContent(), true);
  318.         if($this->isCsrfTokenValid('favorite'.$course->getId(), $data['_token']))
  319.         {
  320.             if ($user instanceof Trainee) {
  321.                 if($course->getTraineesFavorites()->contains($user))
  322.                 {
  323.                     $user->removeFavorite($course);
  324.                 }
  325.                 else
  326.                 {
  327.                     $user->addFavorite($course);
  328.                 }
  329.                 $manager->flush();
  330.             }
  331.             return new JsonResponse(['success' => 1]);
  332.         }
  333.         else 
  334.         {
  335.             return new JsonResponse(['error' => 'token invalid'], 400);
  336.         }
  337.     }
  338.     #[Route(path'/set-search-destination/in-session'name'set_search_destination')]
  339.     public function setSearchDestination(Request $request)
  340.     {
  341.         $data json_decode($request->getContent(), true);
  342.         $destination $data['searchDestination'];
  343.         $session = new Session();
  344.         $session->set('destination'$destination);
  345.         return new JsonResponse(['ok' => 'ok']);
  346.     }
  347.     private function getNextDate(Course $course$startingDate null)
  348.     {
  349.         if ($startingDate == null) {
  350.             $fromDate = new DateTime();
  351.             $fromDate->setTimestamp(strtotime('today midnight'));
  352.         } else {
  353.             $fromDate date_create_from_format('d/m/Y'$startingDate);
  354.         }
  355.         $date null;
  356.         $nextAvailability = new CourseAvailability();
  357.         $frequency $course->getFrequency();
  358.         if ($frequency == 'punctual') {
  359.             foreach ($course->getAvailabilities() as $availability) {
  360.                 $begin date_create_from_format('d/m/Y'$availability->getBegin());
  361.                 if ($begin $fromDate && ($date == null || $date $begin)) {
  362.                     $date $begin;
  363.                     $nextAvailability->setDuration($availability->getDuration())
  364.                         ->setPrice($availability->getPrice())
  365.                         ->setBegin($begin->format('d.m.y'))
  366.                         ->setEnd(date_create_from_format('d/m/Y'$availability->getEnd())->format('d.m.y'));
  367.                 }
  368.             }
  369.         } else {
  370.             foreach ($course->getAvailabilities() as $availability) {
  371.                 $begin date_create_from_format('d/m/Y'$availability->getBegin());
  372.                 $endDate null;
  373.                 if ($availability->getEnd() != null) {
  374.                     $endDate date_create_from_format('d/m/Y'$availability->getEnd());
  375.                 }
  376.                 if ($endDate == null || $endDate $fromDate) {
  377.                     $startDate $availability->getStartDate();
  378.                     $recurrence $availability->getRecurrence();
  379.                     $currentDate $begin;
  380.                     switch ($startDate) {
  381.                         case 'mon':
  382.                             $nextDay 'monday';
  383.                             break;
  384.                         case 'tue':
  385.                             $nextDay 'tuesday';
  386.                             break;
  387.                         case 'wed':
  388.                             $nextDay 'wednesday';
  389.                             break;
  390.                         case 'thu':
  391.                             $nextDay 'thursday';
  392.                             break;
  393.                         case 'fri':
  394.                             $nextDay 'friday';
  395.                             break;
  396.                         case 'sat':
  397.                             $nextDay 'saturday';
  398.                             break;
  399.                         case 'sun':
  400.                             $nextDay 'sunday';
  401.                             break;
  402.                     }
  403.                     if($nextDay) {
  404.                         $currentDate->modify("next {$nextDay}");
  405.                     }
  406.                     switch ($recurrence) {
  407.                         case 'weekly':
  408.                             $nextRecurrence "next {$nextDay}";
  409.                             break;
  410.                         case 'monthly':
  411.                             $dayPosition $availability->getDayPosition();
  412.                             if ($dayPosition == 1) {
  413.                                 $nextRecurrence "first {$nextDay} of next month";
  414.                             } else if ($dayPosition == 2) {
  415.                                 $nextRecurrence "second {$nextDay} of next month";
  416.                             } else if ($dayPosition == 3) {
  417.                                 $nextRecurrence "third {$nextDay} of next month";
  418.                             } else {
  419.                                 $nextRecurrence "fourth {$nextDay} of next month";
  420.                             }
  421.                             break;
  422.                     }
  423.                     while ($currentDate $fromDate) {
  424.                         $currentDate->modify($nextRecurrence);
  425.                     }
  426.                     if ($date == null || $date > ($currentDate && ($endDate == null || $endDate $date))) {
  427.                         $end = clone ($currentDate);
  428.                         $end->modify("+{$availability->getDuration()} days");
  429.                         $date $currentDate;
  430.                         $nextAvailability->setDuration($availability->getDuration())
  431.                             ->setPrice($availability->getPrice())
  432.                             ->setBegin($begin->format('d.m.y'))
  433.                             ->setEnd($end->format('d.m.y'));
  434.                     }
  435.                 }
  436.             }
  437.         }
  438.         return $nextAvailability;
  439.     }
  440.     private function sortCourseByNextDate($courses)
  441.     {
  442.         $boostCourse array_filter($courses, function($course){
  443.             if(count($course->getFeatureds()) == 0){
  444.                 return false;
  445.             }
  446.             /**
  447.              * @var FeaturedCourse $featured
  448.              */
  449.             foreach($course->getFeatureds() as $featured)
  450.             {
  451.                 if($featured->getStatus() == 'active' && $featured->getFeaturedOffer()->getType() == "research"){
  452.                     return true;
  453.                 }
  454.             }
  455.             return false;
  456.         });
  457.         $noBoostCourse array_filter($courses, function($course){
  458.             if(count($course->getFeatureds()) == 0){
  459.                 return true;
  460.             }
  461.             /**
  462.              * @var FeaturedCourse $featured
  463.              */
  464.             foreach($course->getFeatureds() as $featured)
  465.             {
  466.                 if($featured->getStatus() == 'active' && $featured->getFeaturedOffer()->getType() == "research"){
  467.                     return false;
  468.                 }
  469.             }
  470.             return true;
  471.         });
  472.         //Boost
  473.         $availabilitiesSort = [];
  474.         foreach ($boostCourse as $course) {
  475.             $nextAvailability $this->getNextDate($course)->setCourse($course);
  476.             $availabilitiesSort[] = $nextAvailability;
  477.         }
  478.         usort($availabilitiesSort, function($a$b) {
  479.             $ad DateTime::createFromFormat('d.m.y'$a->getBegin());
  480.             $bd DateTime::createFromFormat('d.m.y'$b->getBegin());
  481.             if ($ad == $bd) {
  482.                 return 0;
  483.             }
  484.             return $ad $bd ? -1;
  485.         });
  486.         $lastedCoursesBoost = [];
  487.         foreach ($availabilitiesSort as $availability)
  488.         {
  489.             $lastedCoursesBoost[] = $availability->getCourse();
  490.         }
  491.         //NoBoost
  492.         $availabilitiesSort = [];
  493.         foreach ($noBoostCourse as $course) {
  494.             $nextAvailability $this->getNextDate($course)->setCourse($course);
  495.             $availabilitiesSort[] = $nextAvailability;
  496.         }
  497.         usort($availabilitiesSort, function($a$b) {
  498.             $ad DateTime::createFromFormat('d.m.y'$a->getBegin());
  499.             $bd DateTime::createFromFormat('d.m.y'$b->getBegin());
  500.             if ($ad == $bd) {
  501.                 return 0;
  502.             }
  503.             return $ad $bd ? -1;
  504.         });
  505.         $lastedCoursesNoBoost = [];
  506.         foreach ($availabilitiesSort as $availability)
  507.         {
  508.             $lastedCoursesNoBoost[] = $availability->getCourse();
  509.         }
  510.         $coursesSort array_merge($lastedCoursesBoost$lastedCoursesNoBoost);
  511.         return $coursesSort;
  512.     }
  513.     private function createAlert(SearchCourse $search)
  514.     {
  515.         $trainee $this->getUser();   
  516.         
  517.         if (!$trainee instanceof Trainee) {
  518.             return;
  519.         }
  520.         
  521.         $alert = new Alert();
  522.         $countryCode null;
  523.         $placeName null;
  524.         if ($search->getPlace() != null) {
  525.             $response file_get_contents("https://maps.googleapis.com/maps/api/place/details/json?place_id={$search->getPlace()}&key={$this->googleApiKeyBack}");
  526.             $resultJson json_decode($response);
  527.             $placeName $resultJson->result->formatted_address;
  528.             $addressComponents count($resultJson->result->address_components);
  529.             $countryCode $resultJson->result->address_components[$addressComponents 1]->short_name;
  530.         }
  531.         $numberAlert $this->alertRepository->getNumberAlert($trainee);
  532.         $alert->setTrainee($trainee)
  533.             ->setTitle("#" $numberAlert 1)
  534.             ->setPlace($search->getPlace())
  535.             ->setPlaceName($placeName)
  536.             ->setCountryCode($countryCode)
  537.             ->setBegin($search->getBegin())
  538.             ->setEmailActivated(true)
  539.             ->setCreatedAt(new DateTime());
  540.         foreach ($search->getAges() as $age) {
  541.             $alert->addAge($age);
  542.         }
  543.         foreach ($search->getSports() as $sport) {
  544.             $alert->addSport($sport);
  545.         }
  546.         foreach ($search->getLevels() as $level) {
  547.             $alert->addLevel($level);
  548.         }
  549.         foreach ($search->getLanguages() as $language) {
  550.             $alert->addLanguage($language);
  551.         }
  552.         $this->entityManager->persist($alert);
  553.         $this->entityManager->flush();
  554.         $this->addFlash(
  555.             'success',
  556.             $this->translator->trans("flashes.course_controller.alert_created")
  557.         );
  558.     }
  559. }