Как вывести древовидную структуру разделов каталога с элементами из Битрикса в csv?

Понадобилось на одном из проектов рекурсивно вывести древовидную структуру разделов каталога, а также все вложенные элементы в csv файл.

Заказчик просил именно древовидную структуру и именно Excel. Поэтому вложенные разделы находятся на следующей от родительской колонки, также как и вложенные элементы

Наглядность немного страдает, когда есть вложенные разделы и товары одновременно, но заказчкика это устроило. Также, чтобы их различать у товаров вначале я добавил символ "-"

Вообще вывод можно допилить как угодно, главное иметь основную подготовку разделов и товаров к выводу.

Я использовал наработки Бедросовой Юлии и Поповича Алексея, немного адаптировав их под свои нужды получился такой скрипт:



global $arSectElements, $maxDepth;

$maxDepth = 0; // максимальная глубина каталога
$map = array();
$arParams["IBLOCK_MAP_ID"] = 1;  // - ID инфоблока

// собираем товары, ключ - айдишник раздела
$resEl = CIBlockElement::GetList(array(),array("IBLOCK_ID"=>37, "ACTIVE"=>"Y"),false,false,array("ID","NAME","IBLOCK_SECTION_ID"));
while($ob = $resEl->Fetch())
{
	$ob["NAME"] = str_replace(";","",$ob["NAME"]); // у меня были в некоторых товарах ; поэтому пришлось их вырезать
	
	$arSectElements[$ob["IBLOCK_SECTION_ID"]][] = $ob;
}

// получаем все разделы и смотрим максимальный уровень вложенности
if(isset($arParams["IBLOCK_MAP_ID"])){
   $arFilter = Array(
        'IBLOCK_ID'=>$arParams["IBLOCK_MAP_ID"],
        'GLOBAL_ACTIVE'=>'Y'
   );
   $db_list = CIBlockSection::GetList(Array("left_margin"=>"asc"), $arFilter, true);
   while($ar_result = $db_list->GetNext()) {
        $map[$ar_result['ID']]=$ar_result;
		
		if($maxDepth < $ar_result["DEPTH_LEVEL"])
			$maxDepth = $ar_result["DEPTH_LEVEL"];		
   }
}

$maxDepth += 1; // добавляем уровень вложенности т.к. еще товары выводятся

// Подготавливаем массив для удобной рекурсии
$map_sec = array();
foreach ($map as $key => $val) {
  if ($val['IBLOCK_SECTION_ID'] > 0) {
	 $parent_id = $map[$val['IBLOCK_SECTION_ID']]['ID'];
	 if(!isset($map_sec[$parent_id])){
		$map_sec[$parent_id]['ID'] = $map[$parent_id]['ID'];
		$map_sec[$parent_id]['CODE'] = $map[$parent_id]['CODE'];
		$map_sec[$parent_id]['NAME'] = $map[$parent_id]['NAME'];

		$map_sec[$parent_id]['DEPTH_LEVEL'] = $map[$parent_id]['DEPTH_LEVEL'];
		$map_sec[$parent_id]['IBLOCK_SECTION_ID'] = $map[$parent_id]['IBLOCK_SECTION_ID'];
	 }
	 $map_sec[$parent_id]['CHILDS'][$val['ID']] = array(
		"ID" => $val['ID'],
		"CODE" => $val['CODE'],
		"NAME" => $val['NAME'],
		"DEPTH_LEVEL" => $val['DEPTH_LEVEL'],
		"IBLOCK_SECTION_ID" => $val['IBLOCK_SECTION_ID'],
	 );
  } else {
	 if(!isset($map_sec[$val['ID']])){
		$map_sec[$val['ID']] = array(
		   "ID" => $val['ID'],
		   "CODE" => $val['CODE'],
		   "NAME" => $val['NAME'],
		   "DEPTH_LEVEL" => $val['DEPTH_LEVEL'],
		   "IBLOCK_SECTION_ID" => $val['IBLOCK_SECTION_ID'],
		);
	 }
  }
}

// Сама рекурсивная функция вывода
function ShowElementsTree($category_arr, $parent_id, $handle)
{
	global $arSectElements, $maxDepth; // чтобы были тут видны (можно через параметры функции передавать)
	
	if (intval($parent_id) > 0)
	{
		if (isset($category_arr[$parent_id]))
		{
			foreach ($category_arr[$parent_id]['CHILDS'] as $value) // Обходим
			{
			   fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] - 1) . $value['NAME'] . str_repeat(";", $maxDepth - $value['DEPTH_LEVEL'])."\r\n");		  

				if (count($category_arr[$value["ID"]]['CHILDS']) > 0)
				{
					//Рекурсивно вызываем эту же функцию, но с новым $parent_id
					ShowElementsTree($category_arr, $value["ID"], $handle);
				}
				else {}
				
				// товары
			   if(!empty($arSectElements[$value["ID"]]))
				{
					foreach($arSectElements[$value["ID"]] as $elem)
					{
						fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL']) . " - ".$elem['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL'] + 1))."\r\n");
					}
				}				
			}
		}
	}
	else
	{
		foreach ($category_arr as $parent_id => $arItems)
		{
			if (isset($category_arr[$parent_id]))
			{
				$value = $category_arr[$parent_id];
				
				fwrite($handle, $value['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL']))."\r\n");
				
				foreach ($category_arr[$parent_id]['CHILDS'] as $value) //Обходим
				{
					fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] - 1) . $value['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL']))."\r\n");
					
					if (count($category_arr[$value["ID"]]['CHILDS']) > 0) {
						// Рекурсивно вызываем эту же функцию, но с новым $parent_id
						ShowElementsTree($category_arr, $value["ID"], $handle);
					}
					else {}
					
					// товары
					if(!empty($arSectElements[$value["ID"]]))
					{
						foreach($arSectElements[$value["ID"]] as $elem)
						{
							fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] ) . " - ".$elem['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL'] + 1))."\r\n");
						}
					}
				}
				
				// товары
				if(!empty($arSectElements[$value["ID"]]))
				{
					foreach($arSectElements[$value["ID"]] as $elem)
					{
						fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] ). " - ".$elem['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL']+1))."\r\n");
					}
				}
			}
		}
	}
	
	return $csv_str;
}



$filename = '/home/bitrix/ПУТЬ_ДО_ФАЙЛА/pnx.csv';

// Вначале давайте убедимся, что файл существует и доступен для записи.
if (is_writable($filename)) {

    if (!$handle = fopen($filename, 'w')) {
         echo "Не могу открыть файл ($filename)";
         exit;
    }

	$csv = ShowElementsTree($map_sec,false, $handle);
	
    // Записываем $somecontent в наш открытый файл.
    if (fwrite($handle, $somecontent) === FALSE) {
        echo "Не могу произвести запись в файл ($filename)";
        exit;
    }
    
    fclose($handle);

} else {
    echo "Файл $filename недоступен для записи";
}


Возврат к списку