Розробка сайту нерухомості на 1С-Бітрікс
Сайт нерухомості — це каталог, де відвідувач не гортає товари, а шукає житло. Середній користувач проводить на порталі 8-12 хвилин, застосовує 4-6 фільтрів, перемикається між списком та картою, зберігає 3-5 об'єктів у вибране. Якщо фільтрація гальмує, карта підвантажується ривками, а картка об'єкта не відповідає на питання «скільки до метро» — відвідувач іде на OLX або DIM.RIA. Бітрікс дозволяє побудувати такий портал, але тільки з правильною архітектурою даних, закладеною до першого коміту.
Архітектура інфоблоків: як зберігати об'єкти нерухомості
Перше рішення — один інфоблок на всі типи чи окремі інфоблоки під квартири, будинки, комерцію. Залежить від кількості унікальних властивостей кожного типу.
Один інфоблок з розділами за типами — працює, коли властивості перетинаються на 70%+. Площа, ціна, адреса, координати, фото — спільні для всіх. Кількість кімнат — тільки для квартир, площа ділянки — тільки для будинків. Порожні властивості не створюють рядків у b_iblock_element_property (рядок не з'являється, якщо значення null). Плюс: єдиний bitrix:catalog.smart_filter по всіх типах, єдина видача, один шаблон списку.
Окремі інфоблоки — виправдано, коли комерційна нерухомість має 15 полів, яких немає у житловій (клас приміщення, тип оренди, висота стель, вантажний ліфт, потужність електрики). Змішувати їх в одному інфоблоці — засмічувати фільтр. Мінус: агрегований пошук потребує кастомний компонент з UNION-логікою або Elasticsearch.
Рекомендована структура для порталу на 10К+ об'єктів:
- Інфоблок «Житлова нерухомість» — розділи: Квартири, Будинки, Таунхауси
- Інфоблок «Комерційна нерухомість» — розділи: Офіси, Торгові, Склади
-
Highload-блок «Житлові комплекси» — довідник ЖК з прив'язкою через
PROPERTY_COMPLEX_ID - Highload-блок «Райони» — довідник з полігонами для пошуку на карті
- Highload-блок «Забудовники» — компанії з реквізитами та логотипами
Мінімум 30 полів на об'єкт. Ключові для фільтрації:
| Властивість | Тип | Індексація |
|---|---|---|
| PRICE | N (число) | Фасетний індекс |
| AREA | N | Фасетний індекс |
| ROOMS | L (список) | Фасетний індекс |
| FLOOR | N | Фасетний індекс |
| FLOORS_TOTAL | N | Звичайний |
| DISTRICT | S:Highload | Фасетний індекс |
| COMPLEX_ID | S:Highload | Фасетний індекс |
| COORDINATES | S (рядок, "lat,lng") | Немає |
| DEAL_TYPE | L (продаж/оренда) | Фасетний індекс |
Координати зберігаються як рядок "50.4501,30.5234" — парсяться на фронті для відображення на карті. Розділяти на два окремі поля (LATITUDE, LONGITUDE) немає сенсу — не існує сценарію, де потрібна фільтрація за однією координатою.
Фільтрація та карта: найскладніша частина проєкту
Штатний bitrix:catalog.smart_filter закриває приблизно 60% задач — чекбокси, списки, діапазони. Але для нерухомості потрібні речі, які він не вміє з коробки.
Повзунки діапазонів для ціни та площі. Smart filter віддає MIN_VALUE і MAX_VALUE для числових властивостей через $arResult['ITEMS']. На фронті будуємо два input + slider на базі noUiSlider або rc-slider. Значення передаються через GET-параметри ?arrFilter_P1_MIN=500000&arrFilter_P1_MAX=2000000. Проблема: кожна зміна повзунка — перезавантаження сторінки. Рішення — AJAX-підвантаження через bitrix:catalog.section з AJAX_MODE=Y і кастомний JavaScript, який перехоплює submit форми фільтра.
Пошук за областю на карті — малювання полігону. Користувач малює область на карті, система повертає об'єкти всередині цієї області. Smart filter не має поняття геопросторових запитів. Реалізація складається з чотирьох кроків.
Крок перший — фронтенд. Google Maps API (для українського ринку зручніший за Yandex.Maps), google.maps.drawing.DrawingManager з режимами polygon і circle. Користувач малює область, JavaScript збирає масив координат вершин.
Крок другий — AJAX-запит на бекенд з координатами полігону. На стороні PHP — алгоритм Ray Casting (point-in-polygon): для кожного об'єкта перевіряємо, чи потрапляють його координати всередину полігону. При 50К об'єктів повний перебір — 20-40ms, прийнятно.
Крок третій — просторовий індекс. При 200К+ об'єктів потрібна оптимізація. MySQL/MariaDB підтримують колонку POINT типу GEOMETRY з індексом SPATIAL. Запит через ST_Contains(polygon, point). Бітрікс не зберігає властивості інфоблоків як GEOMETRY, тому створюємо додаткову таблицю project_realty_geo з полями ELEMENT_ID, LOCATION POINT, SPATIAL INDEX. Синхронізація — через обробник події OnAfterIBlockElementUpdate.
// Синхронізація координат з геоіндексом
EventManager::getInstance()->addEventHandler(
'iblock', 'OnAfterIBlockElementUpdate',
function ($arFields) {
if ($arFields['IBLOCK_ID'] !== REALTY_IBLOCK_ID) return;
GeoIndexService::updatePoint(
$arFields['ID'],
$arFields['PROPERTY_VALUES']['COORDINATES']
);
}
);
Крок четвертий — результат AJAX-запиту (масив ELEMENT_ID) передається в основний CIBlockElement::GetList через фільтр ['ID' => $geoFilteredIds]. Просторова фільтрація комбінується з параметрами smart filter.
Кластеризація маркерів. При 5К+ маркерів Google Maps починає гальмувати. MarkerClusterer (бібліотека @googlemaps/markerclusterer) групує близькі маркери автоматично. Але при зумі на квартал з 200 квартирами в одному ЖК кластер розгортається в кашу. Рішення — кастомний кластер з підказкою «14 квартир у ЖК Сонячний» і посиланням на відфільтрований список.
Синхронне оновлення карти та списку. Користувач рухає повзунок ціни — оновлюється список — оновлюються маркери. Користувач рухає карту — список показує тільки об'єкти у видимій області. Потрібен єдиний контролер стану на фронті. Архітектура: JavaScript-контролер, який тримає поточні фільтри + видиму область карти (bounding box). Будь-яка зміна параметра — один AJAX-запит. Відповідь містить HTML списку та JSON координат для маркерів.
// Синхронізація карти та списку
function updateResults() {
const filters = collectFilters();
const bounds = map.getBounds();
filters.geo_bounds = {
ne: { lat: bounds.getNorthEast().lat(), lng: bounds.getNorthEast().lng() },
sw: { lat: bounds.getSouthWest().lat(), lng: bounds.getSouthWest().lng() }
};
fetch('/api/realty/search/', {
method: 'POST',
body: JSON.stringify(filters)
})
.then(r => r.json())
.then(data => {
renderList(data.html);
renderMarkers(data.markers);
});
}
map.addListener('idle', debounce(updateResults, 300));
filterForm.addEventListener('change', updateResults);
Картка об'єкта
Картка квартири — 30+ полів, фотогалерея, відеотур, 3D-панорама, карта з інфраструктурою, іпотечний калькулятор. Все через один виклик bitrix:news.detail з кастомним шаблоном.
Фотогалерея — множинна властивість типу «Файл». Рендер через Swiper.js з lazy-завантаженням. Прев'ю — CFile::ResizeImageGet() на 400x300. Повноекранний перегляд — оригінали до 1920px.
3D-панорама та відеотур. Панорама — iframe з Matterport, Kuula або власний viewer на Pannellum.js. Відеотур — YouTube embed. Зберігаються як рядкові властивості з URL. У шаблоні — умовний рендер: якщо заповнено PROPERTY_PANORAMA_URL, показуємо вкладку «3D-тур».
Іпотечний калькулятор — чистий JavaScript без серверних запитів. Формула ануїтетного платежу, три повзунки (вартість, перший внесок, термін), результат — щомісячний платіж. Ставки підтягуються з Highload-блоку «Банки-партнери» при завантаженні сторінки.
XML-фіди для агрегаторів
DIM.RIA, OLX, Flatfy — у кожного свій формат. Загальна логіка: агент Бітрікса (CAgent) запускається раз на годину, вибирає активні об'єкти, формує XML, кладе у /upload/feeds/.
Кожен фід — окремий клас-генератор, що наслідує абстрактний BaseFeedGenerator. Маппінг властивостей інфоблоку на поля XML — у конфігу, не в коді. Додати новий агрегатор можна без розробника.
Обсяг фіду: 10К об'єктів → XML ~15MB. Генерація — 30-60 секунд. При 50К+ об'єктів — виносимо у фонову задачу.
CRM-інтеграція та ріелтори
Заявка з картки об'єкта → лід у Бітрікс24. REST API: crm.lead.add з полями TITLE, SOURCE_ID, UF_CRM_REALTY_ID. Webhook або OAuth — залежить від кількості порталів.
Ріелтори — окремий інфоблок або Highload-блок. Кожен об'єкт прив'язаний до агента через PROPERTY_AGENT_ID. Сторінка агента — його об'єкти, контакти, рейтинг. Авторизований агент редагує свої об'єкти через bitrix:iblock.element.edit.form з обмеженням за CREATED_BY.
Вибране та порівняння
Вибране для неавторизованих — cookies або localStorage. Масив ID об'єктів, максимум 50. На сервері — middleware в init.php перевіряє cookie REALTY_FAVORITES і додає прапорець IN_FAVORITES до $arResult. Для авторизованих — Highload-блок UserFavorites з полями USER_ID, ELEMENT_ID, DATE_ADD.
Порівняння — аналогічно, але з таблицею властивостей у два-три стовпці. Компонент читає ID з cookies/Highload, робить GetList за масивом ID, рендерить таблицю з горизонтальним скролом.
SEO для тисяч сторінок
Ручне введення meta для кожної з 10К квартир — нереально. Шаблони SEO через налаштування інфоблоку:
-
#ELEMENT_NAME#— назва об'єкта - Район підставляється через обробник
OnBeforeIBlockElementSeo - Формула:
Купити {тип} {кімнати}-кімн. в {район} — {ціна} ₴ | {сайт}
Мікророзмітка Schema.org RealEstateListing — у template.php картки:
{
"@context": "https://schema.org",
"@type": "RealEstateListing",
"name": "2-кімн. квартира, 65 м², Шевченківський район",
"url": "https://site.ua/kvartiry/123/",
"datePosted": "2025-01-15",
"offers": {
"@type": "Offer",
"price": "2500000",
"priceCurrency": "UAH"
}
}
Canonical URL, hreflang для мультимовності, XML sitemap через модуль seo — до 50К URL на файл.
Продуктивність на 50К+ об'єктів
Фасетні індекси — обов'язково. Без них bitrix:catalog.smart_filter на 50К елементів з 15 фільтрованими властивостями — 3-5 секунд. З фасетним індексом — 50-150ms. Перебудова через Bitrix\Iblock\PropertyIndex\Manager::buildIndex($iblockId), запускається за кроном після масового оновлення.
Посторінкова навігація — bitrix:system.pagenavigation з lazy-scroll. LIMIT + OFFSET на великих вибірках деградує — при OFFSET 40000 MySQL все одно сканує 40К рядків. Альтернатива — курсорна пагінація за ID > $lastId, але штатні компоненти її не підтримують. Для перших 200 сторінок OFFSET прийнятний.
Етапи та терміни
- Аналітика, прототипування (1-2 тижні) — структура даних, карта фільтрів, прототипи Figma
- Дизайн (2-3 тижні) — UI картки, списку, карти, мобільна версія
- Розробка ядра (4-6 тижнів) — інфоблоки, фільтрація, карта, картка об'єкта
- Інтеграції (2-3 тижні) — XML-фіди, CRM, іпотечний калькулятор
- Тестування, оптимізація (1-2 тижні) — навантаження, SEO, кросбраузер
- Запуск (3-5 днів) — деплой, імпорт даних, моніторинг
| Масштаб | Терміни |
|---|---|
| Сайт агенції, до 500 об'єктів | 6-10 тижнів |
| Портал міста, 5-10К об'єктів, карта + фільтри | 10-16 тижнів |
| Національний портал, 50К+ об'єктів, фіди, CRM | 14-24 тижні |
Терміни не включають наповнення контентом та налаштування рекламних кампаній — це паралельні процеси, які стартують на етапі тестування.







