src/Repository/Cvs/JobsRepository.php line 320

Open in your IDE?
  1. <?php
  2. namespace App\Repository\Cvs;
  3. use App\Entity\Cvs\Jobs;
  4. use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
  5. use Doctrine\Persistence\ManagerRegistry;
  6. /**
  7.  * @extends ServiceEntityRepository<Jobs>
  8.  *
  9.  * @method Jobs|null find($id, $lockMode = null, $lockVersion = null)
  10.  * @method Jobs|null findOneBy(array $criteria, array $orderBy = null)
  11.  * @method Jobs[]    findAll()
  12.  * @method Jobs[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
  13.  */
  14. class JobsRepository extends ServiceEntityRepository
  15. {
  16.     public function __construct(ManagerRegistry $registry)
  17.     {
  18.         parent::__construct($registryJobs::class);
  19.     }
  20.     /**
  21.      * Recherche des offres par mots-clés, filtrées par locale, avec pagination.
  22.      * Si $cityKeyword est fourni, contraint les résultats à matcher la ville (en plus du query).
  23.      *
  24.      * @param string $query Mots-clés de recherche
  25.      * @param string $locale Langue (fr, en)
  26.      * @param int $offset Décalage pour la pagination
  27.      * @param int $limit Nombre de résultats
  28.      * @param string|null $cityKeyword Si fourni, le résultat doit aussi matcher j.city LIKE %cityKeyword%
  29.      * @return Jobs[]
  30.      */
  31.     public function searchJobs(string $query ''string $locale 'fr'int $offset 0int $limit 10, ?string $cityKeyword null): array
  32.     {
  33.         $qb $this->createQueryBuilder('j')
  34.             ->where('j.online = :online')
  35.             ->andWhere('j.locale = :locale')
  36.             ->setParameter('online'true)
  37.             ->setParameter('locale'$locale)
  38.             ->orderBy('j.createdAt''DESC')
  39.             ->setFirstResult($offset)
  40.             ->setMaxResults($limit);
  41.         if (!empty(trim($query))) {
  42.             $terms array_filter(explode(' 'trim($query)), function($t) { return strlen($t) >= 2; });
  43.             foreach ($terms as $i => $term) {
  44.                 $param 'term' $i;
  45.                 $qb->andWhere(
  46.                     '(j.jobTitle LIKE :' $param .
  47.                     ' OR j.companyName LIKE :' $param .
  48.                     ' OR j.category LIKE :' $param .
  49.                     ' OR j.city LIKE :' $param .
  50.                     ' OR j.requiredSkills LIKE :' $param ')'
  51.                 )
  52.                     ->setParameter($param'%' $term '%');
  53.             }
  54.         }
  55.         // Contrainte ville : doit matcher la ville indépendamment du query
  56.         if ($cityKeyword !== null && trim($cityKeyword) !== '') {
  57.             $qb->andWhere('j.city LIKE :cityKw')
  58.                 ->setParameter('cityKw''%' trim($cityKeyword) . '%');
  59.         }
  60.         return $qb->getQuery()->getResult();
  61.     }
  62.     /**
  63.      * Compte le total d'offres correspondant à la recherche (pour pagination).
  64.      * Si $cityKeyword est fourni, contraint le compte à la ville.
  65.      *
  66.      * @param string $query Mots-clés de recherche
  67.      * @param string $locale Langue (fr, en)
  68.      * @param string|null $cityKeyword
  69.      * @return int
  70.      */
  71.     public function countSearchJobs(string $query ''string $locale 'fr', ?string $cityKeyword null): int
  72.     {
  73.         $qb $this->createQueryBuilder('j')
  74.             ->select('COUNT(j.id)')
  75.             ->where('j.online = :online')
  76.             ->andWhere('j.locale = :locale')
  77.             ->setParameter('online'true)
  78.             ->setParameter('locale'$locale);
  79.         if (!empty(trim($query))) {
  80.             $terms array_filter(explode(' 'trim($query)), function($t) { return strlen($t) >= 2; });
  81.             foreach ($terms as $i => $term) {
  82.                 $param 'term' $i;
  83.                 $qb->andWhere(
  84.                     '(j.jobTitle LIKE :' $param .
  85.                     ' OR j.companyName LIKE :' $param .
  86.                     ' OR j.category LIKE :' $param .
  87.                     ' OR j.city LIKE :' $param .
  88.                     ' OR j.requiredSkills LIKE :' $param ')'
  89.                 )
  90.                     ->setParameter($param'%' $term '%');
  91.             }
  92.         }
  93.         if ($cityKeyword !== null && trim($cityKeyword) !== '') {
  94.             $qb->andWhere('j.city LIKE :cityKw')
  95.                 ->setParameter('cityKw''%' trim($cityKeyword) . '%');
  96.         }
  97.         return (int) $qb->getQuery()->getSingleScalarResult();
  98.     }
  99.     /**
  100.      * Recherche des offres par keyword exact dans le champ keywords uniquement
  101.      *
  102.      * @param string $keyword Le mot-clé exact à chercher
  103.      * @param string $locale Langue (fr, en)
  104.      * @param int $limit Nombre max de résultats
  105.      * @return Jobs[]
  106.      */
  107.     public function findByKeyword(string $keywordstring $locale 'fr'int $limit 4): array
  108.     {
  109.         $keyword trim($keyword);
  110.         if (empty($keyword)) {
  111.             return [];
  112.         }
  113.         return $this->createQueryBuilder('j')
  114.             ->where('j.online = :online')
  115.             ->andWhere('j.locale = :locale')
  116.             ->andWhere('j.keywords LIKE :kw')
  117.             ->setParameter('online'true)
  118.             ->setParameter('locale'$locale)
  119.             ->setParameter('kw''%' $keyword '%')
  120.             ->orderBy('j.createdAt''DESC')
  121.             ->setMaxResults($limit)
  122.             ->getQuery()
  123.             ->getResult();
  124.     }
  125.     /**
  126.      * Recommande des offres similaires aux offres likées par le candidat
  127.      * (même catégorie ou mêmes compétences requises)
  128.      *
  129.      * @param array $likedJobIds IDs des jobs déjà likés
  130.      * @param int $limit Nombre max de résultats
  131.      * @return Jobs[]
  132.      */
  133.     public function findRecommendedByLikedJobs(array $likedJobIdsint $limit 10): array
  134.     {
  135.         if (empty($likedJobIds)) {
  136.             return [];
  137.         }
  138.         // Récupérer les catégories et compétences des jobs likés
  139.         $likedJobs $this->createQueryBuilder('lj')
  140.             ->select('lj.category, lj.requiredSkills')
  141.             ->where('lj.id IN (:ids)')
  142.             ->setParameter('ids'$likedJobIds)
  143.             ->getQuery()
  144.             ->getResult();
  145.         $categories = [];
  146.         $skillTerms = [];
  147.         foreach ($likedJobs as $lj) {
  148.             if (!empty($lj['category'])) {
  149.                 $categories[] = $lj['category'];
  150.             }
  151.             if (!empty($lj['requiredSkills'])) {
  152.                 // Extraire quelques mots-clés des compétences
  153.                 $words array_filter(explode(','$lj['requiredSkills']), function($w) {
  154.                     return strlen(trim($w)) >= 3;
  155.                 });
  156.                 foreach (array_slice($words03) as $word) {
  157.                     $skillTerms[] = trim($word);
  158.                 }
  159.             }
  160.         }
  161.         $categories array_unique($categories);
  162.         $skillTerms array_unique(array_slice($skillTerms05));
  163.         $qb $this->createQueryBuilder('j')
  164.             ->where('j.online = :online')
  165.             ->andWhere('j.id NOT IN (:excludeIds)')
  166.             ->setParameter('online'true)
  167.             ->setParameter('excludeIds'$likedJobIds);
  168.         // Construire les conditions OR pour catégories et skills
  169.         $orConditions = [];
  170.         $i 0;
  171.         foreach ($categories as $cat) {
  172.             $param 'cat' $i;
  173.             $orConditions[] = 'j.category = :' $param;
  174.             $qb->setParameter($param$cat);
  175.             $i++;
  176.         }
  177.         foreach ($skillTerms as $skill) {
  178.             $param 'skill' $i;
  179.             $orConditions[] = 'j.requiredSkills LIKE :' $param;
  180.             $qb->setParameter($param'%' $skill '%');
  181.             $i++;
  182.         }
  183.         if (empty($orConditions)) {
  184.             return [];
  185.         }
  186.         $qb->andWhere('(' implode(' OR '$orConditions) . ')')
  187.             ->orderBy('j.createdAt''DESC')
  188.             ->setMaxResults($limit);
  189.         return $qb->getQuery()->getResult();
  190.     }
  191.     /**
  192.      * Recommande des offres provenant des entreprises likées par le candidat
  193.      *
  194.      * @param array $likedEnterpriseIds IDs des entreprises likées
  195.      * @param array $excludeJobIds IDs des jobs à exclure (déjà likés)
  196.      * @param int $limit Nombre max de résultats
  197.      * @return Jobs[]
  198.      */
  199.     public function findRecommendedByLikedEnterprises(array $likedEnterpriseIds, array $excludeJobIds = [], int $limit 10): array
  200.     {
  201.         if (empty($likedEnterpriseIds)) {
  202.             return [];
  203.         }
  204.         $qb $this->createQueryBuilder('j')
  205.             ->where('j.online = :online')
  206.             ->andWhere('j.enterprise IN (:enterpriseIds)')
  207.             ->setParameter('online'true)
  208.             ->setParameter('enterpriseIds'$likedEnterpriseIds);
  209.         if (!empty($excludeJobIds)) {
  210.             $qb->andWhere('j.id NOT IN (:excludeIds)')
  211.                 ->setParameter('excludeIds'$excludeJobIds);
  212.         }
  213.         $qb->orderBy('j.createdAt''DESC')
  214.             ->setMaxResults($limit);
  215.         return $qb->getQuery()->getResult();
  216.     }
  217.     /**
  218.      * Trouve des offres similaires à une offre donnée
  219.      * (même catégorie, mêmes compétences, même ville, ou même entreprise)
  220.      *
  221.      * @param Jobs $job L'offre de référence
  222.      * @param int $limit Nombre max de résultats
  223.      * @return Jobs[]
  224.      */
  225.     public function findSimilarJobs(Jobs $jobint $limit 5): array
  226.     {
  227.         $qb $this->createQueryBuilder('j')
  228.             ->where('j.online = :online')
  229.             ->andWhere('j.locale = :locale')
  230.             ->andWhere('j.id != :currentId')
  231.             ->setParameter('online'true)
  232.             ->setParameter('locale'$job->getLocale())
  233.             ->setParameter('currentId'$job->getId());
  234.         $orConditions = [];
  235.         $i 0;
  236.         // Même catégorie
  237.         if (!empty($job->getCategory())) {
  238.             $param 'cat' $i;
  239.             $orConditions[] = 'j.category = :' $param;
  240.             $qb->setParameter($param$job->getCategory());
  241.             $i++;
  242.         }
  243.         // Même ville
  244.         if (!empty($job->getCity())) {
  245.             $param 'city' $i;
  246.             $orConditions[] = 'j.city = :' $param;
  247.             $qb->setParameter($param$job->getCity());
  248.             $i++;
  249.         }
  250.         // Compétences similaires (mots-clés extraits de requiredSkills)
  251.         if (!empty($job->getRequiredSkills())) {
  252.             $skills array_filter(explode(','$job->getRequiredSkills()), function($w) {
  253.                 return strlen(trim($w)) >= 3;
  254.             });
  255.             foreach (array_slice($skills03) as $skill) {
  256.                 $param 'skill' $i;
  257.                 $orConditions[] = 'j.requiredSkills LIKE :' $param;
  258.                 $qb->setParameter($param'%' trim($skill) . '%');
  259.                 $i++;
  260.             }
  261.         }
  262.         // Mots-clés du titre similaires
  263.         if (!empty($job->getJobTitle())) {
  264.             $titleWords array_filter(explode(' '$job->getJobTitle()), function($w) {
  265.                 return strlen(trim($w)) >= 4;
  266.             });
  267.             foreach (array_slice($titleWords02) as $word) {
  268.                 $param 'title' $i;
  269.                 $orConditions[] = 'j.jobTitle LIKE :' $param;
  270.                 $qb->setParameter($param'%' trim($word) . '%');
  271.                 $i++;
  272.             }
  273.         }
  274.         if (empty($orConditions)) {
  275.             // Fallback : retourner les offres les plus récentes
  276.             $qb->orderBy('j.createdAt''DESC')
  277.                 ->setMaxResults($limit);
  278.             return $qb->getQuery()->getResult();
  279.         }
  280.         $qb->andWhere('(' implode(' OR '$orConditions) . ')')
  281.             ->orderBy('j.createdAt''DESC')
  282.             ->setMaxResults($limit);
  283.         $results $qb->getQuery()->getResult();
  284.         // Si pas assez de résultats, compléter avec des offres récentes
  285.         if (count($results) < $limit) {
  286.             $excludeIds array_map(function($j) { return $j->getId(); }, $results);
  287.             $excludeIds[] = $job->getId();
  288.             $complement $this->createQueryBuilder('j')
  289.                 ->where('j.online = :online')
  290.                 ->andWhere('j.locale = :locale')
  291.                 ->andWhere('j.id NOT IN (:excludeIds)')
  292.                 ->setParameter('online'true)
  293.                 ->setParameter('locale'$job->getLocale())
  294.                 ->setParameter('excludeIds'$excludeIds)
  295.                 ->orderBy('j.createdAt''DESC')
  296.                 ->setMaxResults($limit count($results))
  297.                 ->getQuery()
  298.                 ->getResult();
  299.             $results array_merge($results$complement);
  300.         }
  301.         return $results;
  302.     }
  303.     /**
  304.      * Compte le nombre total d'offres en ligne sur tout le site (toutes locales confondues)
  305.      *
  306.      * @return int
  307.      */
  308.     public function countAllOnlineJobs(): int
  309.     {
  310.         return (int) $this->createQueryBuilder('j')
  311.             ->select('COUNT(j.id)')
  312.             ->where('j.online = :online')
  313.             ->setParameter('online'true)
  314.             ->getQuery()
  315.             ->getSingleScalarResult();
  316.     }
  317. }