2026-09-13 single-teacher.php
This commit is contained in:
parent
1efcbb4616
commit
b47113ab98
@ -5019,6 +5019,35 @@ footer a:hover,
|
||||
.form-wrapper .b24-form-container {
|
||||
width: 100%;
|
||||
}
|
||||
.form-wrapper__offer {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-wrapper__badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 999px;
|
||||
background: var(--main-yellow, #FFD43E);
|
||||
color: #1F2937;
|
||||
font-weight: 700;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.form-wrapper__title {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.3;
|
||||
font-weight: 700;
|
||||
color: #1F2937;
|
||||
}
|
||||
.form-wrapper__hint {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
color: #64748B;
|
||||
}
|
||||
.form-wrapper .b24-form {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
|
||||
@ -459,33 +459,6 @@
|
||||
color: var(--color-text-muted, #475569);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.teacher-single__subject-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
.teacher-single__subject-list li {
|
||||
padding: 14px 16px;
|
||||
border-radius: 14px;
|
||||
background: var(--color-bg, #FFFFFF);
|
||||
border: 1px solid var(--color-border, #E2E8F0);
|
||||
}
|
||||
.teacher-single__subject-list strong {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.teacher-single__subject-list span {
|
||||
display: inline-block;
|
||||
margin: 2px 6px 0 0;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill, 100px);
|
||||
background: var(--color-primary-light, #EFF6FF);
|
||||
color: var(--color-primary, #2563EB);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.teacher-single__cert-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
@ -727,6 +700,12 @@
|
||||
.teacher-single__timeline-item {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.teacher-single__hero .ds-hero__inner {
|
||||
gap: 28px;
|
||||
}
|
||||
.teacher-single__hero-facts {
|
||||
margin: var(--space-4, 24px) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
@ -791,10 +770,9 @@
|
||||
box-shadow: var(--shadow-md, 0 8px 24px rgba(15, 23, 42, 0.1));
|
||||
}
|
||||
|
||||
/* Микро-плитки: радиус 16 (иконки, серты, subject-list, FAQ, trust-shield) */
|
||||
/* Микро-плитки: радиус 16 (иконки, серты, FAQ, trust-shield) */
|
||||
.teacher-single__segment-icon,
|
||||
.teacher-single__cert,
|
||||
.teacher-single__subject-list li,
|
||||
.teacher-single__trust-shield,
|
||||
.teacher-single__faq-item {
|
||||
border-radius: 16px;
|
||||
@ -1170,5 +1148,70 @@
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.teacher-single__course-link {
|
||||
padding: 8px 0;
|
||||
min-height: 44px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Соц-доказательство (после программ) ── */
|
||||
.teacher-single__social-proof {
|
||||
padding: 24px 0;
|
||||
}
|
||||
.teacher-single__social-proof-inner {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px 48px;
|
||||
padding: 20px 28px;
|
||||
border: 1px solid var(--color-border, rgba(15, 23, 42, 0.08));
|
||||
border-radius: 20px;
|
||||
background: rgba(15, 23, 42, 0.03);
|
||||
}
|
||||
.teacher-single__sp-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 6px 10px;
|
||||
max-width: 420px;
|
||||
}
|
||||
.teacher-single__sp-stars {
|
||||
display: inline-flex;
|
||||
gap: 2px;
|
||||
color: rgba(15, 23, 42, 0.25);
|
||||
}
|
||||
.teacher-single__sp-stars svg.is-filled {
|
||||
color: #F59E0B;
|
||||
}
|
||||
.teacher-single__sp-num {
|
||||
font-family: var(--font-display, inherit);
|
||||
font-size: 1.375rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
color: var(--color-text, #0F172A);
|
||||
}
|
||||
.teacher-single__sp-text {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.4;
|
||||
color: var(--color-text-muted, #64748B);
|
||||
}
|
||||
.teacher-single__sp-link {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-primary, #006DB7);
|
||||
text-decoration: none;
|
||||
}
|
||||
.teacher-single__sp-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@media (max-width: 639px) {
|
||||
.teacher-single__social-proof-inner {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
padding: 18px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,13 +25,20 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ── Sticky CTA (mobile): показать после hero ──
|
||||
// ── Sticky CTA (mobile): показать после hero, скрыть у финального CTA ──
|
||||
var sticky = document.querySelector('.teacher-single__sticky');
|
||||
if (sticky) {
|
||||
var hero = document.querySelector('.teacher-single__hero');
|
||||
var finalCta = document.querySelector('.teacher-single__final');
|
||||
if (hero) {
|
||||
var showSticky = function () {
|
||||
sticky.hidden = window.scrollY < hero.offsetHeight - 80;
|
||||
var afterHero = window.scrollY >= hero.offsetHeight - 80;
|
||||
var finalVisible = false;
|
||||
if (finalCta) {
|
||||
var fr = finalCta.getBoundingClientRect();
|
||||
finalVisible = fr.top < window.innerHeight && fr.bottom > 0;
|
||||
}
|
||||
sticky.hidden = !afterHero || finalVisible;
|
||||
};
|
||||
showSticky();
|
||||
var ticking = false;
|
||||
|
||||
@ -96,6 +96,11 @@ if (! empty($b24_raw)) {
|
||||
alt="Закрыть"
|
||||
loading="lazy">
|
||||
</button>
|
||||
<div class="form-wrapper__offer">
|
||||
<span class="form-wrapper__badge">Бесплатно</span>
|
||||
<p class="form-wrapper__title">Записаться на бесплатную диагностику</p>
|
||||
<p class="form-wrapper__hint">Перезвоним за 15 минут · Без обязательств</p>
|
||||
</div>
|
||||
<div class="b24-form-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3253,6 +3253,43 @@ function dekart_teacher_review_count(int $teacher_id): int
|
||||
return isset($map[ $teacher_id ]) ? (int) $map[ $teacher_id ] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Средняя оценка преподавателя по реальным отзывам CPT (поле review_star).
|
||||
* Один SQL-агрегат вместо перебора всех отзывов carbon_get_post_meta на каждый id
|
||||
* (500+ отзывов = 500 запросов, object-cache может быть отключён).
|
||||
* Возвращает 0.0, если валидных оценок нет.
|
||||
*/
|
||||
function dekart_teacher_review_avg(int $teacher_id): float
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
static $cache = array();
|
||||
if (isset($cache[ $teacher_id ])) {
|
||||
return $cache[ $teacher_id ];
|
||||
}
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT AVG(CAST(pm.meta_value AS DECIMAL(10, 2)))
|
||||
FROM {$wpdb->postmeta} pm
|
||||
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
|
||||
INNER JOIN {$wpdb->postmeta} pmt
|
||||
ON pmt.post_id = pm.post_id
|
||||
AND pmt.meta_key = %s
|
||||
AND pmt.meta_value = %s
|
||||
WHERE pm.meta_key = %s
|
||||
AND pm.meta_value REGEXP '^[1-5]$'
|
||||
AND p.post_type = 'reviews'
|
||||
AND p.post_status = 'publish'",
|
||||
'_review_teacher|||0|id',
|
||||
(string) $teacher_id,
|
||||
'_review_star'
|
||||
);
|
||||
|
||||
$cache[ $teacher_id ] = (float) $wpdb->get_var($sql);
|
||||
|
||||
return $cache[ $teacher_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* OG-изображение 1200×630 для страницы «Преподаватели».
|
||||
* Глобальный дефолт Yoast (banner-dekart-small.webp 355×250) даёт битый сниппет.
|
||||
|
||||
@ -1,15 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Личная страница преподавателя (Bento 2026) — конверсионная воронка.
|
||||
* Личная страница преподавателя (Bento 2026).
|
||||
*
|
||||
* Воронка: Meta → Hero (+2 CTA + риск) → Цифры (человек+система) →
|
||||
* Дефицит → Сегменты (+4-й под спойлером) → Личная история + «Как проходит
|
||||
* занятие» → Кейсы (табы 9/11) → FAQ (формат) → Отзывы → Доверие/лицензия →
|
||||
* Другие преподаватели → финальный CTA → Карта. Sticky-CTA на мобильном.
|
||||
*
|
||||
* Данные — из CF карточки преподавателя (деплой через контент).
|
||||
* Общие части переиспользуются: template-parts/reviews, template-parts/teachers-cards,
|
||||
* template-parts/contacts (как на главной).
|
||||
* Данные — из CF карточки преподавателя. Общие части: reviews-link,
|
||||
* teachers-cards, contacts, author-eaat. Sticky-CTA на мобильном.
|
||||
*
|
||||
* @package Dekart
|
||||
*/
|
||||
@ -18,6 +12,20 @@ if (! defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// og:image:alt из featured image (паттерн page-camp.php)
|
||||
add_action('wp_head', function () {
|
||||
if (! is_singular('teacher')) {
|
||||
return;
|
||||
}
|
||||
$thumb_id = get_post_thumbnail_id(get_queried_object_id());
|
||||
if ($thumb_id) {
|
||||
$alt = get_post_meta($thumb_id, '_wp_attachment_image_alt', true);
|
||||
if ($alt) {
|
||||
echo '<meta property="og:image:alt" content="' . esc_attr($alt) . '" />' . "\n";
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
|
||||
get_header();
|
||||
|
||||
$tid = (int) get_the_ID();
|
||||
@ -28,10 +36,28 @@ $branch_phone = get_option('branch_phone') ?: carbon_get_theme_option('site_pho
|
||||
$branch_city = get_option('branch_city') ?: carbon_get_theme_option('site_city', 'Щёлково');
|
||||
$branch_city_prep = get_option('branch_city_prep', 'в Щёлково') ?: 'в Щёлково';
|
||||
$branch_name = get_option('branch_name') ?: carbon_get_theme_option('site_name', 'Декарт');
|
||||
$phone_clean = $branch_phone ? preg_replace('/[\s\-\–\(\)]/', '', $branch_phone) : '';
|
||||
$wa_on = (bool) get_option('branch_messenger_whatsapp_on', 1);
|
||||
$wa_url = $wa_on ? get_option('branch_messenger_whatsapp') : '';
|
||||
$wa_url = $wa_url ?: ( $phone_clean ? 'https://wa.me/' . $phone_clean : '' );
|
||||
// Тел.: цифры и «+». WhatsApp: 8-XXXXXXXXXX → 7-XXXXXXXXXX, без номера — без ссылки.
|
||||
$phone_clean = $branch_phone ? preg_replace('/[^0-9+]/', '', $branch_phone) : '';
|
||||
$wa_on = (bool) get_option('branch_messenger_whatsapp_on', 1);
|
||||
$wa_number = '';
|
||||
if ($wa_on) {
|
||||
$wa_number = trim((string) get_option('branch_messenger_whatsapp'));
|
||||
if (false !== strpos($wa_number, 'wa.me/')) {
|
||||
$wa_number = substr($wa_number, strpos($wa_number, 'wa.me/') + 6);
|
||||
}
|
||||
$wa_number = preg_replace('/[^0-9]/', '', $wa_number);
|
||||
if ('' !== $wa_number && '8' === $wa_number[0] && 11 === strlen($wa_number)) {
|
||||
$wa_number = '7' . substr($wa_number, 1);
|
||||
}
|
||||
}
|
||||
// Фолбэк: телефон филиала.
|
||||
if ('' === $wa_number && $phone_clean) {
|
||||
$wa_number = preg_replace('/[^0-9]/', '', $phone_clean);
|
||||
if ('8' === $wa_number[0] && 11 === strlen($wa_number)) {
|
||||
$wa_number = '7' . substr($wa_number, 1);
|
||||
}
|
||||
}
|
||||
$wa_url = ('' !== $wa_number) ? 'https://wa.me/' . $wa_number : '';
|
||||
|
||||
// ─── Профиль преподавателя (CF) ───
|
||||
$direction = carbon_get_post_meta($tid, 'teacher_direction');
|
||||
@ -45,11 +71,24 @@ if ($start_year && absint($start_year) > 1900) {
|
||||
$tenure = max(0, (int) current_time('Y') - (int) $start_year);
|
||||
}
|
||||
$avatar_id = carbon_get_post_meta($tid, 'teacher_avatar');
|
||||
$avatar_url = $avatar_id ? wp_get_attachment_url($avatar_id) : '';
|
||||
$avatar_url = $avatar_id ? wp_get_attachment_url($avatar_id) : ''; // Schema Person.image
|
||||
$avatar_alt = $avatar_id ? get_post_meta($avatar_id, '_wp_attachment_image_alt', true) : '';
|
||||
if (empty($avatar_alt)) {
|
||||
$avatar_alt = 'Преподаватель ' . $full_name . ' — школа «' . $branch_name . '» ' . $branch_city;
|
||||
}
|
||||
// image() = проверка наличия + готовый HTML, без отдельного url-запроса.
|
||||
$avatar_img = $avatar_id ? wp_get_attachment_image(
|
||||
(int) $avatar_id,
|
||||
array( 600, 600 ),
|
||||
false,
|
||||
array(
|
||||
'class' => 'ds-hero__photo',
|
||||
'alt' => $avatar_alt,
|
||||
'loading' => 'eager',
|
||||
'decoding' => 'async',
|
||||
'fetchpriority' => 'high', // LCP-изображение
|
||||
)
|
||||
) : '';
|
||||
|
||||
$avg_score = carbon_get_post_meta($tid, 'teacher_avg_score');
|
||||
$seats_left = carbon_get_post_meta($tid, 'teacher_seats_left');
|
||||
@ -58,8 +97,6 @@ $avg_label = carbon_get_post_meta($tid, 'teacher_avg_label');
|
||||
if (empty($avg_label)) {
|
||||
$avg_label = 'Средний балл учеников';
|
||||
}
|
||||
$rating_label = 'Рейтинг преподавателя';
|
||||
$group_label = 'человек в мини-группе — внимание как на репетиторе';
|
||||
$exam_label = carbon_get_post_meta($tid, 'teacher_exam_label') ?: 'нового набора';
|
||||
$cta_primary = carbon_get_post_meta($tid, 'teacher_cta_primary') ?: 'Записаться на диагностику';
|
||||
$sched_label = carbon_get_post_meta($tid, 'teacher_schedule_label') ?: 'Посмотреть расписание групп';
|
||||
@ -68,7 +105,7 @@ $risk_note = carbon_get_post_meta($tid, 'teacher_risk_note');
|
||||
if (empty($risk_note)) {
|
||||
$risk_note = 'Перезвоним за 15 минут · Без обязательств';
|
||||
}
|
||||
$school_num = carbon_get_post_meta($tid, 'teacher_school_num') ?: '100%';
|
||||
$school_num = carbon_get_post_meta($tid, 'teacher_school_num');
|
||||
$school_label = carbon_get_post_meta($tid, 'teacher_school_label');
|
||||
if (empty($school_label)) {
|
||||
$school_label = 'учеников продолжают обучение на следующий учебный год';
|
||||
@ -83,7 +120,7 @@ $about_text = carbon_get_post_meta($tid, 'teacher_about');
|
||||
$education = carbon_get_post_meta($tid, 'teacher_education');
|
||||
$add_education = carbon_get_post_meta($tid, 'teacher_add_education');
|
||||
$video_html = carbon_get_post_meta($tid, 'teacher_video');
|
||||
// Если в поле ссылка (не iframe) — собираем iframe VK/YouTube автоматически. Пусто → блок не выводится.
|
||||
// Ссылка VK/YouTube → iframe. Пусто → блок скрыт.
|
||||
if ($video_html && 0 !== strpos(ltrim($video_html), '<')) {
|
||||
$video_url = trim($video_html);
|
||||
if (preg_match('~vk\.com/video(-?\d+)_(\d+)~', $video_url, $vm)) {
|
||||
@ -97,10 +134,10 @@ if (empty($license)) {
|
||||
$license = 'Частная школа «' . $branch_name . '» работает по лицензии на образовательную деятельность (бессрочная). Копия лицензии и реквизиты — по запросу у администратора.';
|
||||
}
|
||||
|
||||
// Отзывы (счётчик реальных, как везде на сайте)
|
||||
// Счётчик реальных отзывов.
|
||||
$review_count = function_exists('dekart_teacher_review_count') ? dekart_teacher_review_count($tid) : 0;
|
||||
|
||||
// Подзаголовок hero: редактируется в админке (вкладка Hero). Пусто — авто-сборка.
|
||||
// Подзаголовок hero — из админки (вкладка Hero); пусто — авто-сборка.
|
||||
$hero_subtitle = trim((string) carbon_get_post_meta($tid, 'teacher_hero_subtitle'));
|
||||
if ('' === $hero_subtitle) {
|
||||
$hero_subtitle = ( $subj ? $subj : 'Обучение' ) . ' — мини-группы ' . $group_size . ' чел. · ' . $exam_label . ' · школа «' . $branch_name . '» ' . $branch_city_prep;
|
||||
@ -160,17 +197,20 @@ foreach ($legacy_questions as $lq) {
|
||||
}
|
||||
|
||||
// ─── Дни до экзамена (полоса дефицита) ───
|
||||
// «Сегодня» = 0, «завтра» = 1; прошедшая дата → блок скрыт.
|
||||
$days_left = '';
|
||||
if ($exam_date && preg_match('/^\d{4}-\d{2}-\d{2}$/', $exam_date)) {
|
||||
$diff = (int) (( strtotime($exam_date) - current_time('timestamp') ) / DAY_IN_SECONDS);
|
||||
$tz = wp_timezone();
|
||||
$exam_ts = (int) ( new DateTime($exam_date, $tz) )->format('U');
|
||||
$today_ts = (int) ( new DateTime('now', $tz) )->setTime(0, 0)->format('U');
|
||||
$diff = (int) round( ( $exam_ts - $today_ts ) / DAY_IN_SECONDS );
|
||||
if ($diff >= 0) {
|
||||
$days_left = (string) $diff;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Микроразметка (Schema.org JSON-LD, единый @graph) ───
|
||||
// Данные — только из CF преподавателя и настроек филиала (branch_*/site_*).
|
||||
// Пустые свойства в схему не выводятся (Google ругается на пустые обязательные поля).
|
||||
// ─── Микроразметка (единый @graph) ───
|
||||
// Пустые свойства не выводим (Google ругается на пустые обязательные поля).
|
||||
$teachers_page = get_page_by_path('our-teachers');
|
||||
|
||||
$page_url = get_permalink($tid);
|
||||
@ -205,7 +245,7 @@ if ($branch_email) {
|
||||
$org['email'] = $branch_email;
|
||||
}
|
||||
|
||||
// Адрес филиала — из опций (branch_*), фолбэк — CF theme options (site_*)
|
||||
// Адрес филиала: branch_* → фолбэк site_*.
|
||||
$org_addr = array();
|
||||
$org_street = get_option('branch_address') ?: carbon_get_theme_option('site_address');
|
||||
$org_city = get_option('branch_city') ?: carbon_get_theme_option('site_city');
|
||||
@ -220,7 +260,7 @@ if (! empty($org_addr)) {
|
||||
$org['address'] = $org_addr;
|
||||
}
|
||||
|
||||
// Лицензия (hasCredential) — из настроек филиала
|
||||
// Лицензия — из настроек филиала.
|
||||
$org_license = get_option('branch_license') ?: carbon_get_theme_option('site_license');
|
||||
if (! empty($org_license)) {
|
||||
$org['hasCredential'] = array(
|
||||
@ -230,8 +270,7 @@ if (! empty($org_license)) {
|
||||
}
|
||||
|
||||
// ── Person (преподаватель) ──
|
||||
// Каталог программ филиала (единый для всего сайта): название → ссылка.
|
||||
// Цена берётся из этого каталога только для микроразметки Course.offers (не выводится на странице).
|
||||
// Каталог программ филиала: название → url/price (цена — только для Course.offers в микроразметке).
|
||||
$branch_programs_by_name = array();
|
||||
foreach ((array) get_option('branch_programs', array()) as $bp) {
|
||||
$pn = trim((string) ( $bp['name'] ?? '' ));
|
||||
@ -243,9 +282,7 @@ foreach ((array) get_option('branch_programs', array()) as $bp) {
|
||||
}
|
||||
}
|
||||
|
||||
// Предметы: title → name курса (выбор из каталога филиала),
|
||||
// teacher_subjects_topics → чипы. URL — из каталога филиала (fallback: legacy url преподавателя);
|
||||
// цена — из того же каталога, но используется только микроразметкой Course.offers.
|
||||
// Предметы: title → курс; topics → чипы. URL/цена — из каталога филиала (fallback — CF преподавателя).
|
||||
$teacher_subjects = (array) carbon_get_post_meta($tid, 'teacher_subjects');
|
||||
$subject_items = array(); // [title => ['name', 'url', 'price', 'topics[]']] без дубликатов
|
||||
foreach ($teacher_subjects as $ts) {
|
||||
@ -283,11 +320,13 @@ $geo_walk = get_option('branch_logistics_walk');
|
||||
$geo_parking = get_option('branch_logistics_parking');
|
||||
$geo_schools = get_option('branch_logistics_schools');
|
||||
|
||||
// ─── Результаты учеников (CF; пусто — сборка из метрик преподавателя) ───
|
||||
$results = (array) carbon_get_post_meta($tid, 'teacher_results');
|
||||
if (empty($results) || ! is_array($results)) {
|
||||
$results = array();
|
||||
if ('' !== $school_num) { $results[] = array( 'result_title' => $school_num, 'result_text' => $school_label ); }
|
||||
// ─── Результаты учеников: CF или авто-сборка ───
|
||||
$cf_results = carbon_get_post_meta($tid, 'teacher_results');
|
||||
$results = array();
|
||||
if (is_array($cf_results) && ! empty($cf_results)) {
|
||||
$results = array_values($cf_results);
|
||||
} else {
|
||||
if (! empty($school_num)) { $results[] = array( 'result_title' => (string) $school_num, 'result_text' => $school_label ); }
|
||||
if ($avg_score) { $results[] = array( 'result_title' => $avg_score, 'result_text' => $avg_label ); }
|
||||
if ($tenure) { $results[] = array( 'result_title' => $tenure . ' лет', 'result_text' => 'педагогический стаж преподавателя' ); }
|
||||
if ($review_count > 0) { $results[] = array( 'result_title' => (string) $review_count, 'result_text' => 'реальных отзывов родителей о преподавателе' ); }
|
||||
@ -317,69 +356,23 @@ if ('' !== $diploma) {
|
||||
$person['alumniOf'] = array('@type' => 'CollegeOrUniversity', 'name' => $diploma);
|
||||
}
|
||||
|
||||
// AggregateRating — только реальные данные. Размещается на Course, а не на Person
|
||||
// (свойство aggregateRating невалидно для типа Person в schema.org).
|
||||
$course_rating = array();
|
||||
if ($rating_num > 0 && $review_count > 0) {
|
||||
$course_rating = array(
|
||||
'@type' => 'AggregateRating',
|
||||
'ratingValue' => number_format($rating_num, 1),
|
||||
'bestRating' => '5',
|
||||
'ratingCount' => $review_count,
|
||||
);
|
||||
}
|
||||
// ── Рейтинг преподавателя ──
|
||||
// Среднее реальных отзывов (SQL-агрегат); CF teacher_rating — фолбэк, когда отзывов нет.
|
||||
$sp_avg_num = function_exists('dekart_teacher_review_avg')
|
||||
? (float) dekart_teacher_review_avg($tid)
|
||||
: 0.0;
|
||||
$sp_avg = $sp_avg_num > 0 ? number_format($sp_avg_num, 1, '.', '') : '';
|
||||
$rating_num = $sp_avg_num > 0 ? $sp_avg_num : ( is_numeric($rating) ? (float) $rating : 0.0 );
|
||||
|
||||
// Review — реальные отзывы из CPT «reviews», связанные с преподавателем. Не выдумываем.
|
||||
// Размещаются на Course (свойство review невалидно для Person).
|
||||
$review_pool = get_posts(array(
|
||||
'post_type' => 'reviews',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'no_found_rows' => true,
|
||||
));
|
||||
$reviews = array();
|
||||
foreach ($review_pool as $rid) {
|
||||
$link = carbon_get_post_meta((int) $rid, 'review_teacher');
|
||||
if (! is_array($link) || empty($link[0]['id']) || (int) $link[0]['id'] !== $tid) {
|
||||
continue;
|
||||
}
|
||||
$r_author = trim((string) get_the_title((int) $rid));
|
||||
$r_body = trim((string) carbon_get_post_meta((int) $rid, 'review_text'));
|
||||
$r_star = (int) ( carbon_get_post_meta((int) $rid, 'review_star') ?: 5 );
|
||||
// AggregateRating не размечаем: невалиден для Person в schema.org, Google не даёт
|
||||
// по нему rich results. Средний балл — только UI; в Schema — отдельные Review с reviewRating.
|
||||
|
||||
$review = array('@type' => 'Review');
|
||||
if ('' !== $r_author) {
|
||||
$review['author'] = array('@type' => 'Person', 'name' => $r_author);
|
||||
}
|
||||
$pub = get_the_date('c', (int) $rid);
|
||||
if ($pub) {
|
||||
$review['datePublished'] = $pub;
|
||||
}
|
||||
if ($r_star > 0) {
|
||||
$review['reviewRating'] = array(
|
||||
'@type' => 'Rating',
|
||||
'ratingValue' => (string) $r_star,
|
||||
'bestRating' => '5',
|
||||
);
|
||||
}
|
||||
if ('' !== $r_body) {
|
||||
$review['reviewBody'] = $r_body;
|
||||
}
|
||||
$reviews[] = $review;
|
||||
if (count($reviews) >= 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! empty($reviews)) {
|
||||
// $reviews переносятся на каждый Course ниже (в блоке сборки Course).
|
||||
}
|
||||
// Review не размечаем: Google не поддерживает Person среди itemReviewed для review
|
||||
// rich results. Отзывы остаются в видимом HTML (reviews-link) — там их место.
|
||||
|
||||
// ── Course — по каждому предмету из CF (комбинация предмет + формат мини-группы) ──
|
||||
// Валидная структура: Course → hasCourseInstance (CourseInstance) с courseMode/instructor;
|
||||
// aggregateRating и review живут на Course (не на Person).
|
||||
// Person и Org объявляются один раз (@id), в курсах — только ссылки {"@id": ...}.
|
||||
$build_course = function ( $name, $cid, $availability = null, $description = '', $price = '' ) use ( $page_url, $org_id, $person_id, $group_size, $full_name, $course_rating, $reviews, $branch_price ) {
|
||||
// ── Course — по каждому предмету (предмет + мини-группа) ──
|
||||
// Person и Org объявляются один раз (@id); в курсах — ссылки.
|
||||
$build_course = function ( $name, $cid, $availability = null, $description = '', $price = '' ) use ( $page_url, $org_id, $person_id, $group_size, $full_name, $branch_price ) {
|
||||
$course = array(
|
||||
'@type' => 'Course',
|
||||
'@id' => $page_url . '#course-' . $cid,
|
||||
@ -398,28 +391,30 @@ $build_course = function ( $name, $cid, $availability = null, $description = '',
|
||||
'category' => 'Мини-группа 2–' . $group_size . ' человек',
|
||||
),
|
||||
);
|
||||
// Цена (только микроразметка Course.offers): из каталога программы (per-program) → fallback branch_price.
|
||||
// Цена — только микроразметка: из каталога программы → фолбэк branch_price;
|
||||
// Schema требует число — извлекаем из строки вида «от 1 290 ₽».
|
||||
$offer_price = ( '' !== (string) $price ) ? $price : $branch_price;
|
||||
if ('' !== (string) $offer_price) {
|
||||
$course['offers']['price'] = $offer_price;
|
||||
$offer_price_norm = preg_replace('/\s+/u', '', (string) $offer_price);
|
||||
if (preg_match('/\d+(?:[.,]\d+)?/', $offer_price_norm, $pm)) {
|
||||
$course['offers']['price'] = str_replace(',', '.', $pm[0]);
|
||||
$course['offers']['priceCurrency'] = 'RUB';
|
||||
}
|
||||
if (null !== $availability) {
|
||||
$course['offers']['availability'] = $availability;
|
||||
}
|
||||
if (! empty($course_rating)) {
|
||||
$course['aggregateRating'] = $course_rating;
|
||||
}
|
||||
if (! empty($reviews)) {
|
||||
$course['review'] = $reviews;
|
||||
}
|
||||
return $course;
|
||||
};
|
||||
|
||||
$courses = array();
|
||||
if (! empty($subject_names)) {
|
||||
foreach ($subject_names as $i => $cn) {
|
||||
$availability = ( $seats_left && (int) $seats_left > 0 ) ? 'https://schema.org/LimitedAvailability' : 'https://schema.org/InStock';
|
||||
// Availability: >0 → Limited, =0 → SoldOut, пусто → не указываем.
|
||||
$availability = null;
|
||||
if ('' !== (string) $seats_left) {
|
||||
$availability = ( (int) $seats_left > 0 )
|
||||
? 'https://schema.org/LimitedAvailability'
|
||||
: 'https://schema.org/SoldOut';
|
||||
}
|
||||
$courses[] = $build_course($cn, $i + 1, $availability, '', $subject_items[$cn]['price']);
|
||||
}
|
||||
} else {
|
||||
@ -480,7 +475,6 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
<meta itemprop="position" content="1">
|
||||
</span>
|
||||
<span aria-hidden="true">/</span>
|
||||
<?php $teachers_page = get_page_by_path('our-teachers'); ?>
|
||||
<?php if ($teachers_page) : ?>
|
||||
<span itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
||||
<a itemprop="item" href="<?php echo esc_url(get_permalink($teachers_page)); ?>"><span itemprop="name">Преподаватели</span></a>
|
||||
@ -543,10 +537,10 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
</div>
|
||||
|
||||
<div class="ds-hero__visual teacher-single__visual">
|
||||
<?php if ($avatar_url) : ?>
|
||||
<?php if ($avatar_img) : ?>
|
||||
<div class="ds-hero__photo-wrap">
|
||||
<div class="ds-hero__photo-frame teacher-single__photo-frame">
|
||||
<img class="ds-hero__photo" src="<?php echo esc_url($avatar_url); ?>" alt="<?php echo esc_attr($avatar_alt); ?>" width="600" height="600" loading="eager" decoding="async">
|
||||
<?php echo $avatar_img; ?>
|
||||
</div>
|
||||
<div class="ds-hero__photo-ring" aria-hidden="true"></div>
|
||||
</div>
|
||||
@ -558,9 +552,8 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
</section>
|
||||
|
||||
<?php
|
||||
// ─────────────────── ЦИФРЫ (человек + система) ───────────────────
|
||||
// Карточки цифр — ТОЛЬКО из CF complex teacher_metrics (цифра + подпись), можно добавлять/удалять.
|
||||
// Пустые карточки пропускаются. Нет карточек — блок скрыт (резервных полей нет).
|
||||
// ─────────────────── ЦИФРЫ ───────────────────
|
||||
// Карточки — только из CF complex teacher_metrics; пусто — секция скрыта.
|
||||
$metrics = array();
|
||||
$metric_cards = (array) carbon_get_post_meta($tid, 'teacher_metrics');
|
||||
if (! empty($metric_cards) && is_array($metric_cards)) {
|
||||
@ -618,9 +611,8 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
|
||||
<?php if (! empty($subject_items)) : ?>
|
||||
<?php
|
||||
// ─────────────────── ПРОГРАММЫ И НАПРАВЛЕНИЯ (H2-секции курсов) ───────────────────
|
||||
// Каждый курс — отдельная H2-секция «{Предмет} {в городе}» с якорем #course-N
|
||||
// (якорь совпадает с @id в микроразметке Course). Цена на странице не выводится.
|
||||
// ─────────────────── ПРОГРАММЫ (H2-секция на курс) ───────────────────
|
||||
// Якорь #course-N совпадает с @id в микроразметке. Цена на странице не выводится.
|
||||
?>
|
||||
<section class="teacher-single__courses section-bg reveal" data-bg="teacher-courses" aria-labelledby="teacher-courses-title">
|
||||
<div class="container">
|
||||
@ -662,6 +654,44 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
// ─────────────────── СОЦИАЛЬНОЕ ДОКАЗАТЕЛЬСТВО ───────────────────
|
||||
// Рейтинг преподавателя — $sp_avg (рассчитан выше), школы — из опций филиала.
|
||||
$branch_avg = (string) get_option('branch_avg_rating', '');
|
||||
$branch_cnt = (string) get_option('branch_ext_review_count', '');
|
||||
if ('' !== $sp_avg || $branch_avg || $branch_cnt) :
|
||||
?>
|
||||
<section class="teacher-single__social-proof section-bg reveal" data-bg="teacher-social-proof" aria-label="Отзывы родителей">
|
||||
<div class="container">
|
||||
<div class="teacher-single__social-proof-inner">
|
||||
<?php if ('' !== $sp_avg) : ?>
|
||||
<div class="teacher-single__sp-item">
|
||||
<span class="teacher-single__sp-stars" aria-hidden="true">
|
||||
<?php for ($sp_i = 1; $sp_i <= 5; $sp_i++) : ?>
|
||||
<svg class="<?php echo $sp_i <= (int) round((float) $sp_avg) ? 'is-filled' : ''; ?>" width="18" height="18" viewBox="0 0 20 20" fill="currentColor"><path d="M10 1l2.39 4.84 5.34.78-3.87 3.77.92 5.34L10 13.27l-4.78 2.51.92-5.34L2.27 6.6l5.34-.78L10 1z"/></svg>
|
||||
<?php endfor; ?>
|
||||
</span>
|
||||
<strong class="teacher-single__sp-num"><?php echo esc_html($sp_avg); ?></strong>
|
||||
<span class="teacher-single__sp-text">оценка преподавателя по отзывам родителей</span>
|
||||
<?php if ($review_count > 0) : ?>
|
||||
<a class="teacher-single__sp-link" href="#reviews"><?php echo esc_html($review_count); ?> отзывов о преподавателе →</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($branch_avg || $branch_cnt) : ?>
|
||||
<div class="teacher-single__sp-item">
|
||||
<strong class="teacher-single__sp-num"><?php echo esc_html($branch_avg); ?></strong>
|
||||
<span class="teacher-single__sp-text">рейтинг школы в Яндекс Картах</span>
|
||||
<?php if ($branch_cnt) : ?>
|
||||
<span class="teacher-single__sp-link"><?php echo esc_html($branch_cnt); ?> отзывов на внешних площадках</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
// ─────────────────── СЕГМЕНТЫ «ДЛЯ КОГО» ───────────────────
|
||||
?>
|
||||
@ -672,6 +702,7 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
<p class="bento-p">Формат мини-группы закрывает три разные задачи — выберите свою.</p>
|
||||
</div>
|
||||
<div class="teacher-single__segments-grid">
|
||||
<?php $seg_num = 0; ?>
|
||||
<?php foreach ($segments as $i => $seg) :
|
||||
if ($i >= 3) {
|
||||
break;
|
||||
@ -681,9 +712,10 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
if (! $s_title) {
|
||||
continue;
|
||||
}
|
||||
$seg_num++;
|
||||
?>
|
||||
<article class="teacher-single__segment-card">
|
||||
<span class="teacher-single__segment-icon" aria-hidden="true"><?php echo esc_html(str_pad((string) ( $i + 1 ), 2, '0', STR_PAD_LEFT)); ?></span>
|
||||
<span class="teacher-single__segment-icon" aria-hidden="true"><?php echo esc_html(str_pad((string) $seg_num, 2, '0', STR_PAD_LEFT)); ?></span>
|
||||
<h3 class="teacher-single__segment-title"><?php echo esc_html($s_title); ?></h3>
|
||||
<p class="teacher-single__segment-text"><?php echo esc_html($s_text); ?></p>
|
||||
</article>
|
||||
@ -743,15 +775,17 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
<div class="teacher-single__story-steps">
|
||||
<h2 class="bento-h2">Как проходит первое занятие</h2>
|
||||
<ol class="teacher-single__timeline">
|
||||
<?php $step_num = 0; ?>
|
||||
<?php foreach ($steps as $i => $step) :
|
||||
$st_title = $step['step_title'] ?? '';
|
||||
$st_text = $step['step_text'] ?? '';
|
||||
if (! $st_title) {
|
||||
continue;
|
||||
}
|
||||
$step_num++;
|
||||
?>
|
||||
<li class="teacher-single__timeline-item">
|
||||
<span class="teacher-single__timeline-num" aria-hidden="true"><?php echo esc_html($i + 1); ?></span>
|
||||
<span class="teacher-single__timeline-num" aria-hidden="true"><?php echo esc_html($step_num); ?></span>
|
||||
<div class="teacher-single__timeline-content">
|
||||
<h3><?php echo esc_html($st_title); ?></h3>
|
||||
<?php if ($st_text) : ?><p><?php echo esc_html($st_text); ?></p><?php endif; ?>
|
||||
@ -791,14 +825,24 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
<div class="container">
|
||||
<h2 id="teacher-cases-title" class="bento-h2">Результаты учеников</h2>
|
||||
<div class="teacher-single__cases-tabs" role="tablist" aria-label="Класс">
|
||||
<?php foreach ($case_tabs as $ci => $tab) : ?>
|
||||
<button type="button" role="tab" id="case-tab-<?php echo esc_attr($tab); ?>" aria-controls="case-panel-<?php echo esc_attr($tab); ?>" aria-selected="<?php echo 0 === $ci ? 'true' : 'false'; ?>" class="teacher-single__case-tab<?php echo 0 === $ci ? ' is-active' : ''; ?>" data-case-tab="<?php echo esc_attr($tab); ?>">
|
||||
<?php foreach ($case_tabs as $ci => $tab) :
|
||||
$tab_id = sanitize_title($tab) . '-' . $ci;
|
||||
if ('' === trim($tab_id, '-')) {
|
||||
$tab_id = 'tab-' . $ci;
|
||||
}
|
||||
?>
|
||||
<button type="button" role="tab" id="case-tab-<?php echo esc_attr($tab_id); ?>" aria-controls="case-panel-<?php echo esc_attr($tab_id); ?>" aria-selected="<?php echo 0 === $ci ? 'true' : 'false'; ?>" class="teacher-single__case-tab<?php echo 0 === $ci ? ' is-active' : ''; ?>" data-case-tab="<?php echo esc_attr($tab); ?>">
|
||||
<?php echo esc_html($tab); ?> класс
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php foreach ($case_tabs as $ci => $tab) : ?>
|
||||
<div role="tabpanel" id="case-panel-<?php echo esc_attr($tab); ?>" aria-labelledby="case-tab-<?php echo esc_attr($tab); ?>" class="teacher-single__case-panel<?php echo 0 === $ci ? ' is-active' : ''; ?>" data-case-panel="<?php echo esc_attr($tab); ?>">
|
||||
<?php foreach ($case_tabs as $ci => $tab) :
|
||||
$tab_id = sanitize_title($tab) . '-' . $ci;
|
||||
if ('' === trim($tab_id, '-')) {
|
||||
$tab_id = 'tab-' . $ci;
|
||||
}
|
||||
?>
|
||||
<div role="tabpanel" id="case-panel-<?php echo esc_attr($tab_id); ?>" aria-labelledby="case-tab-<?php echo esc_attr($tab_id); ?>" class="teacher-single__case-panel<?php echo 0 === $ci ? ' is-active' : ''; ?>" data-case-panel="<?php echo esc_attr($tab); ?>">
|
||||
<div class="teacher-single__cases-grid">
|
||||
<?php foreach ($cases as $case) :
|
||||
if (trim((string) ( $case['case_tab'] ?? '' )) !== $tab || empty($case['case_text'])) {
|
||||
@ -818,31 +862,15 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
// ─────────────────── ОБРАЗОВАНИЕ / ПРЕДМЕТЫ / СЕРТИФИКАТЫ ───────────────────
|
||||
if ($teacher_subjects || $education || $add_education || carbon_get_post_meta($tid, 'certificates')) :
|
||||
$certificates = (array) carbon_get_post_meta($tid, 'certificates');
|
||||
// ─────────────────── ОБРАЗОВАНИЕ / СЕРТИФИКАТЫ ───────────────────
|
||||
// Направления здесь не дублируем — они уже показаны выше в секции «Программы и направления».
|
||||
$certificates = (array) carbon_get_post_meta($tid, 'certificates');
|
||||
if ($education || $add_education || ! empty($certificates)) :
|
||||
?>
|
||||
<section class="teacher-single__edu section-bg reveal" data-bg="teacher-edu" aria-labelledby="teacher-edu-title">
|
||||
<div class="container">
|
||||
<h2 id="teacher-edu-title" class="bento-h2">Образование и опыт</h2>
|
||||
<div class="teacher-single__edu-grid">
|
||||
<?php if ($teacher_subjects) : ?>
|
||||
<div class="teacher-single__edu-col">
|
||||
<h3 class="teacher-single__edu-h">Направления</h3>
|
||||
<ul class="teacher-single__subject-list">
|
||||
<?php foreach ($teacher_subjects as $ts) :
|
||||
$s_title = $ts['teacher_subjects_title'] ?? '';
|
||||
if (! $s_title) {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<li>
|
||||
<strong><?php echo esc_html($s_title); ?></strong>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($education || $add_education) : ?>
|
||||
<div class="teacher-single__edu-col">
|
||||
<?php if ($education) : ?>
|
||||
@ -859,18 +887,25 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
<?php if ($certificates) : ?>
|
||||
<div class="teacher-single__cert-grid">
|
||||
<?php foreach ($certificates as $cert) :
|
||||
$c_id = $cert['certificates_foto'] ?? '';
|
||||
if (! $c_id) {
|
||||
continue;
|
||||
}
|
||||
$c_url = wp_get_attachment_url($c_id);
|
||||
$c_alt = $c_id ? get_post_meta($c_id, '_wp_attachment_image_alt', true) : '';
|
||||
if (! $c_url) {
|
||||
$c_id = $cert['certificates_foto'] ?? '';
|
||||
$c_alt = $c_id ? get_post_meta((int) $c_id, '_wp_attachment_image_alt', true) : '';
|
||||
// image() = проверка наличия + вывод, без отдельного url-запроса.
|
||||
$c_img = $c_id ? wp_get_attachment_image(
|
||||
(int) $c_id,
|
||||
'medium',
|
||||
false,
|
||||
array(
|
||||
'alt' => $c_alt ?: ('Диплом или сертификат — ' . $full_name),
|
||||
'loading' => 'lazy',
|
||||
'decoding' => 'async',
|
||||
)
|
||||
) : '';
|
||||
if (! $c_img) {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<figure class="teacher-single__cert">
|
||||
<img src="<?php echo esc_url($c_url); ?>" alt="<?php echo esc_attr($c_alt ?: 'Диплом или сертификат — ' . $full_name); ?>" loading="lazy" decoding="async">
|
||||
<?php echo $c_img; ?>
|
||||
</figure>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
@ -899,7 +934,7 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
// ───────────── ОТЗЫВЫ О ПРЕПОДАВАТЕЛЕ (bento-блок «как на главной», part reviews-link) ─────────────
|
||||
// ─── ОТЗЫВЫ О ПРЕПОДАВАТЕЛЕ (part reviews-link, как на главной) ───
|
||||
$show_social_reviews = carbon_get_post_meta($tid, 'teacher_show_social_reviews');
|
||||
if ('no' !== (string) $show_social_reviews) {
|
||||
get_template_part('template-parts/reviews-link', null, array( 'teacher' => $tid ));
|
||||
@ -922,7 +957,7 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
</section>
|
||||
|
||||
<?php
|
||||
// ─────────────────── ДРУГИЕ ПРЕПОДАВАТЕЛИ (общий блок сайта, как на главной) ───────────────────
|
||||
// ─── ДРУГИЕ ПРЕПОДАВАТЕЛИ (общий блок сайта) ───
|
||||
get_template_part('template-parts/teachers-cards');
|
||||
?>
|
||||
|
||||
@ -994,10 +1029,10 @@ $schema_graph[] = array('@type' => 'BreadcrumbList', 'itemListElement' => $bread
|
||||
</section>
|
||||
|
||||
<?php
|
||||
// ─────────────────── КОНТАКТЫ / НАШ АДРЕС (шаблон главной) ───────────────────
|
||||
// ─── КОНТАКТЫ / АДРЕС ───
|
||||
get_template_part('template-parts/contacts');
|
||||
|
||||
// ─────────────────── БЛОК ЭКСПЕРТНОСТИ (E-E-A-T): автор — сам преподаватель, под картой ───────────────────
|
||||
// ─── ЭКСПЕРТНОСТЬ (E-E-A-T): автор — преподаватель, под картой ───
|
||||
$teacher_creds = trim(implode(', ', array_filter(array(
|
||||
$direction ?: '',
|
||||
($tenure ? 'педагогический стаж ' . $tenure . ' лет' : ''),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user