Компонент розкладу лікарів на 1С-Бітрікс: кешування та інтеграція

Сторінка лікаря з текстом «Записатися за телефоном» — це втрачений онлайн-трафік. Користувач хоче бачити конкретні доступні дні та години, а не телефонувати до реєстратури. Відображення розкладу — окреме завдання від онлайн-запису: розклад має бути наочним, швидким і актуальним, навіть якщо кнопка «
Послуги, які ми пропонуємо
Показано 1 з 1Усі 1626 послуг
Компонент розкладу лікарів на 1С-Бітрікс: кешування та інтеграція
Простий
~1 день

Наші компетенції:

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1454
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1018
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    760
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    879
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    803
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    1162

Сторінка лікаря з текстом «Записатися за телефоном» — це втрачений онлайн-трафік. Користувач хоче бачити конкретні доступні дні та години, а не телефонувати до реєстратури. Відображення розкладу — окреме завдання від онлайн-запису: розклад має бути наочним, швидким і актуальним, навіть якщо кнопка «Записатися» веде до дзвінка. Ми проєктуємо архітектуру так, щоб дані підвантажувалися з МІС або HL-блоків, кешувалися з автоінвалідацією та віддавалися в адаптивному UI. У цій статті — наш підхід з реальним кодом та кейсами для клінік із 50+ лікарями.

Чому готові рішення не підходять?

Більшість готових модулів Маркетплейсу або показують порожню таблицю, або вимагають ручного введення слотів. Вони не інтегруються з внутрішньою МІС, не враховують складні правила прийому (повторювані шаблони, відгули). У результаті — або застарілі дані, або довге завантаження сторінки до 10 секунд.

Які проблеми вирішуємо?

  1. Консистентність даних. Розклад має синхронізуватися з МІС (1С:Медицина, МедМікс, iMed) через REST або SQL. Якщо в МІС змінили графік, на сайті зміни з'являються в межах TTL кешу (2–5 хвилин), а не через добу.
  2. Продуктивність. Сторінка списку лікарів (50+ спеціалістів) не повинна завантажуватися 10 секунд. Використовуємо один SQL-запит для найближчої дати, кешуємо результат теговано. При AJAX-навігації — фонове оновлення через \Bitrix\Main\Data\Cache.
  3. Мобільна адаптація. Слайдер дат на мобільних — не просто горизонтальний скрол, а компонент з touch-подіями. Для десктопа — тижнева сітка із зеленими комірками.

Як ми реалізуємо компонент розкладу

Стек: PHP 8.1, Bitrix ORM, HL-блоки (local_doctor_slots), тегований кеш. Компонент local:doctor.schedule з параметрами DOCTOR_ID, WEEKS_AHEAD, VIEW_TYPE. Алгоритм завантаження слотів: кожен слот — запис HL-блока з полями DOCTOR_ID, SLOT_DATE, SLOT_TIME, STATUS, PATIENT_ID (якщо зайнятий), SOURCE (manual/api). HL-блоки проходять фільтрацію в 5–10 разів швидше за інфоблоки на великих вибірках (50k+ записів).

Для вибору шаблону відображення орієнтуємося на тип лікаря. При щільному розкладі (терапевти) зручна тижнева сітка з відображенням усіх слотів. Для вузьких спеціалістів (хірург, невролог) компактніший список найближчих дат — він не показує пусті комірки та фокусується на доступних вікнах. На мобільних пристроях застосовуємо слайдер дат із touch-подіями — це інтуїтивно, але дає більше JS-навантаження.

Компонент розкладу

/local/components/local/doctor.schedule/class.php:

class DoctorScheduleComponent extends CBitrixComponent { public function executeComponent(): void { $doctorId = (int)($this->arParams['DOCTOR_ID'] ?? 0); $weeksAhead = (int)($this->arParams['WEEKS_AHEAD'] ?? 2); if (!$doctorId) { $this->arResult = ['ERROR' => 'Doctor not specified']; $this->includeComponentTemplate(); return; } $dateFrom = new \DateTime(); $dateTo = (clone $dateFrom)->modify("+{$weeksAhead} weeks"); $slots = $this->loadSlots($doctorId, $dateFrom, $dateTo); $scheduleByDate = []; foreach ($slots as $slot) { $date = $slot['SLOT_DATE']; if (!isset($scheduleByDate[$date])) { $scheduleByDate[$date] = [ 'date' => $date, 'day_name' => $this->getDayName(new \DateTime($date)), 'free_count' => 0, 'slots' => [], ]; } $scheduleByDate[$date]['slots'][] = $slot; if ($slot['STATUS'] === 'free') { $scheduleByDate[$date]['free_count']++; } } $nextFreeSlot = $this->getNextFreeSlot($slots); $this->arResult = [ 'DOCTOR_ID' => $doctorId, 'SCHEDULE' => $scheduleByDate, 'NEXT_FREE_SLOT' => $nextFreeSlot, 'DATE_FROM' => $dateFrom->format('Y-m-d'), 'DATE_TO' => $dateTo->format('Y-m-d'), ]; $this->setResultCacheKeys(['SCHEDULE', 'NEXT_FREE_SLOT']); $this->includeComponentTemplate(); } private function loadSlots(int $doctorId, \DateTime $from, \DateTime $to): array { return LocalDoctorSlotsTable::getList([ 'filter' => [ 'DOCTOR_ID' => $doctorId, '>=SLOT_DATE' => $from->format('Y-m-d'), '<=SLOT_DATE' => $to->format('Y-m-d'), ], 'order' => ['SLOT_DATE' => 'ASC', 'SLOT_TIME' => 'ASC'], 'select' => ['ID', 'SLOT_DATE', 'SLOT_TIME', 'STATUS'], ])->fetchAll(); } } 

Кешування

Розклад — дані, які змінюються при новому записі. Кешуємо з автоінвалідацією:

$this->arParams['CACHE_TYPE'] = 'A'; $this->arParams['CACHE_TIME'] = 120; // При створенні запису скидаємо кеш компонента для лікаря \CBitrixComponent::clearComponentCache('local:doctor.schedule', '', ['DOCTOR_ID' => $doctorId]); 

Для AJAX-запитів при перемиканні тижня використовуємо окремий кеш через \Bitrix\Main\Data\Cache.

Шаблон: тижнева сітка

templates/.default/template.php:

$today = new \DateTime(); $daysOfWeek = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Нд']; ?> <div class="doctor-schedule" data-doctor-id="<?= $arResult['DOCTOR_ID'] ?>"> <div class="schedule-nav"> <button class="schedule-prev" data-offset="-7">← Попередній тиждень</button> <button class="schedule-next" data-offset="7">Наступний тиждень →</button> </div> <div class="schedule-grid"> <?php foreach ($arResult['SCHEDULE'] as $dateStr => $dayData): ?> <?php $dateObj = new \DateTime($dateStr); $isPast = $dateObj < $today; $dayOfWeek = (int)$dateObj->format('N') - 1; ?> <div class="schedule-day <?= $isPast ? 'past' : '' ?> <?= $dayData['free_count'] > 0 ? 'has-slots' : 'no-slots' ?>"> <div class="day-header"> <span class="day-name"><?= $daysOfWeek[$dayOfWeek] ?></span> <span class="day-date"><?= $dateObj->format('d.m') ?></span> </div> <?php if ($dayData['free_count'] > 0): ?> <div class="slots-container"> <?php foreach ($dayData['slots'] as $slot): ?> <?php if ($slot['STATUS'] === 'free'): ?> <button class="slot-btn free" data-slot-id="<?= $slot['ID'] ?>" data-time="<?= substr($slot['SLOT_TIME'], 0, 5) ?>"> <?= substr($slot['SLOT_TIME'], 0, 5) ?> </button> <?php endif; ?> <?php endforeach; ?> </div> <div class="day-free-count"><?= $dayData['free_count'] ?> місця</div> <?php else: ?> <div class="no-slots-label">Немає запису</div> <?php endif; ?> </div> <?php endforeach; ?> </div> <?php if ($arResult['NEXT_FREE_SLOT']): ?> <div class="next-available"> Найближчий вільний запис: <strong><?= date('d.m.Y', strtotime($arResult['NEXT_FREE_SLOT']['SLOT_DATE'])) ?></strong> о <strong><?= substr($arResult['NEXT_FREE_SLOT']['SLOT_TIME'], 0, 5) ?></strong> </div> <?php endif; ?> </div> 

AJAX-підвантаження при перемиканні тижня

document.querySelectorAll('.schedule-prev, .schedule-next').forEach(btn => { btn.addEventListener('click', async function() { const doctorId = document.querySelector('.doctor-schedule').dataset.doctorId; const offset = parseInt(this.dataset.offset); const dateFrom = new Date(currentDateFrom); dateFrom.setDate(dateFrom.getDate() + offset); const res = await fetch('/local/ajax/doctor-schedule.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ doctor_id: doctorId, date_from: dateFrom.toISOString().split('T')[0], sessid: BX.bitrix_sessid() }) }); const data = await res.json(); renderScheduleGrid(data.schedule); currentDateFrom = dateFrom; }); }); 

Відображення розкладу на сторінці списку лікарів

На сторінці каталогу лікарів повний розклад не потрібен — достатньо індикатора «Найближчий запис: завтра». Це один SQL-запит по всіх лікарях:

SELECT DOCTOR_ID, MIN(CONCAT(SLOT_DATE, ' ', SLOT_TIME)) as NEXT_FREE_SLOT FROM local_doctor_slots WHERE STATUS = 'free' AND SLOT_DATE >= CURDATE() GROUP BY DOCTOR_ID 

Як інтегрувати розклад із МІС?

Інтеграція з МІС — ключовий етап. Якщо МІС надає REST API, налаштовуємо агент із періодичністю 1–5 хвилин. При прямому SQL-доступі — створюємо матеріалізоване представлення або тригери. У будь-якому разі, після синхронізації скидаємо тегований кеш відповідного лікаря.

Що входить у роботу?

  • Проєктування схеми HL-блоків та системи кешування.
  • Розробка компонента local:doctor.schedule з двома шаблонами (тижнева сітка та список).
  • Налаштування AJAX-підвантаження та мобільного слайдера.
  • Інтеграція з МІС (REST/SQL) та агент синхронізації.
  • Тестування на реальних даних (від 50 лікарів) та навантажувальне тестування.
  • Документація з експлуатації та адміністрування.

Порівняння шаблонів відображення

Параметр Тижнева сітка Список дат
Підходить для Щільний розклад (терапевти) Рідкі слоти (хірурги)
Інформативність Показує всі зайняті та вільні слоти Фокусується на доступних датах
Мобільна адаптація Слайдер дат із touch-подіями Вертикальний список
Швидкість завантаження Вимагає більше даних (усі слоти) Менше даних (тільки дати)

Продуктивність: HL-блоки vs Інфоблоки

Параметр HL-блоки Інфоблоки
Час запиту 50k записів ~150 мс ~800 мс
Гнучкість індексів Індекси на будь-які поля Тільки стандартні
Складність інтеграції Проста ORM Вимагає метаполів
Підходить для Табличних даних (слоти) Контентних даних (новини)

Згідно з документацією Бітрікс, HL-блоки оптимізовані для табличних даних і виконують вибірку в 5–10 разів швидше за інфоблоки.

Типові помилки при реалізації

  • Зберігати розклад в інфоблоці (повільна фільтрація). Використовуємо HL-блоки — ORM працює швидше, індекси ставити простіше.
  • Не інвалідувати кеш при записі через адмінку або API. Використовуємо події OnAfterAdd/Update/Delete HL-блока.
  • Ігнорувати часові пояси. Лікарі можуть працювати в різних філіалах — зберігаємо час в UTC, перетворюємо на клієнті.

Як почати?

Ми реалізували 20+ проєктів для клінік з гарантією продуктивності. Зв'яжіться — обговоримо ваш проєкт і покажемо демо з вашими даними. Отримайте консультацію безплатно.