{ "index": 358, "slug": "editorial-2018-01-field-bitrix-elements", "title": "Почему элемент Bitrix есть в админке, но пропадает из каталога", "excerpt": "ID после CIBlockElement::Add подтверждает запись, но не публичную видимость. Разбираем фильтры инфоблока, товарный слой, права и кеш по проверяемой цепочке.", "contentHtml": "

Скрипт импорта сообщает об успехе: CIBlockElement::Add вернул ID. В админке элемент открывается. В каталоге его нет. Пользователь получает пустой список или старую карточку, а команда начинает очищать весь кеш. Цена ошибки — повторная загрузка того же товара, дубли, потерянное время и отсутствие доказательства, на каком условии запись исчезла.

\n

Тезис: успешная запись и публичная видимость — разные факты. Сначала подтвердите, что Bitrix сохранил элемент. Затем повторите условия публичной выборки. После этого проверьте товарный слой, права, маршрут и кеш. Если запись не проходит более ранний фильтр, поздние проверки не объяснят симптом.

\n

Поворот в расследовании: что показывает админка

\n

На учебной копии каталога разработчик повторяет импорт и видит тот же симптом: после вызова Add в журнале есть ID, но гостевой список пуст. Сначала он открывает запись в админке, затем сверяет поля по ID и только после этого сравнивает публичный фильтр. Такой порядок отделяет сохранённый элемент от результата страницы.

\n

Админский список может показать неактивный элемент, запись с прошедшей датой публикации или элемент из другого раздела. Публичный компонент обычно добавляет ACTIVE, ACTIVE_DATE, раздел, свойства, доступность и собственные проектные условия. Поэтому фраза «элемент есть в админке» означает только, что одна административная выборка его нашла.

\n

У записи есть несколько границ. CIBlockElement::Add создаёт элемент инфоблока. Свойства сохраняются рядом с ним, но их наличие не означает, что товар готов к покупке. Цена, остаток, тип товара и торговые предложения относятся к каталожному слою. Компонент каталога может затем отфильтровать результат ещё раз. Кеш влияет только после того, как данные уже проходят действующие условия.

\n
\"Цепочка
Проверяйте границы слева направо. Каждая следующая проверка имеет смысл только после подтверждения предыдущей.
\n

Сначала зафиксируйте результат Add

\n

Не ограничивайтесь ID. При ошибке Add возвращает ложное значение, а причина доступна через LAST_ERROR. Сохраните внешний идентификатор импорта, ID инфоблока, название операции и результат. Если код продолжает выполнение после неуспешной записи, последующий запрос может проверять старый или пустой ID и увести расследование в сторону.

\n

Учебный фрагмент ниже показывает только форму проверки. Число инфоблока и идентификатор элемента условны. Код не доказывает, что конкретный проект использует именно такую схему импорта.

\n
<?php\n\nconst PRODUCT_IBLOCK_ID = 12;\n\n$fields = [\n    'IBLOCK_ID' => PRODUCT_IBLOCK_ID,\n    'NAME' => 'Учебный элемент',\n    'ACTIVE' => 'Y',\n];\n\n$element = new CIBlockElement;\n$elementId = $element->Add($fields);\n\nif (!$elementId) {\n    throw new RuntimeException(\n        'Элемент не создан: ' . $element->LAST_ERROR\n    );\n}\n\n$select = ['ID', 'IBLOCK_ID', 'NAME', 'CODE', 'ACTIVE',\n    'DATE_ACTIVE_FROM', 'DATE_ACTIVE_TO'];\n$stored = CIBlockElement::GetList(\n    [],\n    [\n        'IBLOCK_ID' => PRODUCT_IBLOCK_ID,\n        '=ID' => (int) $elementId,\n    ],\n    false,\n    ['nTopCount' => 1],\n    $select\n)->Fetch();\n\nif ($stored === false) {\n    throw new RuntimeException('Сохранённый элемент не читается');\n}\n\n$public = CIBlockElement::GetList(\n    [],\n    [\n        'IBLOCK_ID' => PRODUCT_IBLOCK_ID,\n        '=ID' => (int) $elementId,\n        'ACTIVE' => 'Y',\n        'ACTIVE_DATE' => 'Y',\n    ],\n    false,\n    ['nTopCount' => 1],\n    $select\n)->Fetch();\n\nif ($public === false) {\n    echo 'Элемент сохранён, но не проходит базовый публичный фильтр';\n}
\n

В реальном вызове в первый аргумент Add передают массив полей и, при необходимости, свойств. В этом учебном примере указаны минимальные поля, а свойства проекта и товарные параметры намеренно не добавлены. Так видна граница между результатом записи и контрольным чтением: сохранённый ID ещё не отвечает на вопрос, попадёт ли элемент в каталог.

\n

Повторите базовый публичный фильтр

\n

CIBlockElement::GetList возвращает элементы по переданному фильтру. Для минимальной пробы используйте тот же инфоблок, ID, ACTIVE => Y и ACTIVE_DATE => Y, которые применяет публичная выдача. Выбирайте только нужные поля. Если контрольный запрос с этими условиями пуст, ищите проблему в полях элемента, датах, инфоблоке или разделах, а не в шаблоне.

\n

Если базовая выборка возвращает строку, это ещё не доказательство видимости карточки. Компонент может требовать конкретное свойство, принадлежность к разделу, заполненный символьный код, разрешение пользователя или собственный флаг публикации. Скопируйте фильтр компонента в диагностический запрос по частям. Не заменяйте его догадкой «наверняка нужен кеш».

\n

Симптом → причина → проверка → действие

\n
Матрица диагностики элемента и каталога
СимптомПричинаПроверкаДействие
ID не полученОшибка обязательного поля, свойства или правПроверить результат Add и LAST_ERRORИсправить входные данные; не проверять пустой ID в каталоге
ID есть, базовый GetList пустACTIVE, даты, инфоблок или ID не совпадаютСчитать элемент без публичных условий и сравнить поляИсправить запись или фильтр импорта
Базовый запрос есть, карточки нетРаздел, свойство, права или фильтр компонентаСравнить реальный фильтр компонента с контрольнымУстранить первое несовпадающее условие
Карточка есть, купить нельзяНет цены, остатка, связи SKU или доступностиПроверить товарную модель и параметры каталога отдельноЗаполнить товарный слой или изменить требование компонента
После исправления видна старая версияКеш, индекс или другой источник данныхСначала повторить чтение в обход кеша и сравнить ключТочечно обновить нужный слой после подтверждения данных
В админке виден, у гостя нетПрава или условие показа для текущего пользователяПовторить запрос с теми же правами, что у посетителяИсправить доступ или явно принять ограниченную видимость
\n

Инфоблок не равен товару

\n

В Bitrix элемент инфоблока хранит общие данные: имя, код, активность, раздел и свойства. Для каталога этого может быть мало. Простой товар, товар с торговыми предложениями и другие типы используют разные связи и состояния. Публичный код может отбрасывать элемент без корректной цены, доступного предложения или нужного остатка.

\n

Проверяйте товарный слой отдельным запросом и в терминах версии проекта. Старый код может использовать CCatalogProduct, а новый — классы пространства \\Bitrix\\Catalog\\Product. Нельзя смешивать исправление видимости инфоблока с миграцией API: сначала установите, какой слой не проходит условие, затем выбирайте совместимый способ изменения.

\n

Отрицательный путь важен. Если элемент не должен продаваться, не добавляйте цену и остаток только ради того, чтобы карточка появилась. В таком случае видимость и доступность — разные требования. Изменение товарных параметров может открыть покупку, скидки или уведомления, хотя исходная задача касалась только списка.

\n

Почему очистка кеша редко бывает первым действием

\n

Кеш может вернуть старую выборку, но он не исправит неактивный элемент, неверную дату или отсутствующую цену. Полная очистка также меняет поведение других страниц и создаёт лишнюю нагрузку. Если данные не проходят фильтр, после очистки результат останется пустым. Если данные корректны, нужен адресный способ инвалидирования с понятным ключом.

\n

Для проверки разделите два чтения: контрольное чтение из источника и чтение через публичный путь. Если источник уже не проходит фильтр, остановитесь на данных. Если источник проходит, а публичный ответ старый, зафиксируйте ключ кеша, время формирования и слой, который его обслуживает. Не делайте вывод по одному обновлению браузера.

\n

Порядок действий

\n
  1. Запишите внешний ID операции, ID инфоблока и входные данные, которые безопасно хранить в журнале.
  2. Проверьте результат CIBlockElement::Add; при ошибке остановитесь и выведите LAST_ERROR.
  3. Считайте элемент по ID без ограничений и подтвердите инфоблок, активность, даты, код, раздел и обязательные свойства.
  4. Повторите базовый публичный фильтр с ACTIVE и ACTIVE_DATE; запишите, на каком условии строка исчезает.
  5. Сравните фильтр конкретного компонента, права посетителя, раздел и проектные флаги.
  6. Если это товар, отдельно проверьте тип товара, цену, остаток, доступность и связь торговых предложений.
  7. Только после проверки источника сравните публичный ответ, кеш-ключ и индекс; обновляйте адресно.
  8. Повторите чтение тем же пользователем и по тому же маршруту, где наблюдался исходный симптом.
  9. Зафиксируйте либо исправленное условие, либо точную причину остановки. Не называйте задачу решённой по одному успешному ID.
\n

Ограничения

\n

Эта схема не заменяет знание конфигурации конкретного инфоблока и компонента. У проекта могут быть дополнительные фильтры, торговые предложения, складские остатки, права, поиск или внешний индекс. Один GetList не воспроизводит автоматически весь путь страницы.

\n

Учебный код использует условный ID 12 и не выполняет реальную запись. Не запускайте его в production без заполненных полей, проверки прав и понимания побочных эффектов. Не очищайте общий кеш и не повторяйте импорт, пока не установили идемпотентность операции. В журнале не сохраняйте токены и персональные данные.

\n

Если нет доступа к фильтру компонента, правам пользователя или товарному слою, вывод должен быть ограниченным: «элемент читается по ID, но публичная причина не установлена». Это полезнее, чем неподтверждённое обвинение кеша.

\n

Проверяемый критерий готовности

\n

Диагностика готова, когда другой инженер может пройти путь от внешнего ID до публичного запроса и увидеть результат каждой границы: запись создана, поля сохранены, базовый фильтр пройден, товарный слой соответствует типу, права совпадают, а кеш или индекс проверены последними. Для отрицательного сценария указано условие остановки и действие, которое не следует выполнять.

\n

Исправление можно считать подтверждённым только после повторного запроса по исходному публичному маршруту и с исходными правами. Он должен вернуть ожидаемый элемент, а журнал должен объяснять, какое условие изменилось. Если видимость подтверждена только в админке, задача ещё не закрыта.

\n

Проверяемые источники

\n" }