src/Controller/ThemesWebsite/Whileresume/Application/JobsController.php line 142

Open in your IDE?
  1. <?php
  2. namespace App\Controller\ThemesWebsite\Whileresume\Application;
  3. use App\Entity\Core\Users;
  4. use App\Entity\Cvs\Candidates;
  5. use App\Entity\Cvs\CandidatesHasExperiences;
  6. use App\Entity\Cvs\CandidatesHasProjects;
  7. use App\Entity\Cvs\CandidatesHasServices;
  8. use App\Entity\Cvs\CandidatesHasSkills;
  9. use App\Entity\Cvs\Jobs;
  10. use App\Entity\Cvs\JobsHasLikes;
  11. use App\Form\Core\UsersType;
  12. use App\Form\Cvs\JobsForm;
  13. use App\Form\Cvs\JobsFrForm;
  14. use App\Services\Core\RequestData;
  15. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
  16. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  17. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  18. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  19. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpFoundation\Response;
  22. use Symfony\Component\Routing\Annotation\Route;
  23. use Symfony\Component\HttpFoundation\Cookie;
  24. use Symfony\Component\HttpFoundation\JsonResponse;
  25. use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  26. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  27. use Doctrine\ORM\EntityManagerInterface;
  28. class JobsController extends AbstractController
  29. {
  30.     private $rd;
  31.     private $em;
  32.     public function __construct(RequestData $rd,
  33.                                 EntityManagerInterface $em
  34.     ) {
  35.         $this->rd $rd;
  36.         $this->em $em;
  37.     }
  38.     public function new(Request $request): Response
  39.     {
  40.         $locale $request->getLocale();
  41.         $user $this->getUser();
  42.         $jobClass JobsForm::class;
  43.         if($locale == "fr") {
  44.             $jobClass JobsFrForm::class;
  45.         }
  46.         $job = new Jobs();
  47.         $form $this->createForm($jobClass$job);
  48.         $form->handleRequest($request);
  49.         if ($form->isSubmitted() && $form->isValid()) {
  50.             $job->setSlug("0");
  51.             $job->setLocale($locale);
  52.             $job->setValidation(false);
  53.             $job->setVerification(false);
  54.             $job->setOnline(false);
  55.             $job->setEnterprise(null);
  56.             $job->setUser($user);
  57.             $this->em->persist($job);
  58.             $this->em->flush();
  59.             if($job->getSlug() != "0") {
  60.                 $job->setSlug($this->generateSlug($job->getId()));
  61.                 $this->em->persist($job);
  62.                 $this->em->flush();
  63.             }
  64.             if($locale !== "en") {
  65.                 return $this->redirectToRoute('locale_cvs_application_job_new_confirm',['_locale' => $locale]);
  66.             }
  67.             return $this->redirectToRoute('cvs_application_job_new_confirm');
  68.         }
  69.         return $this->render('application/whileresume/application/jobs/new_'.$locale.'.html.twig',[
  70.             'form' => $form->createView(),
  71.         ]);
  72.     }
  73.     private function generateSlug(int $id): string
  74.     {
  75.         $letters 'abcdefghijklmnopqrstuvwxyz';
  76.         $random '';
  77.         for ($i 0$i 6$i++) {
  78.             $random .= $letters[random_int(025)];
  79.         }
  80.         return $random '-' $id;
  81.     }
  82.     public function show(Request $request$slug): Response
  83.     {
  84.         $locale $request->getLocale();
  85.         $jobsRepo $this->em->getRepository(Jobs::class);
  86.         $job $jobsRepo->findOneBy(['locale' => $locale'slug' => $slug'online' => true'validation' => true]);
  87.         if($job === null) {
  88.             throw $this->createNotFoundException('Page non trouvée');
  89.         }
  90.         $othersJobs null;
  91.         if($job->getEnterprise() != null) {
  92.             $othersJobs $jobsRepo->findBy(['enterprise' => $job->getEnterprise(), 'online' => true]);
  93.         }
  94.         // Offres similaires basées sur catégorie, compétences, ville, titre
  95.         $similarJobs $jobsRepo->findSimilarJobs($job5);
  96.         // Filtres sidebar : tags polymorphes (villes + métiers) configurés en BDD
  97.         $filtersRepo $this->em->getRepository(\App\Entity\Cvs\JobsFilters::class);
  98.         $sidebarFilters $filtersRepo->findActiveByLocale($locale30);
  99.         // Séparer par type pour l'affichage (city vs category)
  100.         $sidebarFiltersCities = [];
  101.         $sidebarFiltersCategories = [];
  102.         foreach ($sidebarFilters as $f) {
  103.             if ($f->getType() === 'city') {
  104.                 $sidebarFiltersCities[] = $f;
  105.             } else {
  106.                 $sidebarFiltersCategories[] = $f;
  107.             }
  108.         }
  109.         // Keywords cliquables sous la barre de recherche (homepage de la locale)
  110.         $keywordsRepo $this->em->getRepository(\App\Entity\Cvs\KeywordsLandingJobs::class);
  111.         $searchKeywords $keywordsRepo->findByLocaleForHomepage($locale);
  112.         $count 0;
  113.         if($job->getViews() != null) {
  114.             $count $job->getViews() + 1;
  115.         }
  116.         $job->setViews($count);
  117.         $this->em->persist($job);
  118.         $this->em->flush();
  119.         return $this->render('application/whileresume/application/jobs/show.html.twig',[
  120.             'slug' => $slug,
  121.             'job' =>  $job,
  122.             'othersJobs' => $othersJobs,
  123.             'similarJobs' => $similarJobs,
  124.             'sidebarFiltersCities' => $sidebarFiltersCities,
  125.             'sidebarFiltersCategories' => $sidebarFiltersCategories,
  126.             'searchKeywords' => $searchKeywords,
  127.         ]);
  128.     }
  129.     /**
  130.      * Retourne les détails d'une offre en JSON pour la fiche modale (AJAX).
  131.      * Si l'utilisateur n'est pas connecté, retourne uniquement les champs publics
  132.      * (résumé + meta) avec un flag "locked" sur les détails.
  133.      */
  134.     public function ajaxShow(Request $request$slug): JsonResponse
  135.     {
  136.         $locale $request->getLocale();
  137.         $user $this->getUser();
  138.         $job $this->em->getRepository(Jobs::class)->findOneBy([
  139.             'locale' => $locale,
  140.             'slug' => $slug,
  141.             'online' => true,
  142.             'validation' => true
  143.         ]);
  144.         if ($job === null) {
  145.             return new JsonResponse(['error' => 'not_found'], 404);
  146.         }
  147.         $isAuthenticated $user !== null;
  148.         $isCandidate $isAuthenticated && $user->getCandidate() !== null;
  149.         $enterpriseData null;
  150.         if ($job->getEnterprise() !== null) {
  151.             $enterpriseData = [
  152.                 'id' => $job->getEnterprise()->getId(),
  153.                 'slug' => $job->getEnterprise()->getSlug(),
  154.                 'name' => $job->getEnterprise()->getCompanyName(),
  155.                 'logo' => method_exists($job->getEnterprise(), 'getLogo') ? $job->getEnterprise()->getLogo() : null,
  156.                 'city' => method_exists($job->getEnterprise(), 'getCity') ? $job->getEnterprise()->getCity() : null,
  157.                 'country' => method_exists($job->getEnterprise(), 'getCountry') ? $job->getEnterprise()->getCountry() : null,
  158.             ];
  159.         }
  160.         // Données publiques (toujours visibles)
  161.         $payload = [
  162.             'id' => $job->getId(),
  163.             'slug' => $job->getSlug(),
  164.             'title' => $job->getJobTitle(),
  165.             'companyName' => $job->getCompanyName(),
  166.             'category' => $job->getCategory(),
  167.             'city' => $job->getCity(),
  168.             'country' => $job->getCountry(),
  169.             'employmentType' => $job->getEmploymentType(),
  170.             'remoteWork' => $job->getRemoteWork(),
  171.             'experienceLevel' => $job->getExperienceLevel(),
  172.             'salaryMin' => $job->getSalaryMin(),
  173.             'salaryMax' => $job->getSalaryMax(),
  174.             'salaryPeriod' => $job->getSalaryPeriod(),
  175.             'devise' => $job->getDevise(),
  176.             'jobSummary' => $job->getJobSummary(),
  177.             'verification' => $job->getVerification(),
  178.             'website' => method_exists($job'getWebsite') ? $job->getWebsite() : null,
  179.             'websearch' => method_exists($job'getWebsearch') ? $job->getWebsearch() : null,
  180.             'enterprise' => $enterpriseData,
  181.             'isAuthenticated' => $isAuthenticated,
  182.             'isCandidate' => $isCandidate,
  183.             'locked' => !$isAuthenticated,
  184.             'showUrl' => $this->generateUrl('locale_cvs_application_job_show', [
  185.                 '_locale' => $locale,
  186.                 'slug' => $slug
  187.             ]),
  188.             'likeUrl' => null,
  189.         ];
  190.         // Données détaillées (uniquement si connecté)
  191.         if ($isAuthenticated) {
  192.             $payload['keyResponsabilities'] = $job->getKeyResponsabilities();
  193.             $payload['requirements'] = $job->getRequirements();
  194.             $payload['benefits'] = $job->getBenefits();
  195.             if ($isCandidate) {
  196.                 $payload['likeUrl'] = $this->generateUrl('locale_cvs_application_job_like', [
  197.                     '_locale' => $locale,
  198.                     'slug' => $slug
  199.                 ]);
  200.             }
  201.         }
  202.         return new JsonResponse($payload);
  203.     }
  204.     /**
  205.      * Page landing pour un filtre (ville/métier/etc.) configuré dans cvs_jobsfilters.
  206.      * URL : /{locale}/jobs/{slug} → ex: /fr/jobs/paris ou /fr/jobs/developpement-web
  207.      *
  208.      * Affiche la 1ère offre matchant le filtre via la page show classique, ou un 404
  209.      * si le slug n'existe pas dans cvs_jobsfilters.
  210.      */
  211.     public function filterLanding(Request $request$slug): Response
  212.     {
  213.         $locale $request->getLocale();
  214.         $filtersRepo $this->em->getRepository(\App\Entity\Cvs\JobsFilters::class);
  215.         $filter $filtersRepo->findOneBySlugAndLocale($slug$locale);
  216.         if ($filter === null) {
  217.             throw $this->createNotFoundException('Filtre introuvable');
  218.         }
  219.         // Recherche : on prend le 1er job qui matche le searchKeyword
  220.         $jobsRepo $this->em->getRepository(Jobs::class);
  221.         $keyword $filter->getSearchKeyword() ?: $filter->getLabel();
  222.         // searchJobs cherche dans jobTitle / companyName / category / city / requiredSkills
  223.         $matchedJobs $jobsRepo->searchJobs($keyword$locale06);
  224.         if (empty($matchedJobs)) {
  225.             // Aucune offre : on rend la page filterLanding "vide"
  226.             return $this->render('application/whileresume/application/jobs/filter_landing.html.twig', [
  227.                 'filter' => $filter,
  228.                 'job' => null,
  229.                 'similarJobs' => [],
  230.             ]);
  231.         }
  232.         // On prend la 1ère offre comme offre "principale" et les suivantes comme similaires
  233.         $job array_shift($matchedJobs);
  234.         $similarJobs $matchedJobs;
  235.         // Filtres sidebar
  236.         $sidebarFilters $filtersRepo->findActiveByLocale($locale30);
  237.         $sidebarFiltersCities = [];
  238.         $sidebarFiltersCategories = [];
  239.         foreach ($sidebarFilters as $f) {
  240.             if ($f->getType() === 'city') {
  241.                 $sidebarFiltersCities[] = $f;
  242.             } else {
  243.                 $sidebarFiltersCategories[] = $f;
  244.             }
  245.         }
  246.         $keywordsRepo $this->em->getRepository(\App\Entity\Cvs\KeywordsLandingJobs::class);
  247.         $searchKeywords $keywordsRepo->findByLocaleForHomepage($locale);
  248.         return $this->render('application/whileresume/application/jobs/show.html.twig', [
  249.             'slug' => $job->getSlug(),
  250.             'job' => $job,
  251.             'othersJobs' => null,
  252.             'similarJobs' => $similarJobs,
  253.             'sidebarFiltersCities' => $sidebarFiltersCities,
  254.             'sidebarFiltersCategories' => $sidebarFiltersCategories,
  255.             'searchKeywords' => $searchKeywords,
  256.             'currentFilter' => $filter// pour le SEO / titre / breadcrumb
  257.         ]);
  258.     }
  259.     /**
  260.      * API publique de recherche d'offres pour l'autocomplétion.
  261.      * GET /api/jobs/search?q=...&locale=...&limit=...
  262.      */
  263.     public function apiSearch(Request $request): JsonResponse
  264.     {
  265.         $query trim((string) $request->query->get('q'''));
  266.         $locale $request->query->get('locale'$request->getLocale());
  267.         $limit max(1min(20, (int) $request->query->get('limit'6)));
  268.         $offset max(0, (int) $request->query->get('offset'0));
  269.         if (mb_strlen($query) < 2) {
  270.             return new JsonResponse([
  271.                 'items' => [],
  272.                 'total' => 0,
  273.                 'query' => $query,
  274.             ]);
  275.         }
  276.         $repo $this->em->getRepository(Jobs::class);
  277.         $jobs $repo->searchJobs($query$locale$offset$limit);
  278.         $total $repo->countSearchJobs($query$locale);
  279.         $items = [];
  280.         foreach ($jobs as $job) {
  281.             $logo null;
  282.             if ($job->getEnterprise() !== null && method_exists($job->getEnterprise(), 'getLogo')) {
  283.                 $logo $job->getEnterprise()->getLogo();
  284.             }
  285.             $items[] = [
  286.                 'id' => $job->getId(),
  287.                 'slug' => $job->getSlug(),
  288.                 'title' => $job->getJobTitle(),
  289.                 'companyName' => $job->getCompanyName(),
  290.                 'category' => $job->getCategory(),
  291.                 'city' => $job->getCity(),
  292.                 'country' => $job->getCountry(),
  293.                 'employmentType' => $job->getEmploymentType(),
  294.                 'salaryMin' => $job->getSalaryMin(),
  295.                 'salaryMax' => $job->getSalaryMax(),
  296.                 'devise' => $job->getDevise(),
  297.                 'logo' => $logo,
  298.                 'url' => $this->generateUrl('locale_cvs_application_job_show', [
  299.                     '_locale' => $locale,
  300.                     'slug' => $job->getSlug(),
  301.                 ]),
  302.             ];
  303.         }
  304.         return new JsonResponse([
  305.             'items' => $items,
  306.             'total' => $total,
  307.             'query' => $query,
  308.             'limit' => $limit,
  309.             'offset' => $offset,
  310.         ]);
  311.     }
  312.     public function likeJob(Request $request$slug): Response
  313.     {
  314.         $locale $request->getLocale();
  315.         $user $this->getUser();
  316.         if($user == null) {
  317.             if($locale != "en") {
  318.                 return $this->redirectToRoute('locale_cvs_gestion_candidates_dashboard',['_locale' => $locale]);
  319.             }
  320.             return $this->redirectToRoute('cvs_gestion_candidates_dashboard');
  321.         }
  322.         $candidate  $user->getCandidate();
  323.         $job $this->em->getRepository(Jobs::class)->findOneBy(['slug' => $slug]);
  324.         $jhl $this->em->getRepository(JobsHasLikes::class)->findOneBy(['candidate' => $candidate'job' => $job]);
  325.         if($jhl == null) {
  326.             $jhl = new JobsHasLikes();
  327.             $jhl->setJob($job);
  328.             $jhl->setCandidate($candidate);
  329.             $jhl->setCreatedAt(new \DateTime("now"));
  330.             $jhl->setUpdatedAt(new \DateTime("now"));
  331.             $this->em->persist($jhl);
  332.             $this->em->flush();
  333.         }
  334.         if($locale != "en") {
  335.             return $this->redirectToRoute('locale_cvs_gestion_candidates_dashboard',['_locale' => $locale]);
  336.         }
  337.         return $this->redirectToRoute('cvs_gestion_candidates_dashboard');
  338.     }
  339. }