get_*(). * Повторные вызовы instance() возвращают тот же объект — без дублирования запросов. * * @package Dekart */ if ( ! defined( 'ABSPATH' ) ) { exit; } class Pksh_Data { private static ?Pksh_Data $instance = null; private int $post_id; /** @var array Кэш branch-данных (get_option с fallback) */ private array $branch = []; /** @var array Кэш page-specific meta */ private array $page = []; /** @var array|null Кэш complex полей */ private ?array $social = null; private array $programs = []; private ?array $compare = null; private ?array $teachers = null; private ?array $reviews = null; private ?array $faq = null; private ?array $gallery = null; private function __construct() { $this->post_id = get_the_ID(); } /** * Получить экземпляр (синглтон) */ public static function instance(): self { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } // ============================================================ // BRANCH / OPTION DATA (get_option -> theme_option -> post_meta) // ============================================================ public function get_address(): string { $cache_key = 'dekart_branch_address'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'address', $this->branch ) ) { $this->branch['address'] = get_option( 'branch_address' ) ?: carbon_get_theme_option( 'site_address' ) ?: carbon_get_post_meta( $this->post_id, 'contact_address' ) ?: ''; } wp_cache_set( $cache_key, $this->branch['address'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['address']; } public function get_coords(): string { $cache_key = 'dekart_branch_coords'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'coords', $this->branch ) ) { $this->branch['coords'] = get_option( 'branch_coords' ) ?: carbon_get_post_meta( $this->post_id, 'contact_location' ) ?: ''; } wp_cache_set( $cache_key, $this->branch['coords'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['coords']; } public function get_phone(): string { $cache_key = 'dekart_branch_phone'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'phone', $this->branch ) ) { $this->branch['phone'] = get_option( 'branch_phone' ) ?: carbon_get_theme_option( 'site_phone' ) ?: carbon_get_post_meta( $this->post_id, 'contact_tel' ) ?: ''; } wp_cache_set( $cache_key, $this->branch['phone'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['phone']; } public function get_phone_clean(): string { if ( ! array_key_exists( 'phone_clean', $this->branch ) ) { $this->branch['phone_clean'] = preg_replace( '/[^0-9+]/', '', $this->get_phone() ); } return $this->branch['phone_clean']; } public function get_email(): string { $cache_key = 'dekart_branch_email'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'email', $this->branch ) ) { $this->branch['email'] = get_option( 'branch_email' ) ?: carbon_get_theme_option( 'site_mail' ) ?: carbon_get_post_meta( $this->post_id, 'contact_mail' ) ?: ''; } wp_cache_set( $cache_key, $this->branch['email'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['email']; } public function get_name(): string { $cache_key = 'dekart_branch_name'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'name', $this->branch ) ) { $this->branch['name'] = get_option( 'branch_name' ) ?: carbon_get_theme_option( 'site_name' ) ?: 'Декарт'; } wp_cache_set( $cache_key, $this->branch['name'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['name']; } public function get_city_prep(): string { $cache_key = 'dekart_branch_city_prep'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'city_prep', $this->branch ) ) { $this->branch['city_prep'] = get_option( 'branch_city_prep', 'в Щёлково' ); } wp_cache_set( $cache_key, $this->branch['city_prep'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['city_prep']; } public function get_city_genitive(): string { $genitive = get_option( 'branch_city_genitive', '' ); if ( ! empty( $genitive ) ) { return $genitive; } $clean = $this->get_city_clean(); if ( str_ends_with( $clean, 'ово' ) || str_ends_with( $clean, 'ево' ) || str_ends_with( $clean, 'ино' ) || str_ends_with( $clean, 'ыно' ) ) { return mb_substr( $clean, 0, -1 ) . 'а'; } return $clean; } public function get_city_clean(): string { if ( ! array_key_exists( 'city_clean', $this->branch ) ) { $this->branch['city_clean'] = str_replace( array( 'в ', 'В ' ), '', $this->get_city_prep() ); } return $this->branch['city_clean']; } public function get_ext_review_count(): string { $cache_key = 'dekart_branch_ext_review_count'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'ext_review_count', $this->branch ) ) { $this->branch['ext_review_count'] = get_option( 'branch_ext_review_count', '247' ); } wp_cache_set( $cache_key, $this->branch['ext_review_count'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['ext_review_count']; } public function get_social_links(): array { $cache_key = 'dekart_branch_social'; $cached = wp_cache_get( $cache_key, 'dekart_branch' ); if ( false !== $cached ) { return $cached; } if ( ! array_key_exists( 'social', $this->branch ) ) { $raw = get_option( 'branch_social' ) ?: carbon_get_post_meta( $this->post_id, 'social_media' ) ?: []; $this->branch['social'] = is_array( $raw ) ? $raw : []; } wp_cache_set( $cache_key, $this->branch['social'], 'dekart_branch', DAY_IN_SECONDS ); return $this->branch['social']; } public function get_logistics_walk(): string { if ( ! array_key_exists( 'logistics_walk', $this->branch ) ) { $this->branch['logistics_walk'] = get_option( 'branch_logistics_walk', '5 минут пешком от ТЦ «Глобус»' ); } return $this->branch['logistics_walk']; } public function get_logistics_parking(): string { if ( ! array_key_exists( 'logistics_parking', $this->branch ) ) { $this->branch['logistics_parking'] = get_option( 'branch_logistics_parking', 'Есть бесплатная парковка у входа' ); } return $this->branch['logistics_parking']; } public function get_logistics_nearby(): string { if ( ! array_key_exists( 'logistics_nearby', $this->branch ) ) { $this->branch['logistics_nearby'] = get_option( 'branch_logistics_schools', 'Рядом со школами №9 и №12, детским садом №15' ); } return $this->branch['logistics_nearby']; } // ============================================================ // AUTHOR / DATES (E-E-A-T / GEO) // ============================================================ public function get_author_name(): string { if ( ! array_key_exists( 'author_name', $this->page ) ) { $this->page['author_name'] = carbon_get_post_meta( $this->post_id, 'pksh_author_name' ) ?: get_option( 'branch_og_site_name' ) ?: get_option( 'branch_name' ) ?: carbon_get_theme_option( 'site_name' ) ?: 'Детский центр «Декарт»'; } return $this->page['author_name']; } /** * Количество лет на рынке (из года основания). * Автоматически: текущий год − год основания. * Источник: CF schema_founded → Settings API branch_founded. * Если год основания не задан — 0. */ public function get_years_on_market(): int { if ( ! array_key_exists( 'years_market', $this->page ) ) { $founded = carbon_get_theme_option( 'schema_founded' ); if ( empty( $founded ) ) { $founded = get_option( 'branch_founded' ); } if ( ! empty( $founded ) && is_string( $founded ) && is_numeric( $founded ) ) { $this->page['years_market'] = (int) date( 'Y' ) - (int) $founded; } else { $this->page['years_market'] = 0; } } return $this->page['years_market']; } public function get_page_publish_date(): string { if ( ! array_key_exists( 'publish_date', $this->page ) ) { $post = get_post( $this->post_id ); $this->page['publish_date'] = $post ? get_the_date( 'c', $post ) : ''; } return $this->page['publish_date']; } public function get_page_modified_date(): string { if ( ! array_key_exists( 'modified_date', $this->page ) ) { $post = get_post( $this->post_id ); $this->page['modified_date'] = $post ? get_the_modified_date( 'c', $post ) : ''; } return $this->page['modified_date']; } // ============================================================ // HERO / BANNER // ============================================================ public function get_banner_image_id(): int { if ( ! array_key_exists( 'banner_image_id', $this->page ) ) { $this->page['banner_image_id'] = (int) carbon_get_post_meta( $this->post_id, 'banner_image' ); } return $this->page['banner_image_id']; } public function get_h1(): string { if ( ! array_key_exists( 'h1', $this->page ) ) { $this->page['h1'] = carbon_get_post_meta( $this->post_id, 'banner_h1' ) ?: 'Подготовка к школе'; } return $this->page['h1']; } public function get_subtitle(): string { if ( ! array_key_exists( 'subtitle', $this->page ) ) { $this->page['subtitle'] = carbon_get_post_meta( $this->post_id, 'banner_subtitle' ) ?: 'Первое занятие — бесплатно. Ребёнок научится читать, считать и писать в игровой форме. Группы до 10 человек.'; } return $this->page['subtitle']; } public function get_subtitle_footnote_link(): string { if ( ! array_key_exists( 'subtitle_footnote_link', $this->page ) ) { $this->page['subtitle_footnote_link'] = carbon_get_post_meta( $this->post_id, 'banner_subtitle_footnote_link' ) ?: ''; } return $this->page['subtitle_footnote_link']; } /** * Определение для AI-цитирования (GEO, первые 60 слов). * Заполняется в админке: вкладка "🖼️ Герой" -> поле "Определение для AI". */ public function get_hero_definition(): string { if ( ! array_key_exists( 'hero_definition', $this->page ) ) { $this->page['hero_definition'] = carbon_get_post_meta( $this->post_id, 'pksh_hero_definition' ) ?: ''; } return $this->page['hero_definition']; } // ============================================================ // SOCIAL COUNTERS (complex) // ============================================================ public function get_social_counters(): array { if ( null === $this->social ) { $raw = carbon_get_post_meta( $this->post_id, 'pksh_social_counters' ); $this->social = is_array( $raw ) ? $raw : []; } return $this->social; } public static function social_icons(): array { return array( 'icon-graduation', 'icon-calendar', 'icon-star-filled', 'icon-teacher' ); } // ============================================================ // PROGRAMS // ============================================================ public function get_base_program(): array { if ( ! array_key_exists( 'base', $this->programs ) ) { $this->programs['base'] = array( 'name' => carbon_get_post_meta( $this->post_id, 'pksh_prog_base_name' ) ?: 'Уверенный старт', 'subname' => carbon_get_post_meta( $this->post_id, 'pksh_prog_base_subname' ) ?: '2 раза в неделю — комфортный темп без перегрузки', 'schedule' => carbon_get_post_meta( $this->post_id, 'pksh_prog_base_schedule' ) ?: 'вт, чт — 18:00–19:30', 'price_sub' => carbon_get_post_meta( $this->post_id, 'pksh_prog_base_price_sub' ) ?: '7 500 ₽', 'price_single' => carbon_get_post_meta( $this->post_id, 'pksh_prog_base_price_single' ) ?: '1 000 ₽', 'price_razov' => carbon_get_post_meta( $this->post_id, 'pksh_prog_base_price_razov' ) ?: '', 'urgency' => carbon_get_post_meta( $this->post_id, 'pksh_prog_base_urgency' ) ?: 'Осталось 2 места в группе', ); } return $this->programs['base']; } public function get_premium_program(): array { if ( ! array_key_exists( 'prem', $this->programs ) ) { $this->programs['prem'] = array( 'name' => carbon_get_post_meta( $this->post_id, 'pksh_prog_prem_name' ) ?: 'Результат за 3 месяца', 'subname' => carbon_get_post_meta( $this->post_id, 'pksh_prog_prem_subname' ) ?: '3 раза в неделю — быстрый прогресс', 'schedule' => carbon_get_post_meta( $this->post_id, 'pksh_prog_prem_schedule' ) ?: 'пн, ср, пт — 16:30–18:00', 'price_sub' => carbon_get_post_meta( $this->post_id, 'pksh_prog_prem_price_sub' ) ?: '10 500 ₽', 'price_single' => carbon_get_post_meta( $this->post_id, 'pksh_prog_prem_price_single' ) ?: '1 500 ₽', 'price_razov' => carbon_get_post_meta( $this->post_id, 'pksh_prog_prem_price_razov' ) ?: '', 'urgency' => carbon_get_post_meta( $this->post_id, 'pksh_prog_prem_urgency' ) ?: 'Осталось 3 места в группе', ); } return $this->programs['prem']; } // ============================================================ // GEO ANSWER BLOCKS (self-contained, 134-167 слов) // ============================================================ public function get_geo_answer_programs(): string { if ( ! array_key_exists( 'geo_answer_programs', $this->page ) ) { $this->page['geo_answer_programs'] = carbon_get_post_meta( $this->post_id, 'pksh_geo_answer_programs' ) ?: ''; } return $this->page['geo_answer_programs']; } public function get_geo_answer_results(): string { if ( ! array_key_exists( 'geo_answer_results', $this->page ) ) { $this->page['geo_answer_results'] = carbon_get_post_meta( $this->post_id, 'pksh_geo_answer_results' ) ?: ''; } return $this->page['geo_answer_results']; } public function get_geo_answer_howitworks(): string { if ( ! array_key_exists( 'geo_answer_howitworks', $this->page ) ) { $this->page['geo_answer_howitworks'] = carbon_get_post_meta( $this->post_id, 'pksh_geo_answer_howitworks' ) ?: ''; } return $this->page['geo_answer_howitworks']; } public function get_geo_question_1(): string { if ( ! array_key_exists( 'geo_q1', $this->page ) ) { $this->page['geo_q1'] = carbon_get_post_meta( $this->post_id, 'pksh_geo_q1' ) ?: ''; } return $this->page['geo_q1']; } public function get_geo_question_2(): string { if ( ! array_key_exists( 'geo_q2', $this->page ) ) { $this->page['geo_q2'] = carbon_get_post_meta( $this->post_id, 'pksh_geo_q2' ) ?: ''; } return $this->page['geo_q2']; } public function get_geo_question_3(): string { if ( ! array_key_exists( 'geo_q3', $this->page ) ) { $this->page['geo_q3'] = carbon_get_post_meta( $this->post_id, 'pksh_geo_q3' ) ?: ''; } return $this->page['geo_q3']; } // ============================================================ // COMPARE TABLE (complex) // ============================================================ public function get_compare_rows(): array { if ( null === $this->compare ) { $raw = carbon_get_post_meta( $this->post_id, 'pksh_compare_rows' ); $this->compare = is_array( $raw ) ? $raw : []; } return $this->compare; } public function get_compare_footnote(): string { if ( ! array_key_exists( 'footnote', $this->page ) ) { $this->page['footnote'] = carbon_get_post_meta( $this->post_id, 'pksh_compare_footnote' ) ?: ''; } return $this->page['footnote']; } // ============================================================ // TEACHERS (complex) // ============================================================ public function get_teachers(): array { if ( null === $this->teachers ) { $raw = carbon_get_post_meta( $this->post_id, 'pksh_teachers' ); $this->teachers = is_array( $raw ) ? $raw : []; } return $this->teachers; } // ============================================================ // REVIEWS (complex) // ============================================================ public function get_reviews(): array { if ( null === $this->reviews ) { $raw = carbon_get_post_meta( $this->post_id, 'pksh_reviews_list' ); $this->reviews = is_array( $raw ) ? $raw : []; } return $this->reviews; } // ============================================================ // FAQ (complex) // ============================================================ public function get_faq_items(): array { if ( null === $this->faq ) { $raw = carbon_get_post_meta( $this->post_id, 'pksh_faq_items' ); $this->faq = is_array( $raw ) ? $raw : []; } return $this->faq; } // ============================================================ // GALLERY (complex) // ============================================================ public function get_gallery(): array { if ( null === $this->gallery ) { $raw = carbon_get_post_meta( $this->post_id, 'pksh_gallery' ); $this->gallery = is_array( $raw ) ? $raw : []; } return $this->gallery; } // ============================================================ // HONEST TIMER // ============================================================ public function get_honest_deadline(): int { if ( ! array_key_exists( 'deadline', $this->page ) ) { $this->page['deadline'] = function_exists( 'dekart_get_honest_deadline' ) ? (int) dekart_get_honest_deadline() : 0; } return $this->page['deadline']; } public static function render_honest_timer( string $label = 'Цены действуют до конца месяца:' ): void { $ts = self::instance()->get_honest_deadline(); if ( $ts <= 0 ) { return; } ?>