среда, 10 апреля 2013 г.

О событиях, которые возникают при импорте каталога из 1С в Битрикс

В контексте интеграции 1С Предприятия и Битрикс не утихают споры по поводу того, какие события при этом отрабатывают, а какие не отрабатывают. А спорить, в общем-то, не о чем – благо, Битрикс поставляется нам в исходных кодах.
Класс импорта CIBlockCMLImport, который я так люблю наследовать, описан в файле 
\bitrix\modules\iblock\classes\general\cml2
Открыв этот файл, мы ясно видим, что элементы инфоблока товаров, как и элементы инфоблока предложений добавляются функцией CIBlockElement::Add
, а изменяются функцией CIBlockElement::Update

Исходные коды этих функций мы можем посмотреть в файлах (в одном описан класс CAllIBlockElement в другом – его наследник CIBlockElement)
 \bitrix\modules\iblock\classes\general\iblockelement.php
\bitrix\modules\iblock\classes\mysql\iblockelement.php

Что же мы видим там?
В функции Add имеется вот такая конструкция – перед самым сбросом управляемого кеша:

$events = GetModuleEvents("iblock", "OnAfterIBlockElementAdd");
while ($arEvent = $events->Fetch())
ExecuteModuleEventEx($arEvent, array(&$arFields));

То есть вызывается и отрабатывает событие OnAfterIBlockElementAdd Причем каждый из описанных обработчиков этого события. Как мы можем видеть, вызов этого события происходит всегда – безусловно, то есть каждый раз при отработке функции CIBlockElement::Add и не важно, вызвана ли она в классе импорта или где-то еще.

Кроме этого в этой же функции вызывается другое событие

if(!isset($arFields["WF_PARENT_ELEMENT_ID"]) && $arIBlock["FIELDS"]["LOG_ELEMENT_ADD"]["IS_REQUIRED"] == "Y")
{
    $USER_ID = is_object($USER)? intval($USER->GetID()) : 0;
    $db_events = GetModuleEvents("main", "OnBeforeEventLog");
    $arEvent = $db_events->Fetch();

}
Но не каждый раз, а по условию. Это не что иное, как событие при записи в журнал событий. И оно выполняется, если в настройках инфоблока указано журналировать добавление элемента.

А вот вызова события OnBeforeIBlockElementAdd я в функции Add не нашла (оно там есть, но вызвано не напрямую). Нет его и в классе импорта CIBlockCMLImport 

Посмотрим теперь, что у нас имеется в функции  CIBlockElement::Update

$events = GetModuleEvents("iblock", "OnAfterIBlockElementUpdate");
while ($arEvent = $events->Fetch())
ExecuteModuleEventEx($arEvent, array(&$arFields));

Вызов события OnAfterIBlockElementUpdate – имеется
Вызова события OnBeforeIBlockElementUpdate – не имеется

Зато вызовы событий OnBeforeIBlockElementUpdate и OnBeforeIBlockElementAdd присутствует в методе класса CAllIBlockElement CheckFields, который в свою очередь вызывается и в Add, и в Update Но функция CheckFields вызывается в них уже по условию.

Из вышесказанного я делаю следующие выводы: безусловно, при импорте каталога из 1С Предприятия в Битрикс отрабатывают обработчики событий OnAfterIBlockElementAdd и OnAfterIBlockElementUpdate, они отрабатывают уже после вставки/обновления элемента и, в принципе, могут быть использованы для модификации данных в инфоблоке, но ценой дополнительных запросов к базе. А дополнительные запросы к базе при импорте – это порой убийство импорта.

События OnBeforeIBlockElementUpdate и OnBeforeIBlockElementAdd так же могут отрабатывать при импорте при определенных условиях, однако уже по тому, каким образом они вставлены в исходный код битрикс, видно, что они задуманы не для модификации данных перед вставкой/обновлением, а для какой-то необычной кастомной проверки, возможно, какого-то поля необычного формата.

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

О событиях, возникающих при добавлении/обновлении раздела инфоблока, а так же при обновлении/добавлении цены товара я писать здесь не буду – нужно вклиниться в них – открываем исходники и смотрим, где они и как.

О событии
foreach(GetModuleEvents("catalog", "OnSuccessCatalogImport1C", true) as $arEvent)
ExecuteModuleEventEx($arEvent);
которое Битриксоиды вставили прямо на последний шаг работы своего стандартного компонента импорта из 1С, наверное, знают все. Но если вы используете его для кастомизации импорта … это плохо.

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

Я думаю, к интеграции 1С Предприятия и Битрикс нельзя подходить по принципу «если работает, то ничего не нужно менять». Нужно оптимизировать, оптимизировать и еще раз оптимизировать.

23 комментария:

Александра Плотникова комментирует...

Юлия, спасибо за такое подробное описание логики работы событий!
Работаю с битриксом с 2008 года, но с интернет-магазинами только недавно, особенно в плане 1С-интеграции.
Стоит задача выставлять флаг "Новинка" для товаров, количество которых возросло после выгрузки (и, соответственно, снимать с остальных).
Каким событием вы рекомендовали бы воспользоваться?
Скорее всего, подходит что-нибудь из Before?

(И вообще спасибо за ваш блог! Мне тоже "начали нравиться сапоги" - мучительно выбираю, из чего именно их сшить)

Юлия комментирует...

Спасибо, Александра!

Для решения задачи, которую вы описали, я не стала бы пользоваться событиями, а скопировала бы компонент импорта в собственное пространство имен, потом бы унаследовала класс импорта

class CIBlockCMLCustomImport extends CIBlockCMLImport
{

}

И переопределила бы функцию ImportElement.

И вот в ней бы уже сохраняла в переменную кол-во товара, к-е было , сравнивала бы его с количеством, к-е пришло из 1С и, если нужно - включила бы флаг новинки.

Александра Плотникова комментирует...

Юлия, в таком случае, как я понимаю, мы теряем возможность обновляться?
Мне всегда хочется минимизировать изменение стандартных компонентов битрикса.
В теме ООП я, честно признаться, пока плаваю. Так что с наследованием классов и переопределением событий испытываю затруднения. Возможно, порекомендуете что-то почитать на эту тему?
Прошерстила весь форум битрикса, пока ничего конкретного не нашла.

Юлия комментирует...

Нет, возможность обновлений потеряна не будет, если вы все скопируете в собственное пространство имен.

Наследование в php - это элементарное дело. Вот тут почитайте: http://www.php.net/manual/ru/language.oop5.inheritance.php

Юлия комментирует...

Просто на событиях - это реально будет намного сложнее.

Евгений комментирует...

Только толку от этих обновлений уже не будет.

Анонимный комментирует...

Юлия, спасибо за подробное объяснение. Подскажите, а каким образом можно присвоить номер пакета выгрузки всем товарам? И где его задавать. Я как раз думала вызвать обработчик события OnSuccessCatalogImport1C чтобы определить номер выгрузки. Но как присвоить этот номер всем попавшим в выгрузку позициям, не затирая старый номер пока не понятно.

Юлия комментирует...

Попробуйте хранить то, что вам нужно, - в сессии.

Яна комментирует...

Спасибо Юлия!
Я сейчас написала уже обработок и понимаю, что нужно переписывать.

Сейчас я вообще сделала на кроне и обхожу весь сайт и понимаю что при каждом обходе обновляется весь каталог и чиститься кеш, что очень сильно отражается на итак тормознутом сайте.
мой крон делает

1. Обновление поля Новинок по истечению 10 дней с момента публикации на сайт

2. Обновление поля Наличие скидки, чтобы выводить по фильтру наличии скидки список товаров

3. Считает минимальную цену у товара с учетом всех скидок, когда цена хранится только у торгового предложения. Использую для сортировки.

Сделано это с кроне, так как на события вешать не хоетлось это тормозит выгрузку из 1с.
Очень не нравится что на сайте такие огромные вычисления скидок и все так тормозит. У нас работающий демо магазин www.yatto.ru


Очень не хватает опыта чтобы оптимизировать и сделать сайт быстрым. Наш сайт www.yatto.ru



Сейчас нашла ваш форум в поиске ответов. И нам нужен консультант, помощник? старший брат, который собаку скушал на битриксе. Мы ищем. Вы оказываете такие услуги? в скайпе я motolana.

Юлия комментирует...

Яна, оказываю, но сейчас, правда, очень загружена. Мой логин в скайпе Bedrosova. Только я это - старшим братом быть не смогу - только если старшей сестрой. :))

Яна комментирует...

Спасибо, Юлия, за ответ!
Сейчас вот какая задача стоит.
У нас магазин одежды.
Все просто есть товар и торговые предложения (цвет, размер).
Но в выдаче нужно отображать не просто товар, а товар по цветам, то есть товаро-цвет. При чем в 1с кривые данные и часть товаров идет как отдельно товар с одним цветом в торговых предложениях, а часть товар и все цвеа в торговых предложениях.
Чтобы не ломать битрикс придумали такое решение хранения товаро-цвета.
Что есть товар, у карточке товара фотка с цветом1. у товара все торговые предложения по всем цветам.
И мы дублируем товары меняя фотку цвета у товара, а торговые предложения те же самые. То есть у торговых предложений связь будет не содним товаром, а с несколькими по количеству цветов. То есть мы решаем и вопрос отображения выдачи. У нас на выдаче товар во всех цветах и с карточкой, в карточке каждого товара можно переключиться на торговые предложения других цветов и с корзиной, в которое. мы кладем торговое предложение, оно то имеет тот же самый id не важно из какой карточки товара-цвета мы его добавили в карзину

Юлия комментирует...

Яна, в вашей ситуации я бы посоветовала нанять контент-менеджера, чтобы привести в порядок номенклатуру на стороне 1С. Это будет намного-намного дешевле, чем вставлять костыли, к-е все равно костылями останутся.

Яна комментирует...

Спасибо за ответ! Но задачи это не решит к сожалению отображения цветопозиций.

Менеджеры могут привести структуру или к виду в одном товаре один цвет, тогда на сайте мы не поулчим в карточке товара другие цвета и покупатель не будет уведомлен что выбираемая им майка есть в других цветах. на выдаче (catalog.section) тоже не гуд. Да, будет список всех товароцветов, но без возможности сменить цвет, так как же как и на карточке товара.

Если менеджеры приведут структуру к виду у товара хранятся все цвета в торговых предложениях, то на выдаче(catalog.section) мы получим только первый цвет с возможностью внутри переключаться по цветам.

Ни первое ни второе не решают вопроса отображения товароцветов. Странно что такая задача, и ее никто не решал.
Посмотрите магазин http://www.wildberries.ru/ тут идеально решена эта задача. На выдаче отображаются все цвета, но и внутри карточки можно переключаться по цветам


Юлия комментирует...

Яна, эта задача решена на sapato.ru (была решена, когда я в последний раз выбирала там туфли). Сапато сделан на битриксе, и там метод реализации прослеживается очень четко. Учет на стороне 1С идет только по одной характеристике - размер. Кроме того - у каждого товара есть множественное свойство - "этот товар в другом цвете", к-е содержит ссылки на тот же товар других цветов. К такому виду базу и должен привести контент-менеджер.

Яна комментирует...

Огромное спасибо!
Сейчас исследую!

Яна комментирует...

Юля, но получается что нужно переписывать логику шаблона демо магазина, так как он не будет переключать по цветам или я ошибаюсь и при реализации SAPATO Можно настроить компонент чтобы переключать цвета в каждой карточке товара. Так же при этом решении нужно переворачивать с ног на голову 1с.


Я посмотрела сайт, там не реализовано до конца. Например, толстовка http://www.sapato.ru/1693585 у нее 4 цвета в поисковой выдаче(3 строка) http://www.sapato.ru/clothing_puloveryiikardiganyi/?pol=w и я не вижу и в поисковой выдаче ни на карточке товара возможность посмотреть другие цвета.

В моем решении логику можно не трогать ни 1с ни Битрикса. Вопрос только в выгрузке. При чем первую часть (когда у одного товара один цвет) делает менеджер и он привязывает торговые предложения по другим цветам с товару. А выгрузку менять нужно только в ситуации, когда у товара несколько торговых предложений по цветам. Нужно создавать дубли товаров с ведущим цветом и линковать торговые предложения, меняя их расположения внутри товара (чтобы первым стояли торговые предложения, у которых совпадает цвет с цветом товара)

Юлия комментирует...

Яна, я вижу, вы не ищете легких путей. Вам делать - Вам и выбирать способ. Вариант, к-й предложила я - это 2 строки кода на уровне шаблона + работа контент-менеджера. Ваш вариант - это поездка из Армавира в Сочи с пересадкой во Владивостоке.

Яна комментирует...

Какое решение вы имели в виду?

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

Вы писали:
1."Яна, в вашей ситуации я бы посоветовала нанять контент-менеджера, чтобы привести в порядок номенклатуру на стороне 1С. Это будет намного-намного дешевле, чем вставлять костыли, к-е все равно костылями останутся." Не вижу решения. К какому виду. Чтобы изменить структуру данных в 1С, нужно ломать 1С - это процесс долгий.

2."Учет на стороне 1С идет только по одной характеристике - размер. Кроме того - у каждого товара есть множественное свойство - "этот товар в другом цвете", к-е содержит ссылки на тот же товар других цветов. К такому виду базу и должен привести контент-менеджер." Тоже нужно ломать 1с. К тому хранение только размера в ТП не решит вопроса переключения цветов на карточке товара.

Какое решение вы имели в виду?

Юлия комментирует...

Яна, делайте свой вариант - он вам понятнее. Потом когда сделаете или, может быть, в процессе поймете, что имела в виду я. А так переубеждать вас я могу только за денежку. 2500р/час и с предварительной записью на неделю вперед.

Яна комментирует...

Хорошо.
Возьмем эту реализацию
"Учет на стороне 1С идет только по одной характеристике - размер. Кроме того - у каждого товара есть множественное свойство - "этот товар в другом цвете", к-е содержит ссылки на тот же товар других цветов. К такому виду базу и должен привести контент-менеджер."
Которая не решает вопроса переключения цветов на карточке товара.
Возможно ли изменить привязку к товарам другим цветам в карточке товара на привязку других торговых предложений (по другим цветам) к товару? То есть связь ТП к товару не 1 к 1, а 1 ко многим. Непонятно только как выгрузка будет себя вести при обновлении. То есть если это update интересует привязка. Сейчас проверю.

Яна комментирует...

Я вас не поняла и лирику про обход через Владивосток, так вы так и не ответили какое это решение, которое решит поставленную задачу. Я в первую очередь думаю о том, как сдлеать чтобы все не ломать. Не ломать 1С, не ломать Битрикс.

Но тем не менее спасибо за ответы.
Чужой опыт всегда ценен.

Яна комментирует...

Да и на саббато эта задача не решена. Я вам все примеры с ссылкой привела. Посмотрите сами. Там вообще нет переключателя по цветам.

anubis3d комментирует...


Для решения задачи, которую вы описали, я не стала бы пользоваться событиями, а скопировала бы компонент импорта в собственное пространство имен, потом бы унаследовала класс импорта

class CIBlockCMLCustomImport extends CIBlockCMLImport
{

}

И переопределила бы функцию ImportElement.

И вот в ней бы уже сохраняла в переменную кол-во товара, к-е было , сравнивала бы его с количеством, к-е пришло из 1С и, если нужно - включила бы флаг новинки.



Насколько я понимаю весь класс копировать не нужно, иначе при его обновлении ничего не обновится - будет использоваться, то что скопировали себе.
Соответственно, нужно каким то правильным образом только определить
"class CIBlockCMLCustomImport extends CIBlockCMLImport"

Правильно понимаю?

"И переопределила бы функцию ImportElement."
Тогда штатная будет отрабатывать или нет?