20.09.2025

Получение элементов ИБ со всеми свойствами и создание Excel с помощью PhpSpreadsheet

В примере рассмотрим как сформировать Excel файл с помощью PhpSpreadsheet состоящий из двух страниц, на страницах записываем информацию о всех элементах ИБ с полями и всеми свойствами.

Для начала подключаем необходимые классы и получаем необходимые параметры


<?php
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style;
use Bitrix\Main\Loader;
use Bitrix\Iblock\PropertyTable;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

global $USER;

function getFormData($method)
{
	// GET или POST: данные возвращаем как есть
	if($method === 'GET')
		return $_GET;
	if($method === 'POST')
		return $_POST;

	// PUT, PATCH или DELETE
	$data = [];
	$exploded = explode('&', file_get_contents('php://input'));

	foreach($exploded as $pair)
	{
		$item = explode('=', $pair);
		if(count($item) == 2)
		{
			$data[urldecode($item[0])] = urldecode($item[1]);
		}
	}

	return $data;
}

$formData = getFormData($_SERVER['REQUEST_METHOD']);

$action = $formData["action"];

if($action != "downloadExcel" && !$USER->IsAuthorized())
{
	exit;
}

Loader::includeModule('iblock');

$host = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'];

$spreadsheetOPEN = new Spreadsheet();

//шрифт файла
$spreadsheetOPEN->getDefaultStyle()->getFont()->setName('Aptos SemiBold')->setSize(12);

///////////// Платформы /////////////
$sheet1 = $spreadsheetOPEN->getActiveSheet();
$sheet1->setTitle('Платформы'); // название листа
$sheet1->getPageMargins()->setTop(0.50);
$sheet1->getPageMargins()->setRight(0.35);
$sheet1->getPageMargins()->setLeft(0.35);
$sheet1->getPageMargins()->setBottom(0.50);
$sheet1->setShowGridlines(false);
	

Получаем все свойства ИБ и записываем их для select в формате в зависимости от типа


$props = PropertyTable::getList([
	'filter' => [
		'=IBLOCK_ID' => CONFIGURATOR_SYSTEMS,
		'=ACTIVE' => 'Y'
	],
	'order' => [
		'SORT' => 'ASC',
		'ID' => 'ASC'
	],
	'select' => [
		'ID',
		'CODE',
		'NAME',
		'PROPERTY_TYPE',
		'USER_TYPE',
		'MULTIPLE',
		'SORT',
		'LINK_IBLOCK_ID'
	]
])->fetchAll();

$selectProps = [];
$arrProps = [];
foreach($props as $p)
{
	if($p["PROPERTY_TYPE"] == "E")
	{
		$selectProps[] = $p["CODE"].".ELEMENT";
	}
	elseif($p["PROPERTY_TYPE"] == "S" || $p["PROPERTY_TYPE"] == "N")
	{
		$selectProps[] = $p["CODE"];
	}
	elseif($p["PROPERTY_TYPE"] == "L")
	{
		$selectProps[] = $p["CODE"].".ITEM";
	}
	elseif($p["PROPERTY_TYPE"] == "F")
	{
		$selectProps[] = $p["CODE"].".FILE";
	}
	$arrProps[$p["CODE"]] = $p;
}

$selectFields = [
	'ID' => "ID",
	'DETAIL_TEXT' => "Название",
	'ACTIVE' => "Активность"
];

$row = 2;
$sheet1->getCell("A".$row)->setValue("Платформы");
$row += 2;

Заполняем шапку таблицы названиями полей и свойств и устанавливаем их ширину


$startCell = "A".$row;
$col = 1;
foreach($selectFields as $kF => $field)
{
	$colLetter = Coordinate::stringFromColumnIndex($col); // A, B, C...
	$cell = $colLetter.$row;

	// Пишем значение
	$sheet1->setCellValue($cell, $field);

	$w = 30;
	if(in_array($kF, ["DETAIL_TEXT"]))
	{
		$w = 50;
	}
	$sheet1->getColumnDimension($colLetter)->setWidth($w);

	$col++;
}

foreach($arrProps as $prop)
{
	$colLetter = Coordinate::stringFromColumnIndex($col); // A, B, C...
	$cell = $colLetter.$row;

	// Пишем значение
	$sheet1->setCellValue($cell, $prop["NAME"]);

	$w = 30;
	if(in_array($prop["CODE"], ["CO_DESCRIPTION", "DESCRIPTION", "DOCUMENTATION", "COMPLECT"]))
	{
		$w = 80;
	}
	$sheet1->getColumnDimension($colLetter)->setWidth($w);

	$col++;
}

$endColumnLetter = $colLetter;

$selectFields['NAME'] = "Название(старое)";
$select = array_merge(array_keys($selectFields), $selectProps);
	

Делаем запрос на ИБ с сформированным select, в котором нужные поля и все свойства.


// Важно!!! Значения свойств ИБ должны храниться "в отдельной таблице для данного информационного блока" чтобы не было ошибки лимита 61 таблица в одном JOIN (Too many tables; MariaDB can only use 61 tables in a join)
$entity = \Bitrix\Iblock\Iblock::wakeUp(CONFIGURATOR_SYSTEMS)->getEntityDataClass();
$rowsItems = $entity::getList([
	'select' => $select,
	'order' => [
		'ID' => 'ASC',
	],
])->fetchCollection();
	

Перебираем полученные элементы ИБ и заполняем таблицу значениями в соответствии с типом свойств


foreach($rowsItems as $element)
{
	$col = 1;
	$row++;

	$cell = Coordinate::stringFromColumnIndex($col).$row;
	$sheet1->setCellValue($cell, $element->get("ID"));

	$col++;
	$cell = Coordinate::stringFromColumnIndex($col).$row;
	$sheet1->setCellValue($cell, $element->get("DETAIL_TEXT") ?: $element->get("NAME"));

	$col++;
	$cell = Coordinate::stringFromColumnIndex($col).$row;
	$sheet1->setCellValue($cell, $element->get("ACTIVE") ? "Да" : "Нет");

	foreach($arrProps as $key => $value)
	{
		$col++;
		$valCell = "";
		if($value["PROPERTY_TYPE"] == "E")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$prObItem = $prVal->getElement();
						$valCell .= ($prObItem->get("DETAIL_TEXT") ?: $prObItem->get("NAME"))."\n";
					}
				}
			}
			else
			{
				$prOb = $element->get($value["CODE"])?->getElement() ?? [];
				if($prOb)
				{
					$valCell = $prOb->get("DETAIL_TEXT") ?: $prOb->get("NAME");
				}
			}
		}
		elseif($value["PROPERTY_TYPE"] == "N")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$valCell .= $prVal->getValue()."\n";
					}
				}
			}
			else
			{
				$valCell = $element->get($value["CODE"])?->getValue();
			}
		}
		elseif($value["PROPERTY_TYPE"] == "S")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					$kitArr = [];
					foreach($prOb as $prVal)
					{
						if($value["USER_TYPE"] == "HTML")
						{
							$valCell .= unserialize($prVal->getValue())["TEXT"];
						}
						else
						{
							if($value["CODE"] == "COMPLECT")
							{
								$kitArr[$prVal->getValue()] = $prVal->getDescription();
							}
							else
							{
								$valCell .= $prVal->getValue()."\n";
							}
						}
					}
					if($value["CODE"] == "COMPLECT")
					{
						$kitOb = \Bitrix\Iblock\Elements\ElementConfiguratorComponentsTable::getList([
							'filter' => [
								"ID" => array_keys($kitArr),
							],
							"select" => [
								"ID",
								"NAME",
								"DETAIL_TEXT"
							],
						]);
						while($arrKit = $kitOb->fetch())
						{
							$valCell .= ($arrKit["DETAIL_TEXT"] ?: $arrKit["NAME"])." - ".$kitArr[$arrKit["ID"]]."шт\n";
						}
					}
				}
			}
			else
			{
				if($value["USER_TYPE"] == "HTML")
				{
					$valCell = unserialize($element->get($value["CODE"])?->getValue())["TEXT"];
				}
				else
				{
					$valCell = $element->get($value["CODE"])?->getValue();
				}
			}
		}
		elseif($value["PROPERTY_TYPE"] == "L")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$prObItem = $prVal->getItem();
						$valCell .= $prObItem->getValue()."\n";
					}
				}
			}
			else
			{
				$prOb = $element->get($value["CODE"])?->getItem() ?? [];
				if($prOb)
				{
					$valCell = $prOb->getValue();
				}
			}
		}
		elseif($value["PROPERTY_TYPE"] == "F")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$fName = $prVal->getFile()->getFileName();
						$valCell .= $host."/upload/".$prVal->getFile()->getSubdir()."/".$fName."\n";
					}
				}
			}
			else
			{
				$prVal = $element->get($value["CODE"]);
				$fName = $prVal->getFile()->getFileName();
				$valCell = $host."/upload/".$prVal->getFile()->getSubdir()."/".$fName."\n";
			}
		}
		$cell = Coordinate::stringFromColumnIndex($col).$row;

		// включаем перенос строк
		//	$sheet1->getStyle($cell)->getAlignment()->setWrapText(true);

		// включаем авто-высоту для строки
		$sheet1->getRowDimension($row)->setRowHeight(-1);

		$sheet1->setCellValue($cell, $valCell);
	}
}

$sheet1->getStyle($startCell.':'.$endColumnLetter.$row)->applyFromArray([
	'borders' => [
		'allBorders' => [ // рамки со всех сторон
			'borderStyle' => Style\Border::BORDER_THIN,
			'color' => ['argb' => 'FF000000'], // чёрный цвет
		],
	],
	'alignment' => [
		'horizontal' => Style\Alignment::HORIZONTAL_CENTER, // по горизонтали
		'vertical' => Style\Alignment::VERTICAL_CENTER,   // и по вертикали
		//	'indent'     => 20, // отступ в символах (только при горизонтальном выравнивании Left или Right)
		'wrapText' => true, // перенос строк, если надо
	],
]);
	

Для второй страницы делаем тоже самое


///////////// Компоненты /////////////
$sheet2 = new Worksheet($spreadsheetOPEN, 'Компоненты');
$sheet2->getPageMargins()->setTop(0.50);
$sheet2->getPageMargins()->setRight(0.35);
$sheet2->getPageMargins()->setLeft(0.35);
$sheet2->getPageMargins()->setBottom(0.50);
$sheet2->setShowGridlines(false);
$spreadsheetOPEN->addSheet($sheet2);

$props = PropertyTable::getList([
	'filter' => [
		'=IBLOCK_ID' => CONFIGURATOR_ITEMS,
		'=ACTIVE' => 'Y'
	],
	'order' => [
		'SORT' => 'ASC',
		'ID' => 'ASC'
	],
	'select' => [
		'ID',
		'CODE',
		'NAME',
		'PROPERTY_TYPE',
		'USER_TYPE',
		'MULTIPLE',
		'SORT',
		'LINK_IBLOCK_ID'
	]
])->fetchAll();

$selectProps = [];
$arrProps = [];
foreach($props as $p)
{
	if($p["PROPERTY_TYPE"] == "E")
	{
		$selectProps[] = $p["CODE"].".ELEMENT";
	}
	elseif($p["PROPERTY_TYPE"] == "S" || $p["PROPERTY_TYPE"] == "N")
	{
		$selectProps[] = $p["CODE"];
	}
	elseif($p["PROPERTY_TYPE"] == "L")
	{
		$selectProps[] = $p["CODE"].".ITEM";
	}
	elseif($p["PROPERTY_TYPE"] == "F")
	{
		$selectProps[] = $p["CODE"].".FILE";
	}
	$arrProps[$p["CODE"]] = $p;
}

$selectFields = [
	'ID' => "ID",
	'DETAIL_TEXT' => "Название",
	'ACTIVE' => "Активность"
];

$row = 2;
$sheet2->getCell("A".$row)->setValue("Компоненты");
$row += 2;

$startCell = "A".$row;
$col = 1;
foreach($selectFields as $kF => $field)
{
	$colLetter = Coordinate::stringFromColumnIndex($col); // A, B, C...
	$cell = $colLetter.$row;

	// Пишем значение
	$sheet2->setCellValue($cell, $field);

	$w = 30;
	if(in_array($kF, ["DETAIL_TEXT"]))
	{
		$w = 50;
	}
	$sheet2->getColumnDimension($colLetter)->setWidth($w);

	$col++;
}

foreach($arrProps as $prop)
{
	$colLetter = Coordinate::stringFromColumnIndex($col); // A, B, C...
	$cell = $colLetter.$row;

	// Пишем значение
	$sheet2->setCellValue($cell, $prop["NAME"]);

	$w = 30;
	if(in_array($prop["CODE"], []))
	{
		$w = 80;
	}
	$sheet2->getColumnDimension($colLetter)->setWidth($w);

	$col++;
}

$endColumnLetter = $colLetter;

$selectFields['NAME'] = "Название(старое)";
$select = array_merge(array_keys($selectFields), $selectProps);

$entity = \Bitrix\Iblock\Iblock::wakeUp(CONFIGURATOR_ITEMS)->getEntityDataClass();
$rowsItems = $entity::getList([
	'select' => $select,
	'order' => [
		'ID' => 'ASC',
	],
])->fetchCollection();

foreach($rowsItems as $element)
{
	$col = 1;
	$row++;

	$cell = Coordinate::stringFromColumnIndex($col).$row;
	$sheet2->setCellValue($cell, $element->get("ID"));

	$col++;
	$cell = Coordinate::stringFromColumnIndex($col).$row;
	$sheet2->setCellValue($cell, $element->get("DETAIL_TEXT") ?: $element->get("NAME"));

	$col++;
	$cell = Coordinate::stringFromColumnIndex($col).$row;
	$sheet2->setCellValue($cell, $element->get("ACTIVE") ? "Да" : "Нет");

	foreach($arrProps as $key => $value)
	{
		$col++;
		$valCell = "";
		if($value["PROPERTY_TYPE"] == "E")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$prObItem = $prVal->getElement();
						$valCell .= ($prObItem->get("DETAIL_TEXT") ?: $prObItem->get("NAME"))."\n";
					}
				}
			}
			else
			{
				$prOb = $element->get($value["CODE"])?->getElement() ?? [];
				if($prOb)
				{
					$valCell = $prOb->get("DETAIL_TEXT") ?: $prOb->get("NAME");
				}
			}
		}
		elseif($value["PROPERTY_TYPE"] == "N")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$valCell .= $prVal->getValue()."\n";
					}
				}
			}
			else
			{
				$valCell = $element->get($value["CODE"])?->getValue();
			}
		}
		elseif($value["PROPERTY_TYPE"] == "S")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					$kitArr = [];
					foreach($prOb as $prVal)
					{
						if($value["USER_TYPE"] == "HTML")
						{
							$valCell .= unserialize($prVal->getValue())["TEXT"];
						}
						else
						{
							if($value["CODE"] == "COMPLECT")
							{
								$kitArr[$prVal->getValue()] = $prVal->getDescription();
							}
							else
							{
								$valCell .= $prVal->getValue()."\n";
							}
						}
					}
					if($value["CODE"] == "COMPLECT")
					{
						$kitOb = \Bitrix\Iblock\Elements\ElementConfiguratorComponentsTable::getList([
							'filter' => [
								"ID" => array_keys($kitArr),
							],
							"select" => [
								"ID",
								"NAME",
								"DETAIL_TEXT"
							],
						]);
						while($arrKit = $kitOb->fetch())
						{
							$valCell .= ($arrKit["DETAIL_TEXT"] ?: $arrKit["NAME"])." - ".$kitArr[$arrKit["ID"]]."шт\n";
						}
					}
				}
			}
			else
			{
				if($value["USER_TYPE"] == "HTML")
				{
					$valCell = unserialize($element->get($value["CODE"])?->getValue())["TEXT"];
				}
				else
				{
					$valCell = $element->get($value["CODE"])?->getValue();
				}
			}
		}
		elseif($value["PROPERTY_TYPE"] == "L")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$prObItem = $prVal->getItem();
						$valCell .= $prObItem->getValue()."\n";
					}
				}
			}
			else
			{
				$prOb = $element->get($value["CODE"])?->getItem() ?? [];
				if($prOb)
				{
					$valCell = $prOb->getValue();
				}
			}
		}
		elseif($value["PROPERTY_TYPE"] == "F")
		{
			if($value["MULTIPLE"] == "Y")
			{
				$prOb = $element->get($value["CODE"])?->getAll() ?? [];
				if($prOb)
				{
					foreach($prOb as $prVal)
					{
						$fName = $prVal->getFile()->getFileName();
						$valCell .= $host."/upload/".$prVal->getFile()->getSubdir()."/".$fName."\n";
					}
				}
			}
			else
			{
				$prVal = $element->get($value["CODE"]);
				$fName = $prVal->getFile()->getFileName();
				$valCell = $host."/upload/".$prVal->getFile()->getSubdir()."/".$fName."\n";
			}
		}
		$cell = Coordinate::stringFromColumnIndex($col).$row;

		// включаем авто-высоту для строки
		$sheet2->getRowDimension($row)->setRowHeight(-1);
		$sheet2->setCellValue($cell, $valCell);
	}
}

$sheet2->getStyle($startCell.':'.$endColumnLetter.$row)->applyFromArray([
	'borders' => [
		'allBorders' => [ // рамки со всех сторон
			'borderStyle' => Style\Border::BORDER_THIN,
			'color' => ['argb' => 'FF000000'], // чёрный цвет
		],
	],
	'alignment' => [
		'horizontal' => Style\Alignment::HORIZONTAL_CENTER, // по горизонтали
		'vertical' => Style\Alignment::VERTICAL_CENTER,   // и по вертикали
		//	'indent'     => 20, // отступ в символах (только при горизонтальном выравнивании Left или Right)
		'wrapText' => true, // перенос строк, если надо
	],
]);
	

Устанавливаем активность первой странице и сохраняем файл


$spreadsheetOPEN->setActiveSheetIndex(0);
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheetOPEN, "Xlsx");
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="'.urlencode("all-catalogs.xlsx").'"');
ob_end_clean();
$writer->save('php://output');

$spreadsheetOPEN->disconnectWorksheets();
unset($spreadsheetOPEN);
?>
		

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