raise editorial quality gate and revise 2018 spring
Build and deploy / deploy (push) Successful in 15s
Build and deploy / deploy (push) Successful in 15s
This commit is contained in:
@@ -92,6 +92,7 @@ const practiceArticle = {
|
||||
figure('/assets/editorial/2018/bitrix-catalog-workflow.png', 'Разработчик проверяет путь от формы к карточке товара и фиксирует схему процесса', 'Не начинаем с большого импорта. Сначала рисуем путь данных и называем контрольные точки.'),
|
||||
heading('Сначала формулируем контракт операции'),
|
||||
paragraph('Перед вызовом API полезно выписать, какие поля обязательны именно для нашего инфоблока. Это выглядит занудно до первой ошибки импорта, а после неё экономит часы. Не нужно создавать универсальный валидатор Bitrix: достаточно проверить входные данные, зафиксировать системные значения и вернуть диагностируемую ошибку вызывающему коду.'),
|
||||
paragraph('Ещё один пункт контракта — повторный запуск. Импорт может оборваться после ответа базы или до записи в журнал. Поэтому внешний идентификатор должен позволять отличить новую операцию от повтора. В проекте это обычно означает отдельную проверку существующей записи по внешнему ключу и явное правило: обновляем черновик, пропускаем готовую запись или останавливаемся с конфликтом. Сам <code>Add</code> за это правило не отвечает.'),
|
||||
dataTable(
|
||||
['Участок', 'Что фиксируем', 'Чем доказываем'],
|
||||
[
|
||||
@@ -185,7 +186,7 @@ const mechanismArticle = {
|
||||
excerpt: 'Разбираем жизненный цикл добавления элемента: кто проверяет поля, где срабатывают события Bitrix, почему глобальный обработчик не заменяет сервис и как тестировать эту границу.',
|
||||
readingMinutes: 9,
|
||||
contentHtml: [
|
||||
paragraph('Когда Bitrix-проект разрастается, вокруг простого <code>CIBlockElement::Add</code> появляется невидимый код: обработчики событий, правила символьного кода, импортеры, каталог, поиск и шаблоны. Из-за этого одинаковый вызов сегодня работает из формы, а завтра падает из консольного скрипта. Давайте разложим путь записи по шагам и не будем прятать бизнес-правило в месте, где его трудно обнаружить.'),
|
||||
paragraph('Проблема появляется, когда Bitrix-проект разрастается вокруг простого <code>CIBlockElement::Add</code>: обработчики событий, правила символьного кода, импортеры, каталог, поиск и шаблоны становятся невидимой частью вызова. Из-за этого одинаковый код сегодня работает из формы, а завтра падает из консольного скрипта. Давайте разложим путь записи по шагам и не будем прятать бизнес-правило в месте, где его трудно обнаружить.'),
|
||||
heading('Карта жизненного цикла'),
|
||||
paragraph('Документация Bitrix говорит важную вещь: перед добавлением вызывается <code>OnBeforeIBlockElementAdd</code>. Обработчик получает поля по ссылке, поэтому способен их изменить; чтобы отменить запись, он должен установить исключение через <code>$APPLICATION->ThrowException()</code> и вернуть <code>false</code>. После успешной записи срабатывают события после добавления. Это значит, что обработчик — реальная часть контракта метода, а не декоративная «магия в init.php».'),
|
||||
figure('/assets/editorial/2018/bitrix-add-lifecycle.svg', 'Последовательность от формы до контрольной публичной выборки при создании элемента Bitrix', 'ID возвращается из слоя инфоблока, но качество результата подтверждается уже в пользовательском сценарии.'),
|
||||
@@ -256,6 +257,12 @@ const mechanismArticle = {
|
||||
heading('После записи — это уже другой разговор'),
|
||||
paragraph('Обработчик после добавления удобен для журналирования, запуска поиска или отправки внутреннего уведомления. Но он не должен молча решать судьбу уже созданного элемента. Если побочный шаг упал после того, как <code>Add</code> вернул ID, повторный вызов <code>Add</code> из обработчика легко создаст дубль, а внешний сервис получит два одинаковых запроса. Поэтому после записи я бы сохранял ID, внешний ключ и понятный статус операции, а ошибку реакции разбирал как отдельную задачу.'),
|
||||
paragraph('Если синхронизация с внешней системой действительно обязательна для публикации товара, полезно разделить два состояния: «элемент сохранён» и «элемент готов для пользователя». Первый факт подтверждает сервис создания, второй — контрольная проверка после всех зависимых действий. Тогда временный сбой индексации или уведомления не превращается в неясную историю, где никто не понимает, можно ли безопасно повторить импорт.'),
|
||||
heading('Три вопроса до запуска'),
|
||||
orderedList([
|
||||
'Какой инвариант действительно общий для всех способов создания элемента, а какой относится только к форме или импорту?',
|
||||
'Где вызывающий код получит причину отказа: в результате сервиса, в <code>LAST_ERROR</code> или в отдельном журнале операции?',
|
||||
'Как повторный запуск отличит новую запись от уже созданной и не превратит сбой обработчика в дубликат?',
|
||||
]),
|
||||
heading('Как тестировать такую связку'),
|
||||
paragraph('В 2018-м легко ограничиться ручной проверкой в админке, но здесь полезно хотя бы зафиксировать короткую матрицу. Она не требует сложного тестового фреймворка: часть сценариев можно выполнить на тестовом инфоблоке и сохранить как чек-лист релиза. Главное — проверять и прямой сервис, и поведение глобального события.'),
|
||||
dataTable(
|
||||
@@ -285,6 +292,7 @@ const fieldArticle = {
|
||||
paragraph('Знакомая картина: скрипт вернул ID, в админке новый товар есть, а на сайте его нет. Первый импульс — «почистить кеш». Иногда это действительно помогает, но чаще кеш просто оказывается первым подозреваемым, потому что его легко назвать. Давайте сначала отделим факт записи от публичной видимости и пройдём путь теми же условиями, которыми живёт каталог.'),
|
||||
heading('Постановка проблемы'),
|
||||
paragraph('Админка и публичный компонент редко показывают одинаковую выборку. Админка может отобразить неактивный элемент, а каталог фильтрует по <code>ACTIVE</code>, датам активности, разделу, правам, цене, наличию и проектным свойствам. Поэтому вопрос «почему элемент не виден?» нельзя решать одной командой. Нужен короткий список слоёв и доказательство на каждом.'),
|
||||
paragraph('Полезно сразу сохранить два разных наблюдения: «запись читается по ID без ограничений» и «запись попадает в публичную выборку». Между ними могут стоять несколько независимых условий. Если журнал хранит только успешный ID, а не фильтр и результат контрольного запроса, следующему разработчику останется лишь гадать, какая граница исключила товар.'),
|
||||
figure('/assets/editorial/2018/bitrix-visibility-diagnostic.svg', 'Дерево диагностики: от результата Add к условиям публичного каталога', 'Начинаем не с кеша, а с самого раннего условия, которое может исключить элемент из публичной выборки.'),
|
||||
heading('Проверяем по слоям'),
|
||||
dataTable(
|
||||
|
||||
Reference in New Issue
Block a user