Files
maria_uslugi/result_search_original.php

516 lines
19 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare (strict_types = 1);
require_once __DIR__ . '/../includes/bootstrap_global.php';
require_once APP_HTDOCS . '/includes/avatar_initiales.php';
require_once __DIR__ . '/includes/search_helpers.php';
$SPEC_CANON = require __DIR__ . '/includes/spec_map.php';
$canon = $SPEC_CANON[$lang] ?? [];
$H1_LABELS = [
'ru' => [
'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);
?>
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title><?php echo htmlspecialchars($translations['seo_result_search_title'] ?? '', ENT_QUOTES, 'UTF-8') ?></title>
<meta name="description" content="<?php echo htmlspecialchars($translations['seo_result_search_desc'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
<meta name="keywords" content="<?php echo htmlspecialchars($translations['seo_result_search_keywords'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
<meta name="author" content="Uslugi.fr">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="<?php echo asset('/includes/site_style.css') ?>">
<link rel="stylesheet" href="<?php echo asset('/includes/custom_style.css') ?>">
<link rel="stylesheet" href="<?php echo asset('/includes/mobil_style.css') ?>">
<?php
$trackingFile = __DIR__ . '/includes/tracking.php';
if (is_file($trackingFile)) {
include_once $trackingFile;
}
?>
</head>
<body class="layout">
<?php if (! empty($logged)):
include_header_clean(APP_HTDOCS . '/public/includes/header.php');
else: ?>
<header class="site-header">
<div class="container">
<div class="header-left">
<div class="brand">
<a href="index.php"><img src="/public/uploads/img/logo_u.png" alt="uslugi.fr" class="logo-mobile"></a>
<a href="index.php"><img src="/public/uploads/img/logo.png" alt="uslugi.fr" class="logo-desktop"></a>
</div>
<div class="lang-switch">
<?php require __DIR__ . '/includes/menu_langue.php'; ?>
</div>
</div>
<nav class="header-actions">
<a class="btn-login" href="/connexion.php">
<span class="d-sm-none"><?php echo h($translations['btn_se_connecter'] ?? '') ?></span>
<span class="d-none d-sm-inline"><?php echo h($translations['ja_spec'] ?? '') ?></span>
</a>
<div class="dropdown">
<button class="btn btn-logo rounded-circle d-inline-flex align-items-center justify-content-center question-btn"
type="button" data-bs-toggle="dropdown" data-bs-auto-close="true"
aria-expanded="false" aria-label="Помощь">?</button>
<ul class="dropdown-menu dropdown-menu-end shadow help-menu menu-panel">
<li><a href="/how-it-work.php"><?php echo h($translations['kak_rabotaet'] ?? '') ?></a></li>
<li><a href="/about.php"><?php echo h($translations['pro_nas'] ?? '') ?></a></li>
<li><a href="/faq.php"><?php echo h($translations['faq'] ?? '') ?></a></li>
<li><a href="/contact.php"><?php echo h($translations['nam_pisat'] ?? '') ?></a></li>
</ul>
</div>
</nav>
</div>
</header>
<?php endif; ?>
<div class="main1">
<div class="content">
<div class="container">
<h1 class="results-title"><?php echo h($h1) ?></h1>
</div>
<section class="cards-section">
<div class="container pro-list">
<?php if ($rows): ?>
<?php foreach ($rows as $p):
$img = ! empty($p['photo'])
? "/public/uploads/" . ltrim((string) $p['photo'], '/')
: "/public/uploads/img/user_placeholder.png";
?>
<article class="pro-card">
<div class="pro-photo">
<a href="/detail_public.php?id=<?php echo (int) $p['id'] ?>">
<img src="<?php echo h($img ?: '/public/uploads/img/user_placeholder.png') ?>" alt="Фото специалиста">
</a>
</div>
<div class="pro-info">
<h3 class="pro-name">
<a href="/detail_public.php?id=<?php echo (int) $p['id'] ?>">
<?php echo h(($p['prenom'] ?? '') . ' ' . ($p['nom'] ?? '')) ?>
</a>
</h3>
<?php if (! empty($p['specialite'])): ?>
<div class="pro-spec"><?php echo h($p['specialite']) ?></div>
<?php endif; ?>
<?php if (! empty($p['presentation'])): ?>
<p class="pro-desc"><?php echo h(excerpt($p['presentation'], 360)) ?></p>
<?php endif; ?>
<a href="/detail_public.php?id=<?php echo (int) $p['id'] ?>">
<?php echo h($translations['plus_details'] ?? 'Подробнее'); ?>
</a>
</div>
<?php if (! empty($p['video'])): ?>
<div class="pro-video-col">
<?php
$vinfo = video_thumb_info($p['video']);
$alt = htmlspecialchars($translations['video_alt'] ?? 'Vidéo', ENT_QUOTES, 'UTF-8');
$thumb = $vinfo['thumb'] ?? null;
$prov = $vinfo['provider'] ?? 'other';
$vid = $vinfo['id'] ?? null;
$raw = trim((string) ($p['video'] ?? ''));
?>
<?php if (in_array($prov, ['youtube', 'dailymotion'], true) && $vid): ?>
<a class="pro-video" href="#"
data-provider="<?php echo htmlspecialchars($prov, ENT_QUOTES, 'UTF-8') ?>"
data-id="<?php echo htmlspecialchars($vid, ENT_QUOTES, 'UTF-8') ?>"
aria-label="<?php echo $alt ?>">
<img src="<?php echo htmlspecialchars($thumb ?: '/public/uploads/img/video_placeholder.jpg', ENT_QUOTES, 'UTF-8') ?>"
alt="<?php echo $alt ?>">
</a>
<?php else: ?>
<a class="pro-video" href="<?php echo htmlspecialchars($raw, ENT_QUOTES, 'UTF-8') ?>" target="_blank" rel="noopener">
<img src="<?php echo htmlspecialchars($thumb ?: '/public/uploads/img/video_placeholder.jpg', ENT_QUOTES, 'UTF-8') ?>"
alt="<?php echo $alt ?>">
</a>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="pro-cta-col">
<a class="pro-cta" href="public/client/reserver_rdv.php?praticien_id=<?php echo (int) $p['id'] ?>">
<?php echo h($translations['den'] ?? 'Выберите день') ?>
</a>
</div>
</article>
<?php endforeach; ?>
<?php else: ?>
<div class="no-results"><?php echo h($translations['probuite'] ?? 'Нет результатов') ?></div>
<?php endif; ?>
</div>
<?php if ($pages > 1): ?>
<?php
$qBase = $baseQuery ? ($baseQuery . '&') : '';
$prevPage = max(1, $page - 1);
$nextPage = min($pages, $page + 1);
?>
<nav class="pager" aria-label="Pagination">
<a class="pager-arrow <?php echo $page <= 1 ? 'is-disabled' : '' ?>"
href="<?php echo h('/result_search.php?' . $qBase . 'page=' . $prevPage) ?>"
aria-label="Page précédente">&laquo;&laquo;</a>
<?php
$start = max(1, $page - 2);
$end = min($pages, $page + 2);
for ($i = $start; $i <= $end; $i++):
$href = '/result_search.php?' . $qBase . 'page=' . $i;
?>
<a class="pager-page <?php echo $i === $page ? 'is-active' : '' ?>"
href="<?php echo h($href) ?>"><?php echo (int) $i ?></a>
<?php endfor; ?>
<a class="pager-arrow <?php echo $page >= $pages ? 'is-disabled' : '' ?>"
href="<?php echo h('/result_search.php?' . $qBase . 'page=' . $nextPage) ?>"
aria-label="Page suivante">&raquo;&raquo;</a>
</nav>
<?php endif; ?>
</section>
</div>
</div>
<!-- Modal vidéo -->
<div id="lightboxVideo" class="vmodal" aria-hidden="true">
<div class="vmodal__backdrop" data-close></div>
<div class="vmodal__dialog" role="dialog" aria-modal="true" aria-label="Vidéo">
<div class="vmodal__header">
<h5 class="vmodal__title">Vidéo</h5>
<button class="vmodal__close" type="button" title="Fermer" aria-label="Fermer" data-close>×</button>
</div>
<div class="vmodal__body">
<div class="vmodal__ratio">
<iframe id="vPlayer" src="" title="Video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
referrerpolicy="strict-origin-when-cross-origin"></iframe>
</div>
</div>
<div class="vmodal__footer">
<button class="btn btn-secondary-like" type="button" data-close>Fermer</button>
</div>
</div>
</div>
<footer class="site-footer">
<div class="container">
<p><?php echo $translations['copyr'] ?></p>
</div>
</footer>
<script>
(function(){
const modal = document.getElementById('lightboxVideo');
const frame = document.getElementById('vPlayer');
function buildSrc(provider, id){
if (!provider || !id) return '';
if (provider === 'youtube') return 'https://www.youtube-nocookie.com/embed/' + id + '?autoplay=1&rel=0&modestbranding=1';
if (provider === 'dailymotion') return 'https://www.dailymotion.com/embed/video/' + id + '?autoplay=1&ui-start-screen-info=false';
return '';
}
function openModal(src){
if (!src) return;
frame.src = src;
modal.setAttribute('aria-hidden','false');
document.body.style.overflow = 'hidden';
}
function closeModal(){
frame.src = '';
modal.setAttribute('aria-hidden','true');
document.body.style.overflow = '';
}
document.addEventListener('click', function(e){
const a = e.target.closest('a.pro-video');
if (!a) return;
const provider = a.getAttribute('data-provider');
const id = a.getAttribute('data-id');
if (provider && id){
e.preventDefault();
const src = buildSrc(provider, id);
openModal(src);
}
});
modal.addEventListener('click', (e)=>{ if (e.target.hasAttribute('data-close')) closeModal(); });
window.addEventListener('keydown', (e)=>{ if (e.key === 'Escape' && modal.getAttribute('aria-hidden') === 'false') closeModal(); });
})();
</script>
<script src="/js/bootstrap.bundle.min.js"></script>
</body>
</html>