<?php
namespace App\Controller\Front;
use DateTime;
use App\Entity\Alert;
use App\Entity\Course;
use App\Entity\Trainee;
use App\Entity\SearchCourse;
use App\Entity\FeaturedCourse;
use App\Form\SearchCourseType;
use App\Service\CourseService;
use App\Service\GoogleService;
use App\Entity\CourseViewsStat;
use App\Entity\CourseAvailability;
use App\Entity\SearchAvailability;
use App\Repository\AlertRepository;
use App\Form\SearchAvailabilityType;
use App\Repository\BookingRepository;
use App\Repository\CourseRepository;
use App\Service\InternationalService;
use Doctrine\Persistence\ObjectManager;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\Serializer\Serializer;
use App\Repository\CourseViewsStatRepository;
use App\Repository\WebsiteLanguageRepository;
use Symfony\Component\HttpFoundation\Request;
use App\Repository\TranslatedCourseRepository;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\CourseAvailabilityRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
#[Route(path: '{_locale}/courses')]
class CourseController extends AbstractController
{
private $googleApiKeyBack;
public function __construct(
private AlertRepository $alertRepository,
private EntityManagerInterface $entityManager,
private CourseAvailabilityRepository $courseAvailabilityRepository,
private PaginatorInterface $paginator,
private TranslatorInterface $translator,
private InternationalService $internationalService,
private TranslatedCourseRepository $translatedCourseRepository,
private WebsiteLanguageRepository $websiteLanguageRepository,
private GoogleService $googleService,
private CourseService $courseService,
private ContainerBagInterface $params
){
$this->googleApiKeyBack = $this->params->get('api_key_google_back');
}
function isSearchNull(SearchCourse $search) {
if ($search->getPlace() == null && count($search->getSports()) == 0 && $search->getBegin() == null && count($search->getAges()) == 0 && count($search->getLevels()) == 0 && count($search->getLanguages()) == 0 && $search->getHasLabels() == false) {
return true;
}
return false;
}
/*
#[Route(path: '/translate/courses', name: 't_courses')]
public function t_courses(EntityManagerInterface $manager, CourseRepository $courseRepository)
{
$activeCourses = $courseRepository->findBy(['status' => 'active']);
foreach ($activeCourses as $course) {
$translatedCourses = $course->getTranslatedContents();
$baseTranslatedCourse = $translatedCourses->first();
$websiteLanguages = $this->websiteLanguageRepository->findAll();
foreach ($websiteLanguages as $language) {
$languageSlug = $language->getSlug();
$translatedCourse = $this->translatedCourseRepository->findOneBy(['course' => $course, 'language' => $languageSlug]);
if ($translatedCourse == null) {
$translatedCourse = new TranslatedCourse;
try {
$nameTranslate = $this->googleService->translateWithGoogle($baseTranslatedCourse->getName(), $languageSlug);
$descriptionTranslate = $this->googleService->translateWithGoogle($baseTranslatedCourse->getDescription(), $languageSlug);
if($baseTranslatedCourse->getOthers()) {
$othersTranslate = $this->googleService->translateWithGoogle($baseTranslatedCourse->getOthers(), $languageSlug);
}
foreach($baseTranslatedCourse->getPlannings() as $planning){
$planningTranslate = new Planning;
$titlePlanningTranslate = $this->googleService->translateWithGoogle($planning->getTitle(), $languageSlug);
$descriptionPlanningTranslate = $this->googleService->translateWithGoogle($planning->getDescription(), $languageSlug);
$planningTranslate->setTitle($titlePlanningTranslate)
->setDescription($descriptionPlanningTranslate);
$translatedCourse->addPlanning($planningTranslate);
}
$translatedCourse->setName($nameTranslate)
->setDescription($descriptionTranslate)
->setOthers($othersTranslate ?? "")
->setLanguage($language->getSlug())
->setCourse($course);
$this->entityManager->persist($translatedCourse);
}
catch(Exception $e) {
// Créer une exeption;
}
}
}
}
$this->entityManager->flush();
dd("fait");
}
*/
#[Route(path: '/{view}', name: 'courses', defaults: ['view' => 'list'])]
public function listCourses($view, CourseRepository $courseRepository, PaginatorInterface $paginator, Request $request, EntityManagerInterface $manager, TranslatorInterface $translator, GoogleService $googleService): Response
{
$search = new SearchCourse();
$startingDate = null;
$noSearchResult = false;
$otherResult = false;
$form = $this->createForm(SearchCourseType::class, $search);
$form->handleRequest($request);
if ($request->query->get('search') && $this->isSearchNull($search)) {
return $this->redirectToRoute('courses', [
'view' => $view
]);
}
if ($form->isSubmitted() && $search->getPlace() != "undefined") {
if ($view == 'map' && $search->getPlace() == null) {
return $this->redirectToRoute('courses', [
'view' => 'list'
]);
}
// Create alert for trainee
if ($request->query->get('search') == null) {
try {
$this->createAlert($search);
} catch (\Throwable $th) {
//throw $th;
}
}
$startingDate = $search->getBegin();
$coursesSearch = $courseRepository->searchCourses($search);
if(count($coursesSearch) == 0) {
$countryCode = null;
$noSearchResult = true;
if ($search->getPlace() != null && $googleService->getPlaceCountryCode($search->getPlace())) {
$countryCode = $googleService->getPlaceCountryCode($search->getPlace());
$coursesSearch = $courseRepository->searchCourses($search, $countryCode);
if (count($coursesSearch) > 0) {
$otherResult = true;
}
}
$searchBooster = $courseRepository->findFeaturedCourses(null, 'research', $countryCode);
}
$courses = $this->sortCourseByNextDate($coursesSearch);
}
else {
$courses = $this->sortCourseByNextDate($courseRepository->findLatestCourses());
$search = null;
}
$courses = $paginator->paginate(
$courses,
$request->query->getInt('page', 1),
24
);
$template = 'front/course/search-course.html.twig';
if ($view == 'map') {
$template = 'front/course/search-course-map.html.twig';
}
$response = $this->render($template, [
'menu' => 'courses',
'courses' => $courses,
'noSearchResult' => $noSearchResult,
'otherResult' => $otherResult,
'form' => $form->createView(),
'startingDate' => $startingDate,
'criteria' => $search,
'searchBooster' => $searchBooster ?? 0,
'view' => $view
]);
if ($search != null) {
// Ajouter l'en-tête X-Robots-Tag
$response->headers->set('X-Robots-Tag', 'noindex, nofollow');
}
return $response;
}
#[Route(path: '-details/{token}', name: 'view_course')]
public function courseDetails(Course $course, Request $request, CourseViewsStatRepository $courseViewsStatRepository, EntityManagerInterface $manager, BookingRepository $bookingRepository): Response
{
if ($course->getOwner()->getStatus() != 'online') {
$this->addFlash(
'info',
$this->translator->trans("flashes.course_controller.unknown_course")
);
return $this->redirectToRoute('homepage');
}
if ($course->getStatus() == 'pending' && $course->getOwner() != $this->getUser()) {
return $this->redirectToRoute('homepage');
}
$session = $request->getSession();
$translatedCourse = $course->getTranslatedContent($request->getLocale());
if ($session->get('seenCourses')) {
$seenCourses = $session->get('seenCourses');
} else {
$seenCourses = [];
}
$seenCourses[] = $course->getToken();
$session->set('seenCourses', $seenCourses);
$page = $request->query->getInt('page', 1);
$search = new SearchAvailability();
$startingDate = null;
$form = $this->createForm(SearchAvailabilityType::class, $search);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$startingDate = $search->getBegin();
}
$availabilities = $this->getAvailabilities($course, $page, $startingDate);
$encoder = new JsonEncoder();
$defaultContext = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $json, $context) {
return $object;
},
];
$normalizer = new ObjectNormalizer(null, null, null, null, null, null, $defaultContext);
$serializer = new Serializer([$normalizer], [$encoder]);
$encodedAvailabilities = $serializer->serialize($availabilities->getItems(), 'json');
if ($request->query->getInt('page')) {
$response = new Response();
$response->setContent($encodedAvailabilities);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
//View stat
$ip = $_SERVER['REMOTE_ADDR'];
if ($ip) {
$courseViewsStat = $courseViewsStatRepository->findByIp($ip, $course);
if($courseViewsStat) {
$origin = new \DateTime();
$target = new \DateTime($courseViewsStat->getDate());
$interval = $origin->diff($target)->format('%a');
if($interval > 2){
$courseViewsStat->setViews($courseViewsStat->getViews() + 1)
->setDate(date_format(new \DateTime(), 'Y-m-d'));
}
}
else {
$courseViewsStat = new CourseViewsStat();
$courseViewsStat->setCourse($course)
->setDate(date_format(new \DateTime(), 'Y-m-d'))
->setIp($ip)
->setViews(1);
$manager->persist($courseViewsStat);
}
$manager->flush();
}
$traineeHasPendingBooking = false;
if ($this->getUser() && $this->getUser() instanceof Trainee) {
$trainee = $this->getUser();
$traineePendingBooking = $bookingRepository->traineeCoursePendingBooking($trainee, $course);
if ($traineePendingBooking) {
$traineeHasPendingBooking = true;
}
}
return $this->render('front/course/course-details.html.twig', [
'course' => $course,
'translatedCourse' => $translatedCourse,
'availabilities' => $availabilities,
'encodedAvailabilities' => $encodedAvailabilities,
'traineeHasPendingBooking' => $traineeHasPendingBooking,
'form' => $form->createView(),
'startingDate' => $startingDate
]);
}
public function getAvailabilities(Course $course, $page, $startingDate = null)
{
$nextAvailabilities = $this->courseService->getAvailabilities($course, 'multiple', $startingDate);
usort($nextAvailabilities, array($this, "cmp"));
$sortedAvailabilities = $this->paginator->paginate(
$nextAvailabilities,
$page,
8
);
return $sortedAvailabilities;
}
function cmp($a, $b)
{
$aBegin = date_create_from_format('d.m.y', $a->getBegin());
$bBegin = date_create_from_format('d.m.y', $b->getBegin());
return ($aBegin > $bBegin);
}
#[Route(path: '/add-course-to-favorite/{id}', name: 'add_course_favorite', methods: ['POST'])]
public function addToFavorite(Course $course, ObjectManager $manager, Request $request)
{
$user = $this->getUser();
$data = json_decode($request->getContent(), true);
if($this->isCsrfTokenValid('favorite'.$course->getId(), $data['_token']))
{
if ($user instanceof Trainee) {
if($course->getTraineesFavorites()->contains($user))
{
$user->removeFavorite($course);
}
else
{
$user->addFavorite($course);
}
$manager->flush();
}
return new JsonResponse(['success' => 1]);
}
else
{
return new JsonResponse(['error' => 'token invalid'], 400);
}
}
#[Route(path: '/set-search-destination/in-session', name: 'set_search_destination')]
public function setSearchDestination(Request $request)
{
$data = json_decode($request->getContent(), true);
$destination = $data['searchDestination'];
$session = new Session();
$session->set('destination', $destination);
return new JsonResponse(['ok' => 'ok']);
}
private function getNextDate(Course $course, $startingDate = null)
{
if ($startingDate == null) {
$fromDate = new DateTime();
$fromDate->setTimestamp(strtotime('today midnight'));
} else {
$fromDate = date_create_from_format('d/m/Y', $startingDate);
}
$date = null;
$nextAvailability = new CourseAvailability();
$frequency = $course->getFrequency();
if ($frequency == 'punctual') {
foreach ($course->getAvailabilities() as $availability) {
$begin = date_create_from_format('d/m/Y', $availability->getBegin());
if ($begin > $fromDate && ($date == null || $date > $begin)) {
$date = $begin;
$nextAvailability->setDuration($availability->getDuration())
->setPrice($availability->getPrice())
->setBegin($begin->format('d.m.y'))
->setEnd(date_create_from_format('d/m/Y', $availability->getEnd())->format('d.m.y'));
}
}
} else {
foreach ($course->getAvailabilities() as $availability) {
$begin = date_create_from_format('d/m/Y', $availability->getBegin());
$endDate = null;
if ($availability->getEnd() != null) {
$endDate = date_create_from_format('d/m/Y', $availability->getEnd());
}
if ($endDate == null || $endDate > $fromDate) {
$startDate = $availability->getStartDate();
$recurrence = $availability->getRecurrence();
$currentDate = $begin;
switch ($startDate) {
case 'mon':
$nextDay = 'monday';
break;
case 'tue':
$nextDay = 'tuesday';
break;
case 'wed':
$nextDay = 'wednesday';
break;
case 'thu':
$nextDay = 'thursday';
break;
case 'fri':
$nextDay = 'friday';
break;
case 'sat':
$nextDay = 'saturday';
break;
case 'sun':
$nextDay = 'sunday';
break;
}
if($nextDay) {
$currentDate->modify("next {$nextDay}");
}
switch ($recurrence) {
case 'weekly':
$nextRecurrence = "next {$nextDay}";
break;
case 'monthly':
$dayPosition = $availability->getDayPosition();
if ($dayPosition == 1) {
$nextRecurrence = "first {$nextDay} of next month";
} else if ($dayPosition == 2) {
$nextRecurrence = "second {$nextDay} of next month";
} else if ($dayPosition == 3) {
$nextRecurrence = "third {$nextDay} of next month";
} else {
$nextRecurrence = "fourth {$nextDay} of next month";
}
break;
}
while ($currentDate < $fromDate) {
$currentDate->modify($nextRecurrence);
}
if ($date == null || $date > ($currentDate && ($endDate == null || $endDate > $date))) {
$end = clone ($currentDate);
$end->modify("+{$availability->getDuration()} days");
$date = $currentDate;
$nextAvailability->setDuration($availability->getDuration())
->setPrice($availability->getPrice())
->setBegin($begin->format('d.m.y'))
->setEnd($end->format('d.m.y'));
}
}
}
}
return $nextAvailability;
}
private function sortCourseByNextDate($courses)
{
$boostCourse = array_filter($courses, function($course){
if(count($course->getFeatureds()) == 0){
return false;
}
/**
* @var FeaturedCourse $featured
*/
foreach($course->getFeatureds() as $featured)
{
if($featured->getStatus() == 'active' && $featured->getFeaturedOffer()->getType() == "research"){
return true;
}
}
return false;
});
$noBoostCourse = array_filter($courses, function($course){
if(count($course->getFeatureds()) == 0){
return true;
}
/**
* @var FeaturedCourse $featured
*/
foreach($course->getFeatureds() as $featured)
{
if($featured->getStatus() == 'active' && $featured->getFeaturedOffer()->getType() == "research"){
return false;
}
}
return true;
});
//Boost
$availabilitiesSort = [];
foreach ($boostCourse as $course) {
$nextAvailability = $this->getNextDate($course)->setCourse($course);
$availabilitiesSort[] = $nextAvailability;
}
usort($availabilitiesSort, function($a, $b) {
$ad = DateTime::createFromFormat('d.m.y', $a->getBegin());
$bd = DateTime::createFromFormat('d.m.y', $b->getBegin());
if ($ad == $bd) {
return 0;
}
return $ad < $bd ? -1 : 1;
});
$lastedCoursesBoost = [];
foreach ($availabilitiesSort as $availability)
{
$lastedCoursesBoost[] = $availability->getCourse();
}
//NoBoost
$availabilitiesSort = [];
foreach ($noBoostCourse as $course) {
$nextAvailability = $this->getNextDate($course)->setCourse($course);
$availabilitiesSort[] = $nextAvailability;
}
usort($availabilitiesSort, function($a, $b) {
$ad = DateTime::createFromFormat('d.m.y', $a->getBegin());
$bd = DateTime::createFromFormat('d.m.y', $b->getBegin());
if ($ad == $bd) {
return 0;
}
return $ad < $bd ? -1 : 1;
});
$lastedCoursesNoBoost = [];
foreach ($availabilitiesSort as $availability)
{
$lastedCoursesNoBoost[] = $availability->getCourse();
}
$coursesSort = array_merge($lastedCoursesBoost, $lastedCoursesNoBoost);
return $coursesSort;
}
private function createAlert(SearchCourse $search)
{
$trainee = $this->getUser();
if (!$trainee instanceof Trainee) {
return;
}
$alert = new Alert();
$countryCode = null;
$placeName = null;
if ($search->getPlace() != null) {
$response = file_get_contents("https://maps.googleapis.com/maps/api/place/details/json?place_id={$search->getPlace()}&key={$this->googleApiKeyBack}");
$resultJson = json_decode($response);
$placeName = $resultJson->result->formatted_address;
$addressComponents = count($resultJson->result->address_components);
$countryCode = $resultJson->result->address_components[$addressComponents - 1]->short_name;
}
$numberAlert = $this->alertRepository->getNumberAlert($trainee);
$alert->setTrainee($trainee)
->setTitle("#" . $numberAlert + 1)
->setPlace($search->getPlace())
->setPlaceName($placeName)
->setCountryCode($countryCode)
->setBegin($search->getBegin())
->setEmailActivated(true)
->setCreatedAt(new DateTime());
foreach ($search->getAges() as $age) {
$alert->addAge($age);
}
foreach ($search->getSports() as $sport) {
$alert->addSport($sport);
}
foreach ($search->getLevels() as $level) {
$alert->addLevel($level);
}
foreach ($search->getLanguages() as $language) {
$alert->addLanguage($language);
}
$this->entityManager->persist($alert);
$this->entityManager->flush();
$this->addFlash(
'success',
$this->translator->trans("flashes.course_controller.alert_created")
);
}
}