src/Services/Seo/Redir.php line 38

Open in your IDE?
  1. <?php
  2. namespace App\Services\Seo;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Symfony\Component\DependencyInjection\ContainerInterface;
  5. use Twig\Environment;
  6. use App\Entity\Seo\Redirection;
  7. use Symfony\Component\HttpFoundation\RequestStack;
  8. class Redir
  9. {
  10. private $em;
  11. private $container;
  12. private $twig;
  13. private RequestStack $requestStack;
  14. private array $redirectionCache = [];
  15. public function __construct(EntityManagerInterface $em,
  16. ContainerInterface $container,
  17. Environment $twig,
  18. RequestStack $requestStack
  19. ) {
  20. $this->em = $em;
  21. $this->container = $container;
  22. $this->twig = $twig;
  23. $this->requestStack = $requestStack;
  24. }
  25. public function getUrl($slug)
  26. {
  27. // Essayer d'abord avec le slug nettoyé classique
  28. $cleanSlug = $this->removeAppPath($slug);
  29. // Utiliser la nouvelle méthode de recherche avec variantes
  30. $query = $this->em->getRepository(Redirection::class)->findBySlugVariants($cleanSlug, true);
  31. // Si pas trouvé, essayer avec le slug original
  32. if (!$query) {
  33. $query = $this->em->getRepository(Redirection::class)->findBySlugVariants($slug, true);
  34. }
  35. if(!$query) {
  36. return false;
  37. }
  38. if($query->getUrl() == "/") {
  39. return $this->getBaseUrl();
  40. }
  41. return $this->getBaseUrl().$query->getUrl();
  42. }
  43. public function getType($slug)
  44. {
  45. $cleanSlug = $this->removeAppPath($slug);
  46. // Utiliser la même méthode pour la cohérence
  47. $query = $this->em->getRepository(Redirection::class)->findBySlugVariants($cleanSlug, true);
  48. if (!$query) {
  49. $query = $this->em->getRepository(Redirection::class)->findBySlugVariants($slug, true);
  50. }
  51. if(!$query) {
  52. return false;
  53. }
  54. return (int)$query->getType();
  55. }
  56. /**
  57. * Obtenir la base d'URL
  58. * @return string
  59. */
  60. private function getBaseUrl()
  61. {
  62. $appEnv = $this->container->get('router')->getContext()->getBaseUrl()."/";
  63. if($appEnv != "/app_dev.php/") {
  64. $appEnv = "/";
  65. }
  66. $hostName = $_SERVER['HTTP_HOST'];
  67. $protocol = "http";
  68. if($_SERVER["HTTPS"] == "on") {
  69. $protocol = "https";
  70. }
  71. return $protocol.'://'.$hostName.$appEnv;
  72. }
  73. /**
  74. * Obtenir la suppression de l'environnement de dev et de production apparaissant dans l'URL
  75. * @param $slug
  76. * @return array|string|string[]
  77. */
  78. private function removeAppPath($slug)
  79. {
  80. $search = ['/app_dev.php/','/app.php/'];
  81. $replace = ['',''];
  82. $slug = str_replace($search, $replace, $slug);
  83. $firstLetter = substr($slug,0,1);
  84. if($firstLetter == "/") {
  85. $slug = substr($slug,1);
  86. }
  87. // Décoder l'URL pour convertir les caractères encodés
  88. $slug = urldecode($slug);
  89. // Normaliser tous les types de tirets
  90. $unicodeDashes = ['‐', '–', '—', '―'];
  91. $slug = str_replace($unicodeDashes, '-', $slug);
  92. return $slug;
  93. }
  94. /**
  95. * Transforme les liens href dans le contenu HTML en appliquant les redirections
  96. */
  97. public function transformLinksInContent(string $content): string
  98. {
  99. // Regex pour capturer les attributs href avec guillemets doubles OU simples
  100. $pattern = '/href=(["\'])([^"\']*)\1/i';
  101. return preg_replace_callback($pattern, function($matches) {
  102. $quote = $matches[1]; // Guillemet utilisé (" ou ')
  103. $originalUrl = $matches[2]; // L'URL
  104. $transformedUrl = $this->transformUrl($originalUrl);
  105. return 'href=' . $quote . $transformedUrl . $quote;
  106. }, $content);
  107. }
  108. /**
  109. * Transforme une URL en appliquant les redirections si nécessaire
  110. */
  111. public function transformUrl(string $url): string
  112. {
  113. // Si l'URL est vide ou commence par #, on la retourne telle quelle
  114. if (empty($url) || strpos($url, '#') === 0) {
  115. return $url;
  116. }
  117. // Ne traiter que les URLs qui commencent par / ou https://hiringnotes.com/
  118. if (strpos($url, '/') !== 0 && strpos($url, 'https://hiringnotes.com/') !== 0) {
  119. return $url; // URL non traitée (externe, mailto:, tel:, etc.)
  120. }
  121. // Extraction du chemin à vérifier
  122. $pathToCheck = $this->extractPathFromUrl($url);
  123. if (!$pathToCheck) {
  124. return $url; // URL invalide ou non traitée
  125. }
  126. // Vérification s'il existe une redirection pour ce chemin
  127. $redirection = $this->findRedirection($pathToCheck);
  128. if ($redirection) {
  129. // Construction de l'URL redirigée
  130. return $this->buildRedirectedUrl($url, $pathToCheck, $redirection->getUrl());
  131. }
  132. return $url; // Pas de redirection trouvée
  133. }
  134. /**
  135. * Extrait le chemin d'une URL (relative ou absolue)
  136. */
  137. public function extractPathFromUrl(string $url): ?string
  138. {
  139. // URL relative commençant par /
  140. if (strpos($url, '/') === 0) {
  141. // Pour /fr/page -> on veut fr/page
  142. return $this->removeAppPath($url);
  143. }
  144. // URL absolue avec https://hiringnotes.com/
  145. if (strpos($url, 'https://hiringnotes.com/') === 0) {
  146. // Pour https://hiringnotes.com/fr/page -> on veut fr/page
  147. $path = parse_url($url, PHP_URL_PATH);
  148. return $path ? $this->removeAppPath($path) : null;
  149. }
  150. // Toute autre URL n'est pas traitée
  151. return null;
  152. }
  153. /**
  154. * Trouve une redirection pour un chemin donné
  155. */
  156. private function findRedirection(string $path): ?object
  157. {
  158. // Utilisation du cache pour éviter les requêtes répétées
  159. if (isset($this->redirectionCache[$path])) {
  160. return $this->redirectionCache[$path];
  161. }
  162. // Recherche UNIQUEMENT par slug avec visibility=true
  163. $redirection = $this->em->getRepository(Redirection::class)->findOneBy([
  164. 'slug' => $path,
  165. 'visibility' => true
  166. ]);
  167. $this->redirectionCache[$path] = $redirection;
  168. return $redirection;
  169. }
  170. /**
  171. * Construit l'URL redirigée en préservant le format original
  172. */
  173. private function buildRedirectedUrl(string $originalUrl, string $originalPath, string $redirectedPath): string
  174. {
  175. // URL absolue avec https://hiringnotes.com/
  176. if (strpos($originalUrl, 'https://hiringnotes.com/') === 0) {
  177. $parsedUrl = parse_url($originalUrl);
  178. $newUrl = 'https://hiringnotes.com/' . ltrim($redirectedPath, '/');
  179. if (isset($parsedUrl['query'])) {
  180. $newUrl .= '?' . $parsedUrl['query'];
  181. }
  182. if (isset($parsedUrl['fragment'])) {
  183. $newUrl .= '#' . $parsedUrl['fragment'];
  184. }
  185. return $newUrl;
  186. }
  187. // URL relative commençant par /
  188. if (strpos($originalUrl, '/') === 0) {
  189. $parsedUrl = parse_url($originalUrl);
  190. $newUrl = '/' . ltrim($redirectedPath, '/');
  191. if (isset($parsedUrl['query'])) {
  192. $newUrl .= '?' . $parsedUrl['query'];
  193. }
  194. if (isset($parsedUrl['fragment'])) {
  195. $newUrl .= '#' . $parsedUrl['fragment'];
  196. }
  197. return $newUrl;
  198. }
  199. // Fallback (ne devrait pas arriver avec la nouvelle logique)
  200. return $redirectedPath;
  201. }
  202. /**
  203. * Précharge toutes les redirections en cache (optionnel, pour optimiser les performances)
  204. */
  205. public function preloadRedirections(): void
  206. {
  207. $redirections = $this->em->getRepository(Redirection::class)->findBy(['visibility' => true]);
  208. foreach ($redirections as $redirection) {
  209. if ($redirection->getSlug()) {
  210. $this->redirectionCache[$redirection->getSlug()] = $redirection;
  211. }
  212. if ($redirection->getUrl()) {
  213. $this->redirectionCache[$redirection->getUrl()] = $redirection;
  214. }
  215. }
  216. }
  217. }