У клиента есть база в 1С и есть сайт, который создавался мной. Клиент торгует одеждой, соответственно, у товара могут быть разные размеры и цвета - стоимость меняется по желанию. Времени на переделку 1с пока нет, а выгружать надо было в субботу-воскресенье.
1С была построоена таким образом, что все товары хранились одним списком, отличались только названиями и свойствами
Блуза женская Satin Б-122-1145 / Черная, 44 размер Блуза женская Satin Б-122-1145 / Синяя, 44 размер
Хранить такую информацию в базе и фильтровать неудобно. Было принято решение перейти на торговые предложения с выделением основного товара и выделения модификаций.
Блуза женская Satin Б-122-1145 - Основное
Торговое предложение
Блуза женская Satin Б-122-1145
Цвет Черная
Размер 44
Блуза женская Satin Б-122-1145 Цвет Синяя Размер 44
Свойства хранятся в справочнике, чтобы не было повторений и ошибок в наполнении.
Следовательно, нам выгружали два файла
offers.xml import.xml
В одном хранятся свойства, в другом цены, соответственно.
Файлы XML могут храниться любого размера, соответственно, поэтому был использован XMLReader
<?php
class xml2assoc2
{
function __construct($xml,$tag,$log_name)
{
$this->log_name = $log_name;
$this->need_lock_export = false; //поставить true когда необходимо что бы был 1 экспорт за 1 раз
$this->all_node_count = 0;
$this->firm_list = array();
$this->node_count = 0;
$this->node_name = $tag; //нод перебора заказов
$this->export_type = 2; //тип выгрузки 1 для 1с файла 2 для 2с файла
$this->xml_file = $xml;
if (!file_exists($this->xml_file)) die ('File '.$this->xml_file.' not exists');
$this->create_XML_reader();
$this->current_xml_array = array();
if (!$this->check_lock())
die('Выгрузка уже производится');
$this->calculate_all_node_count();
if ($this->all_node_count==0) die('Экспорт невозможен - количество нодов равно 0');
}
public function calculate_all_node_count()
{
$this->xml_count_reader = new XMLReader();
$this->xml_count_reader->open($this->xml_file);
while($this->xml_count_reader->read())
{
switch ($this->xml_count_reader->nodeType)
{
case XMLReader::END_ELEMENT:
break;
case XMLReader::ELEMENT:
$name = $this->xml_count_reader->name;
if ($name == $this->node_name)
{
$this->all_node_count++;
}
break;
case XMLReader::TEXT:
case XMLReader::CDAT A:
}
}
$this->xml_count_reader->close();
}
//получение данных о ноде
public function node_data()
{
return $this->current_xml_array;
}
public function return_firm_statistic()
{
return $this->firm_list;
}
//закрываем ХМЛ потоковый
public function close_xml()
{
$this->xml->close();
$this->delete_lock();
$this->save_firm_export_to_file(100);
}
///создание потокового чтения ХИЛ
private function create_XML_reader()
{
$this->xml = new XMLReader();
$this->xml->open($this->xml_file);
}
//Устанавливает тип экспорта - смотри инициализацию класса
public function set_export_type($type)
{
if ($type!=1)
$this->export_type = 2;
else
$this->export_type = 1;
}
///считываем заказы поэлементно
private function read_orders()
{
while($this->xml->read())
{
switch ($this->xml->nodeType)
{
case XMLReader::ELEMENT:
$name = $this->xml->name;
//echo $name.'<br>';
if ($name ==$this->node_name)
{
return $this->get_order_node();
}
break;
case XMLReader::TEXT:
case XMLReader::CDAT A:
}
}
}
///функция инициализации и считывания поэлементно заказов
public function get_next_order()
{
$data = $this->read_orders();
//print_r($data);
$this->current_xml_array = array();
if (is_array($data))
{
$this->current_xml_array = $data['data'];
$this->node_count++;
$this->save_firm_export_to_file();
return $this->current_xml_array;
}
$this->close_xml();
return false;
}
///статистика
public function get_statistic()
{
return 'Количество нодов:'.$this->node_count.'<br/>Количество использованной оперативной памяти:'.memory_get_usage(true).' байт</br>';
}
///Считываем сам нод заказа
private function get_order_node()
{
$prefix ='_c';
$assoc = null;
while($this->xml->read())
{
switch ($this->xml->nodeType) {
case XMLReader::END_ELEMENT: return array('type'=>$prefix,'data'=>$assoc);
case XMLReader::ELEMENT:
$name = rus2translit_utf($this->xml->name);
$have_atr = $this->xml->hasAttributes;
if ($this->xml->isEmptyElement)
{
$assoc[$name]['_v']='';
}
else
{
$val = $this->get_order_node();
if (isset($assoc[$name]))
{
///если индекс 0 есть то не нужно переделывать массив
if (!isset($assoc[$name][0]))
{
$val_temp = $assoc[$name];
$assoc[$name] = array();
$assoc[$name][0] = $val_temp;
}
$id = sizeof($assoc[$name]);
if ($have_atr) $el = & $assoc[$name][$id]['_a'];
$assoc[$name][$id][$val['type']] = $val['data'];
}
else
{
if ($have_atr) $el = &$assoc[$name]['_a'];
$assoc[$name][$val['type']] = $val['data'];
}
}
if($have_atr)
{
///$el =& $assoc[$name]['_a'];
while($this->xml->moveToNextAttribute())
$el[rus2translit_utf($this->xml->name)] = $this->xml->value;
}
break;
case XMLReader::TEXT:
case XMLReader::CDAT A:
$prefix = '_v';
$assoc .= $this->xml->value;
}
}
$val = array('type'=>$prefix,'data'=>$assoc);
return $val;
}
private function lock_export()
{
file_put_contents('lock.dat',time()+180);
}
public function delete_lock()
{
if (file_exists('lock.dat')) unlink('lock.dat');
file_put_contents('lock.dat',time()-5);
}
private function check_lock()
{
if ($this->need_lock_export == false) return true;
if (file_exists('lock.dat'))
{
/// die('Идет экспорт');
$time = @file_get_contents('lock.dat');
if (is_numeric(@$time))
{
if (time()<$time)
{
return false;
}
}
}
$this->lock_export();
return true;
}
private function save_firm_export_to_file($procent='')
{
if (@$handle = fopen('procent_status.txt', 'w+'))
{
if ($procent==='') $procent = intval(100*$this->node_count/$this->all_node_count);
fwrite($handle, $this->log_name.": ".$procent.'%');
fclose($handle);
}
}
public function draw_info()
{
return print_r($this->current_xml_array,true).'<hr>';
}
}
if (!function_exists("rus2translit_utf"))
{
function rus2translit_utf($string) {
$converter = array(
'а' => 'a', 'б' => 'b', 'в' => 'v',
'г' => 'g', 'д' => 'd', 'е' => 'e',
'ё' => 'e', 'ж' => 'zh', 'з' => 'z',
'и' => 'i', 'й' => 'y', 'к' => 'k',
'л' => 'l', 'м' => 'm', 'н' => 'n',
'о' => 'o', 'п' => 'p', 'р' => 'r',
'с' => 's', 'т' => 't', 'у' => 'u',
'ф' => 'f', 'х' => 'h', 'ц' => 'c',
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch',
'ь' => '\'', 'ы' => 'y', 'ъ' => '\'',
'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
'А' => 'A', 'Б' => 'B', 'В' => 'V',
'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z',
'И' => 'I', 'Й' => 'Y', 'К' => 'K',
'Л' => 'L', 'М' => 'M', 'Н' => 'N',
'О' => 'O', 'П' => 'P', 'Р' => 'R',
'С' => 'S', 'Т' => 'T', 'У' => 'U',
'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch',
'Ь' => '\'', 'Ы' => 'Y', 'Ъ' => '\'',
'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya',
' ' => '_', '"'=> '',
);
return strtr($string, $converter);
}
}
?>
В классе мы можем видеть сколько памяти потребляет скрипт ( он был оптимизирован под использование не больше 5 МБ памяти на 100 МБ XML )
И сам обработчик:
<?php
//header('Content-type: text/html; charset=UTF-8');
include_once "import_market.php";
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
CModule::IncludeModule("iblock");
CModule::IncludeModule("catalog");
$groups = array();
$xml2assoc2 = new xml2assoc2("import.xml",convert_utf('Группа'),'Экспорт названий категорий');
$xml2assoc2->set_export_type(2);
while ($data = $xml2assoc2->get_next_order())
{
//считываем данные по категориям
foreach ($data['Gruppy']['_c']['Gruppa'] as $id=>$val)
{
$groups[$val['_c']['Id']['_v']] = $val['_c']['Naimenovanie']['_v'];
}
}
//считываем данные данные по товарам - соотношение айди и группы
$goods_data = array();
$xml2assoc2 = new xml2assoc2("import.xml",convert_utf('Товар'),'Экспорт данных товаров по категориям');
$xml2assoc2->set_export_type(2);
while ($data = $xml2assoc2->get_next_order())
{
$goods_data[$data['Id']['_v']] = array('Artikul'=>$data['Artikul']['_v'],'Gruppy'=>$data['Gruppy']['_c']['Id']['_v']);
}
//print_r($goods_data);die();
function convert_1251($str)
{
return iconv('utf-8','windows-1251', $str);
}
function convert_utf($str)
{
return iconv('windows-1251','utf-8', $str);
}
//обработка нодов
$xml2assoc2 = new xml2assoc2("offers.xml",convert_utf('Предложение'),'Экспорт цен');
$xml2assoc2->set_export_type(2);
while ($data = $xml2assoc2->get_next_order())
{
//тут обработка должна быть нода
$full_name = $data['Naimenovanie']['_v'];
$cost = $data['Ceny']['_c']['Cena']['0']['_c']['CenaZaEdinicu']['_v'];
//получаем размер и цвет
$slesh_pos = strrpos($full_name,'/');
$name = convert_1251(trim(mb_substr($full_name,0,$slesh_pos)));
$item_data = explode(',',trim(mb_substr($full_name,$slesh_pos+1)));
$color = convert_1251(trim(@$item_data[0]));
$size = trim(intval(@$item_data[1]));
$group_id = $goods_data[$data['Id']['_v']]['Gruppy'];
$group_name = convert_1251($groups[$goods_data[$data['Id']['_v']]['Gruppy']]);
/*
echo 'Название:'.$name."<br>";
echo 'Цена:'.$cost."<br>";
echo 'Цвет:'.$color."<br>";
echo 'Размер:'.$size."<br>";
echo 'Группа (ID):'.$group_id."<br>";
echo 'Группа (название):'.$group_name."<br>";
echo '<hr>';
*/
/* РАЗДЕЛ */
$arFilterSection = Array('IBLOCK_ID'=>'3', 'GLOBAL_ACTIVE'=>'Y', 'NAME'=>$group_name);
$db_list_section = CIBlockSection::GetList(Array(), $arFilterSection, true);
while($ar_result_section = $db_list_section->GetNext())
{
$GROUP_ID=$ar_result_section['ID'];
}
if(!$GROUP_ID):
$bs = new CIBlockSection;
$arFields = Array(
"ACTIVE" => "Y",
"IBLOCK_ID" => "3",
"NAME" => $group_name,
"CODE" => rus2translit($group_name)
);
$GROUP_ID = $bs->Add($arFields);
endif;
/* РАЗДЕЛ */
/* ЦВЕТ */
echo 'Цвет:'.$color."<br>";
$arSelectColor = Array("ID");
$arFilterColor = Array("IBLOCK_ID"=>"5", "ACTIVE_DATE"=>"Y", "ACTIVE"=>"Y", "NAME"=>$color);
$resColor = CIBlockElement::GetList(Array(), $arFilterColor, false, Array("nPageColor"=>1), $arSelectColor);
while($obColor = $resColor->GetNextElement())
{
$arFieldsColor = $obColor->GetFields();
$COLOR_ID=$arFieldsColor["ID"];
}
if (!$COLOR_ID):
$el_color = new CIBlockElement;
$arLoadProductArrayColor = Array(
"MODIFIED_BY" => $USER->GetID(), // элемент изменен текущим пользователем
"IBLOCK_SECTION_ID" => false, // элемент лежит в корне раздела
"IBLOCK_ID" => 5,
"NAME" => $color,
"ACTIVE" => "Y" // активен
);
$COLOR_ID = $el_color->Add($arLoadProductArrayColor);
endif;
/* ЦВЕТ */
/* РАЗМЕР */
echo 'Размер:'.$size."<br>";
$arSelectSize = Array("ID");
$arFilterSize = Array("IBLOCK_ID"=>"6", "ACTIVE_DATE"=>"Y", "ACTIVE"=>"Y", "NAME"=>$size);
$resSize = CIBlockElement::GetList(Array(), $arFilterSize, false, Array("nPageSize"=>1), $arSelectSize);
while($obSize = $resSize->GetNextElement())
{
$arFieldsSize = $obSize->GetFields();
$SIZE_ID=$arFieldsSize["ID"];
}
if (!$SIZE_ID):
$el_size = new CIBlockElement;
$arLoadProductArraysize = Array(
"MODIFIED_BY" => $USER->GetID(), // элемент изменен текущим пользователем
"IBLOCK_SECTION_ID" => false, // элемент лежит в корне раздела
"IBLOCK_ID" => 6,
"NAME" => $size,
"ACTIVE" => "Y" // активен
);
$SIZE_ID = $el_size->Add($arLoadProductArraysize);
endif;
/* РАЗМЕР */
/* ТОВАР */
$arSelectElement = Array("ID", "NAME", "DATE_ACTIVE_FROM");
$arFilterElement = Array("IBLOCK_ID"=>"3", "ACTIVE_DATE"=>"Y", "ACTIVE"=>"Y", "NAME"=>$name);
$res_element = CIBlockElement::GetList(Array(), $arFilterElement, false, Array("nPageSize"=>50), $arSelectElement);
while($ob_element = $res_element->GetNextElement())
{
$arFieldsElement = $ob_element->GetFields();
$ELEMENT_ID=$arFieldsElement["ID"];
}
if (!$ELEMENT_ID):
$el_element = new CIBlockElement;
$arLoadProductArrayElement = Array(
"MODIFIED_BY" => $USER->GetID(), // элемент изменен текущим пользователем
"IBLOCK_SECTION_ID" => $GROUP_ID, // элемент лежит в корне раздела
"IBLOCK_ID" => 3,
"NAME" => $name,
"ACTIVE" => "Y", // активен
);
$ELEMENT_ID = $el_element->Add($arLoadProductArrayElement);
$el_element_offer = new CIBlockElement;
$PROP = array();
$PROP[13] = $COLOR_ID; // COLOR
$PROP[14] = $SIZE_ID; // SIZE
$arLoadProductArrayElementOffer = Array(
"MODIFIED_BY" => $USER->GetID(), // элемент изменен текущим пользователем
"IBLOCK_SECTION_ID" => false, // элемент лежит в корне раздела
"IBLOCK_ID" => 7,
"NAME" => $name,
"PROPERTY_VALUES"=> $PROP,
"ACTIVE" => "Y" // активен
);
$ELEMENT_ID_OFFER = $el_element_offer->Add($arLoadProductArrayElementOffer);
$arFieldsPrice = Array(
"PRODUCT_ID" => $ELEMENT_ID_OFFER,
"CATALOG_GROUP_ID" => "1",
"PRICE" => $cost,
"CURRENCY" => "RUB"
);
CPrice::Add($arFieldsPrice);
CIBlockElement::SetPropertyValues($ELEMENT_ID_OFFER, "7", $ELEMENT_ID, "CML2_LINK");
else:
$el_element = new CIBlockElement;
$PROP = array();
$PROP[13] = $COLOR_ID; // COLOR
$PROP[14] = $SIZE_ID; // SIZE
$arLoadProductArrayElement = Array(
"MODIFIED_BY" => $USER->GetID(), // элемент изменен текущим пользователем
"IBLOCK_SECTION_ID" => false, // элемент лежит в корне раздела
"IBLOCK_ID" => 7,
"NAME" => $name,
"PROPERTY_VALUES"=> $PROP,
"ACTIVE" => "Y" // активен
);
$ELEMENT_ID_OFFER = $el_element->Add($arLoadProductArrayElement);
CIBlockElement::SetPropertyValues($ELEMENT_ID_OFFER, "7", $ELEMENT_ID, "CML2_LINK");
$arFieldsPrice = Array(
"PRODUCT_ID" => $ELEMENT_ID_OFFER,
"CATALOG_GROUP_ID" => "1",
"PRICE" => $cost,
"CURRENCY" => "RUB"
);
CPrice::Add($arFieldsPrice);
unset($el_element);
unset($PROP);
endif;
echo 'ТОВАР:'.$ELEMENT_ID."<br>";
/* ТОВАР */
unset($GROUP_ID);
unset($SIZE_ID);
unset($COLOR_ID);
unset($ELEMENT_ID);
unset($ELEMENT_ID_OFFER);
//ОБРАБОТКА ДОЛЖНА БЫТЬ ТУТ
}
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog_after.php");
?>
Таким образом у нас заполняются справочники, создаются разделы, товары в них заносятся и привязываются торг предложения.
Не проще было повесить обработчик на OnBeforeIBlockElementUpdate/OnBeforeIBlockElementAdd и в нем распихивать в отдельный инфоблок предложения, а заодно и создавать нужные значения списочных свойств? Мне видится, Вы какой-то велосипед изобрели. Неужели это полотно из кода является оптимальным решением, учитывая, что
Времени на переделку 1с пока нет, а выгружать надо было в субботу-воскресенье.
Сорри, забыл упомянуть: у клиента стоит 1С 7,7. Обновлять и тд - отказывается. Предпочитает только вести настройку 1с с нуля. Этим занимаются его 1с программисты. Поэтому и пришлось развести такой зоопарк, т.к. штатных обработчиков нет в 7.7. + она очень сильно кастомизирована была.
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».