'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"]; } // Fichiers directs $path = parse_url($u, PHP_URL_PATH) ?? ''; if (preg_match('~\\.(mp4|webm|ogg)$~i', $path)) { return ['provider' => 'file', 'id' => null, 'thumb' => null]; } if (strpos(strtolower($u), 'vimeo.com') !== false) { return ['provider' => 'vimeo', 'id' => null, 'thumb' => null]; } return ['provider' => 'other', 'id' => null, 'thumb' => null]; } function fullName(array $p): string { return trim(($p['prenom_affiche'] ?? $p['prenom'] ?? '') . ' ' . ($p['nom_affiche'] ?? $p['nom'] ?? '')); } function excerpt_words(string $html, int $words = 60): string { $plain = trim(preg_replace('/\\s+/', ' ', strip_tags($html))); if ($plain === '') { return ''; } $parts = preg_split('/\\s+/', $plain); if (count($parts) <= $words) { return $plain; } return implode(' ', array_slice($parts, 0, $words)) . '…'; } /* ========================= Requêtes (COUNT + LIST) ========================= */ $whereSql = []; $params = []; /*==========================BLOCK WHERE AVEC ABONNEMENT VALIDE ==========================*/ $whereSql[] = "(p.date_expiration IS NOT NULL AND p.date_expiration >= DATE_SUB(NOW(), INTERVAL 14 DAY))"; /*=======================================================================================*/ if ($q !== '') { $words = preg_split('/\s+/', $q, -1, PREG_SPLIT_NO_EMPTY); $and = []; foreach ($words as $i => $word) { $k1 = ":q_name_$i"; $k2 = ":q_spec_$i"; $and[] = "( LOWER(CONCAT(COALESCE(pt.prenom_affiche, p.prenom, ''),' ',COALESCE(pt.nom_affiche, p.nom, ''))) LIKE $k1 OR LOWER(COALESCE(pt.specialite, p.specialite, '')) LIKE $k2 )"; $params[$k1] = '%' . $word . '%'; $params[$k2] = '%' . $word . '%'; } $whereSql[] = '(' . implode(' AND ', $and) . ')'; } if ($w !== '') { // Candidats latins à partir d'une saisie RU (alias + translittération) $villeCands = city_query_candidates($w); // Ajoute aussi la saisie brute (utile si déjà en latin) $villeCands[] = mb_strtolower($w, 'UTF-8'); $villeCands = array_values(array_unique($villeCands)); $ors = []; // Ville (LOWER(...)) : on cherche par préfixe foreach ($villeCands as $i => $cand) { $key = ":wv$i"; $ors[] = "LOWER(COALESCE(pt.ville_affichee, p.ville, '')) LIKE $key"; $params[$key] = $cand . '%'; // ← passe en '%'.$cand.'%' si tu veux "contient" } // Code postal : on garde un LIKE simple sur la saisie brute $ors[] = "COALESCE(p.code_postal,'') LIKE :wcp"; $params[':wcp'] = '%' . $w . '%'; $whereSql[] = '(' . implode(' OR ', $ors) . ')'; } $whereClause = $whereSql ? (' WHERE ' . implode(' AND ', $whereSql)) : ''; /* Total pour pagination (utile seulement si !isSearch, mais on calcule une fois) */ $sqlCount = " SELECT COUNT(*) FROM praticien p LEFT JOIN praticien_traduction pt ON pt.praticien_id = p.id AND pt.langue = :lang $whereClause "; $stmt = $pdo->prepare($sqlCount); $paramsCount = $params; $paramsCount[':lang'] = $currentLang; $stmt->execute($paramsCount); $total = (int) $stmt->fetchColumn(); $totalPages = max(1, (int) ceil($total / $perPage)); /* Liste (paginée si pas de recherche, sinon tout) */ $sqlList = " SELECT p.id, p.titre, p.nom, p.prenom, p.photo, p.ville, p.code_postal, p.video, COALESCE(pt.nom_affiche, p.nom) AS nom_affiche, COALESCE(pt.prenom_affiche, p.prenom) AS prenom_affiche, COALESCE(pt.specialite, p.specialite) AS specialite_affichee, COALESCE(pt.ville_affichee, p.ville) AS ville_affichee, COALESCE(pt.presentation, p.presentation) AS presentation_affichee FROM praticien p LEFT JOIN praticien_traduction pt ON pt.praticien_id = p.id AND pt.langue = :lang $whereClause ORDER BY COALESCE(pt.specialite, p.specialite) ASC, COALESCE(pt.nom_affiche, p.nom) ASC "; if (! $isSearch) { $sqlList .= " LIMIT :limit OFFSET :offset"; } $stmt = $pdo->prepare($sqlList); $params[':lang'] = $currentLang; foreach ($params as $k => $v) { $stmt->bindValue($k, $v, PDO::PARAM_STR); } if (! $isSearch) { $stmt->bindValue(':limit', $perPage, PDO::PARAM_INT); $stmt->bindValue(':offset', ($page - 1) * $perPage, PDO::PARAM_INT); } $stmt->execute(); $praticiens = $stmt->fetchAll(PDO::FETCH_ASSOC); /* ========================= Rendu partiel AJAX (#results) ========================= */ function render_results(array $praticiens, array $translations, bool $isSearch, int $page, int $totalPages): void { ?>

<?php echo htmlspecialchars($translations['alt_photo'] ?? 'Photo du praticien', ENT_QUOTES, 'UTF-8') ?>
1): ?> <?php echo htmlspecialchars($translations['menu_liste_specialistes'] ?? 'Liste des praticiens', ENT_QUOTES, 'UTF-8') ?>