[ 'avocats' => 'Адвокаты', 'consultants' => 'Консультанты', 'vrachi' => 'Врачи', 'psychologues' => 'Психологи', 'repetitori' => 'Преподаватели', 'finance' => 'Бухгалтеры', 'perevod' => 'Переводчики', 'notarius' => 'Нотариусы', 'gid' => 'Гиды', 'rieltori' => 'Риелторы', 'drugie' => 'Специалисты', ], 'fr' => [ 'avocats' => 'Avocats', 'consultants' => 'Consultants', 'vrachi' => 'Médecins', 'psychologues' => 'Psychologues', 'repetitori' => 'Professeurs', 'finance' => 'Comptables', 'perevod' => 'Traducteurs', 'notarius' => 'Notaires', 'gid' => 'Guides', 'rieltori' => 'Agents immobiliers', 'drugie' => 'Spécialistes', ], ]; // Helper detection video function video_thumb_info(?string $url): ?array { if (! $url || ! trim($url)) { return null; } $u = trim($url); // YouTube if (preg_match('~(?:youtube\.com/(?:watch\?v=|embed/|shorts/)|youtu\.be/)([A-Za-z0-9_-]{6,})~i', $u, $m)) { $id = $m[1]; return ['provider' => 'youtube', 'id' => $id, 'thumb' => "https://i.ytimg.com/vi/$id/hqdefault.jpg"]; } // Dailymotion if (preg_match('~dailymotion\.com/video/([A-Za-z0-9]+)|dai\.ly/([A-Za-z0-9]+)~i', $u, $m)) { $id = $m[1] ?: $m[2]; return ['provider' => 'dailymotion', 'id' => $id, 'thumb' => "https://www.dailymotion.com/thumbnail/video/$id"]; } // Direct file (mp4/webm/ogg) — no reliable thumb $path = parse_url($u, PHP_URL_PATH) ?? ''; if (preg_match('~\.(mp4|webm|ogg)$~i', $path)) { return ['provider' => 'file', 'id' => null, 'thumb' => null]; } // Vimeo and others — fallback placeholder if (strpos(strtolower($u), 'vimeo.com') !== false) { return ['provider' => 'vimeo', 'id' => null, 'thumb' => null]; } return ['provider' => 'other', 'id' => null, 'thumb' => null]; } // Lecture et normalisation des filtres $specialiteParam = trim($_GET['specialite'] ?? ''); $ville = trim($_GET['ville'] ?? ''); // inverse map: canon_value -> key (avocats, vrachi, ...) $canonToKey = []; foreach (($SPEC_CANON[$lang] ?? []) as $k => $v) { $canonToKey[mb_strtolower($v, 'UTF-8')] = $k; } $inFrance = ($lang === 'fr') ? ' en France' : ' во Франции'; $h1Default = $translations['res_poisk'] ?? 'Результаты поиска'; $h1 = $h1Default; // On normalise l'affichage du terme (pour l'utiliser plus tard si besoin) $sp = trim(str_replace(["\xC2\xA0"], ' ', $specialiteParam)); $sp = preg_replace('/\s+/u', ' ', $sp); $sp_l = mb_strtolower($sp, 'UTF-8'); if ($specialiteParam === '__OTHERS__') { $label = $H1_LABELS[$lang]['drugie'] ?? $h1Default; $h1 = $label . $inFrance; } elseif ($sp !== '' && isset($canonToKey[$sp_l])) { $key = $canonToKey[$sp_l]; $label = $H1_LABELS[$lang][$key] ?? ($translations[$key] ?? $sp); $h1 = $label . $inFrance; } // sinon: on laisse $h1Default, on décidera après la requête SQL (selon résultats) // Cas spécial : "Другие специалисты" (chip "drugie") $onlyOthers = false; $specialite = $specialiteParam; if ($specialiteParam === '__OTHERS__') { $onlyOthers = true; $specialite = ''; } // Pagination simple $perPage = 12; $page = max(1, (int) ($_GET['page'] ?? 1)); $offset = ($page - 1) * $perPage; // Construction SQL $where = []; $params = []; /*==========================BLOCK WHERE AVEC ABONNEMENT VALIDE ==========================*/ $where[] = "(date_expiration IS NOT NULL AND date_expiration >= DATE_SUB(NOW(), INTERVAL 14 DAY))"; /*=======================================================================================*/ // 🔎 Unifie: le champ "specialite" peut contenir une spécialité OU un nom/prénom $term = trim((string) $specialite); $term_l = mb_strtolower($term, 'UTF-8'); // 1) Cas "Другие": tout sauf les grandes catégories if ($onlyOthers) { $mainSpecs = array_values($canon); if ($mainSpecs) { $ors = []; foreach ($mainSpecs as $i => $spc) { $key = ":cat{$i}"; $ors[] = "LOWER(specialite) LIKE $key"; $params[$key] = mb_strtolower($spc, 'UTF-8') . '%'; } $where[] = '(specialite IS NULL OR specialite = "" OR NOT (' . implode(' OR ', $ors) . '))'; } } // 2) Cas normal : recherche par spécialité / nom / prénom elseif ($term !== '') { $where[] = "(" . "LOWER(specialite) LIKE :sp_like " . "OR LOWER(nom) LIKE :nm_prefix1 " . "OR LOWER(prenom) LIKE :nm_prefix2 " . "OR LOWER(CONCAT(prenom,' ',nom)) LIKE :nm_like_full1 " . "OR LOWER(CONCAT(nom,' ',prenom)) LIKE :nm_like_full2 " . ")"; $params[':sp_like'] = '%' . $term_l . '%'; $params[':nm_prefix1'] = $term_l . '%'; $params[':nm_prefix2'] = $term_l . '%'; $params[':nm_like_full1'] = '%' . $term_l . '%'; $params[':nm_like_full2'] = '%' . $term_l . '%'; } // Filtre ville — support latin + cyrillique (alias + translit) if ($ville !== '') { $villeCands = city_query_candidates($ville); $villeCands[] = mb_strtolower($ville, 'UTF-8'); $villeCands = array_values(array_unique($villeCands)); $villeOrs = []; foreach ($villeCands as $i => $cand) { $key = ":ville{$i}"; $params[$key] = $cand . '%'; $villeOrs[] = "ville LIKE {$key}"; } if ($villeOrs) { $where[] = '(' . implode(' OR ', $villeOrs) . ')'; } } $whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : ''; // total $sqlCount = "SELECT COUNT(*) FROM praticien $whereSql"; $stmt = $pdo->prepare($sqlCount); $stmt->execute($params); $total = (int) $stmt->fetchColumn(); $pages = max(1, (int) ceil($total / $perPage)); // data $sql = "SELECT id, nom, prenom, specialite, ville, photo, presentation, video FROM praticien $whereSql"; if ($term !== '') { $sql .= " ORDER BY (LOWER(CONCAT(prenom,' ',nom)) = :nm_exact1 OR LOWER(CONCAT(nom,' ',prenom)) = :nm_exact2) DESC, (LOWER(nom) = :nm_exact3 OR LOWER(prenom) = :nm_exact4) DESC, (LOWER(nom) LIKE :nm_prefix3 OR LOWER(prenom) LIKE :nm_prefix4) DESC, (LOWER(specialite) LIKE :sp_prefix1) DESC, nom ASC, prenom ASC"; $params[':nm_exact1'] = $term_l; $params[':nm_exact2'] = $term_l; $params[':nm_exact3'] = $term_l; $params[':nm_exact4'] = $term_l; $params[':nm_prefix3'] = $term_l . '%'; $params[':nm_prefix4'] = $term_l . '%'; $params[':sp_prefix1'] = $term_l . '%'; } else { $sql .= " ORDER BY updated_at DESC, id DESC"; } $sql .= " LIMIT :offset, :limit"; $stmt = $pdo->prepare($sql); // Lie tous les :villeN / :specialite foreach ($params as $k => $v) { $stmt->bindValue($k, $v, PDO::PARAM_STR); } $stmt->bindValue(':offset', (int) $offset, PDO::PARAM_INT); $stmt->bindValue(':limit', (int) $perPage, PDO::PARAM_INT); $stmt->execute(); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); // ✅ Si ce n'est pas une catégorie canon / pas __OTHERS__ // et qu'on a des résultats => on affiche "Résultats pour : X" if ($h1 === $h1Default && $specialiteParam !== '__OTHERS__' && $sp !== '' && ! isset($canonToKey[$sp_l])) { if (! empty($rows)) { if ($lang === 'fr') { $h1 = ''; //'Résultats de recherche pour : ' . $sp; } else { $h1 = ''; //'Результаты поиска по: ' . $sp; } } } // util function h($s): string { return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8'); } function excerpt($text, $len = 160): string { $t = trim(strip_tags((string) $text)); if (mb_strlen($t, 'UTF-8') <= $len) { return $t; } return mb_substr($t, 0, $len, 'UTF-8') . '…'; } // pour liens de pagination $qParams = []; if ($onlyOthers) { $qParams['specialite'] = '__OTHERS__'; } elseif ($specialite !== '') { $qParams['specialite'] = $specialite; } if ($ville !== '') { $qParams['ville'] = $ville; } $baseQuery = http_build_query($qParams); ?>