Разработка доски объявлений на 1С-Битрикс
Доска объявлений — сложнее, чем кажется на первый взгляд. Это не просто «список товаров». Пользователи сами размещают объявления, модераторы проверяют контент, нужны поиск с фильтрами по многим параметрам, управление статусами и сроками объявлений, платное продвижение, личный кабинет продавца. Битрикс даёт хорошую базу — инфоблоки, модуль пользователей, фасетный поиск — но конкретную логику доски нужно строить поверх.
Структура инфоблока объявлений
Инфоблок — основное хранилище. Символьный код: classifieds. Тип: ADS.
Обязательные свойства инфоблока:
| Свойство | Код | Тип |
|---|---|---|
| Цена | PRICE |
Число |
| Тип сделки | DEAL_TYPE |
Список (Продам/Куплю/Обменяю/Отдам) |
| Город | CITY |
Справочник (HL-блок) |
| Телефон продавца | PHONE |
Строка |
| Статус | AD_STATUS |
Список (Активно/Модерация/Отклонено/Истекло) |
| Дата истечения | EXPIRE_DATE |
Дата |
| Просмотры | VIEW_COUNT |
Число |
| VIP | IS_VIP |
Флаг |
| Пользователь | USER_ID |
Число (FK на b_user) |
| Фотографии | PHOTOS |
Файл (множественное) |
Раздел инфоблока — категория объявлений (Транспорт, Недвижимость, Электроника и т.д.). Иерархия разделов — дерево через b_iblock_section.
Размещение объявления пользователем
Форма добавления объявления — страница в /ads/add/. Ключевой момент: пользователь загружает контент, значит нужна защита от спама и обязательная модерация.
// /local/components/local/ads.add/class.php
namespace Local\Ads;
class AdsAddComponent extends \CBitrixComponent
{
public function executeComponent(): void
{
if (!$GLOBALS['USER']->IsAuthorized()) {
LocalRedirect('/auth/?backurl=/ads/add/');
return;
}
if ($this->request->isPost() && check_bitrix_sessid()) {
$this->addAd();
}
$this->includeComponentTemplate();
}
private function addAd(): void
{
$el = new \CIBlockElement();
// Обработка загруженных фотографий
$photos = [];
if (!empty($_FILES['PHOTOS']['tmp_name'])) {
foreach ($_FILES['PHOTOS']['tmp_name'] as $i => $tmpName) {
if (is_uploaded_file($tmpName)) {
$photos[] = [
'name' => $_FILES['PHOTOS']['name'][$i],
'size' => $_FILES['PHOTOS']['size'][$i],
'tmp_name' => $tmpName,
'type' => $_FILES['PHOTOS']['type'][$i],
];
}
}
}
$adId = $el->Add([
'IBLOCK_ID' => CLASSIFIEDS_IBLOCK_ID,
'NAME' => htmlspecialchars($this->request->getPost('title')),
'DETAIL_TEXT' => htmlspecialchars($this->request->getPost('description')),
'IBLOCK_SECTION_ID' => (int)$this->request->getPost('category_id'),
'ACTIVE' => 'N', // Изначально неактивно, до модерации
'PROPERTY_VALUES' => [
'PRICE' => (float)$this->request->getPost('price'),
'DEAL_TYPE' => $this->request->getPost('deal_type'),
'PHONE' => htmlspecialchars($this->request->getPost('phone')),
'USER_ID' => $GLOBALS['USER']->GetID(),
'AD_STATUS' => 'MODERATION',
'EXPIRE_DATE' => date('d.m.Y', strtotime('+30 days')),
'VIEW_COUNT' => 0,
'PHOTOS' => $photos,
],
]);
if ($adId) {
$this->arResult['SUCCESS'] = true;
$this->arResult['AD_ID'] = $adId;
// Уведомить модераторов
$this->notifyModerators($adId);
} else {
$this->arResult['ERROR'] = $el->LAST_ERROR;
}
}
}
Модерация
Страница модератора — стандартный список элементов инфоблока с фильтром по AD_STATUS = MODERATION. Действия модератора меняют статус и активность:
// Одобрить объявление
$el = new \CIBlockElement();
$el->Update($adId, ['ACTIVE' => 'Y']);
\CIBlockElement::SetPropertyValues($adId, CLASSIFIEDS_IBLOCK_ID, 'ACTIVE', 'AD_STATUS');
// Отклонить с причиной
\CIBlockElement::SetPropertyValues($adId, CLASSIFIEDS_IBLOCK_ID, [
'AD_STATUS' => 'REJECTED',
'REJECT_REASON' => $reason,
]);
$el->Update($adId, ['ACTIVE' => 'N']);
// Уведомить пользователя
$event = new \Bitrix\Main\Mail\Event([
'EVENT_NAME' => 'AD_MODERATION_RESULT',
'LID' => SITE_ID,
'C_FIELDS' => [
'AD_ID' => $adId,
'STATUS' => $status,
'REASON' => $reason,
],
]);
$event->send();
Поиск и фильтрация
Фильтр объявлений — критическая часть UX. Для простого поиска — стандартный CIBlockElement::GetList() с фильтром. Для нагруженного проекта — фасетный поиск Битрикс (модуль search) или интеграция с ElasticSearch.
Пример фильтра с диапазоном цен:
$filter = [
'IBLOCK_ID' => CLASSIFIEDS_IBLOCK_ID,
'ACTIVE' => 'Y',
'SECTION_ID' => $categoryId,
'>PROPERTY_PRICE' => $priceMin,
'<PROPERTY_PRICE' => $priceMax,
'PROPERTY_CITY' => $cityId,
'PROPERTY_AD_STATUS' => 'ACTIVE',
];
$sort = ['PROPERTY_IS_VIP' => 'DESC', 'DATE_ACTIVE_FROM' => 'DESC'];
VIP-объявления всегда наверху через сортировку по флагу IS_VIP DESC.
Истечение срока объявлений
Агент проверяет просроченные объявления:
// Регистрация агента в /local/php_interface/init.php
\CAgent::AddAgent(
'Local\\Ads\\ExpireAgent::run();',
'local.ads',
'N',
86400, // Раз в сутки
);
// Метод агента
class ExpireAgent
{
public static function run(): string
{
$today = date('d.m.Y');
$result = \CIBlockElement::GetList(
[],
[
'IBLOCK_ID' => CLASSIFIEDS_IBLOCK_ID,
'ACTIVE' => 'Y',
'<PROPERTY_EXPIRE_DATE' => $today,
],
false,
false,
['ID', 'PROPERTY_USER_ID']
);
while ($ad = $result->Fetch()) {
$el = new \CIBlockElement();
$el->Update($ad['ID'], ['ACTIVE' => 'N']);
\CIBlockElement::SetPropertyValues($ad['ID'], CLASSIFIEDS_IBLOCK_ID, 'EXPIRED', 'AD_STATUS');
// Уведомить пользователя
}
return 'Local\\Ads\\ExpireAgent::run();';
}
}
Личный кабинет пользователя
Страница /personal/ads/ — список объявлений текущего пользователя:
$myAds = \CIBlockElement::GetList(
['DATE_CREATE' => 'DESC'],
[
'IBLOCK_ID' => CLASSIFIEDS_IBLOCK_ID,
'PROPERTY_USER_ID' => $GLOBALS['USER']->GetID(),
],
false,
['nPageSize' => 20],
['ID', 'NAME', 'ACTIVE', 'PROPERTY_AD_STATUS', 'PROPERTY_EXPIRE_DATE', 'PROPERTY_VIEW_COUNT']
);
Действия пользователя: редактировать, деактивировать, продлить срок (если объявление истекло), удалить.
Счётчик просмотров
При каждом открытии детальной страницы — инкрементировать счётчик. Через AJAX, чтобы не замедлять начальный рендер и не считать ботов:
// /local/ajax/ad_view.php
$adId = (int)$_POST['ad_id'];
if ($adId > 0) {
$current = (int)\CIBlockElement::GetProperty(CLASSIFIEDS_IBLOCK_ID, $adId, [], ['CODE' => 'VIEW_COUNT'])->GetNext()['VALUE'];
\CIBlockElement::SetPropertyValues($adId, CLASSIFIEDS_IBLOCK_ID, $current + 1, 'VIEW_COUNT');
}
Сроки разработки
| Вариант | Состав | Срок |
|---|---|---|
| Базовая доска | Инфоблок, размещение, список, фильтр | 8–12 дней |
| С модерацией и ЛК | + Модерация, личный кабинет, агент истечения | 12–18 дней |
| Полнофункциональная | + VIP-объявления, поиск, уведомления, статистика | 20–30 дней |







