S6 Studio

 

Инфоблоки в 1С-Битрикс - наследование свойств разделов

Bitrix  Инфоблоки  Разделы  Свойства  Наследование  Оптимизация  Решение  • Егор Ступников • 12 октября 2013 • Без комментариев

 

Идея решения основана на двух основополагающих факторах. Первый - это способ хранения структуры разделов в базе данных (Nested Set). Второй - это понятие стека. Так что если вы забыли, что такое стек - то рекомендую вспомнить.

Если кто-то из вас не в курсе, как осуществляется хранение разделов в базе, то советую взглянуть на данную картинку:

Nested set

Для каждого раздела существуют параметры LEFT_MARGIN и RIGHT_MARGIN. Их суть проще всего понять графически: мы начинаем обходить дерево разделов слева, не пересекая ребра.

На приведенной выше картинке красным цветом отмечены ID разделов, а синим - значения LEFT_MARGIN и RIGHT_MARGIN

За счет этого в каждую вершину графа (в каждый раздел) мы зайдем дважды - слева (устанавливается значение LEFT_MARGIN) и справа (устанавливается значение RIGHT_MARGIN). При каждом заходе счетчик увеличивается на единицу.

Исходя из этого можно сделать следующие выводы:

  • У разделов нижнего уровня разница между RIGHT_MARGIN и LEFT_MARGIN равна 1
  • Выборка разделов без ограничений и с сортировкой по LEFT_MARGIN дает нам возможность пройти по всему дереву разделов иерархически.
  • Использование выборок по значениям LEFT_MARGIN и RIGHT_MARGIN позволяет получать поддерево данного дерева.

Допустим, что параметр, наследование значений которого мы хотим реализовать - это наценка, пользовательское свойство, имеет код UF_ADDITIONAL_PAYMENT

Первое действие будет максимально простым - делаем выборку по всем разделам инфоблока. Это и будет наш единственный запрос к базе.

$SL = CIBlockSection::GetList(
	Array('LEFT_MARGIN' => 'ASC'),
	Array('IBLOCK_ID' => $CATALOG_ID),
	false,
	Array('UF_*')
);
		
while($S = $SL->GetNext())
{
	$Sections[$S['ID']] = Array(
		'ADDITIONAL_PAYMENT' => strlen($S['UF_ADDITIONAL_PAYMENT']) > 0 ? $S['UF_ADDITIONAL_PAYMENT'] : 0, 
// Если параметр у раздела установлен - берем его значение, иначе пишем ноль
		'LEFT' => $S['LEFT_MARGIN'],
		'RIGHT' => $S['RIGHT_MARGIN']
	);
}

На выходе в массиве $Sections получаем список всех разделов с начальным значением параметров.

Чтобы не терять времени, стек реализуем в виде массива и заведем отдельную переменную для хранения указателя.

// Хранилище стека
$Stack = Array();

// Указатель стека
$k = 0;

// Переменная, которая будет в текущей итерации цикла
// хранить значение RIGHT_MARGIN с элемента предыдущей итерации
$RightPrev = 0; 

// Делаем проход по всему массиву $Sections
foreach($Sections as &$S)
{
	if($S['LEFT_MARGIN'] == 1)
	{
//Начальный раздел, пушим в стэк
		
		$Stack[$k] = $S;
	}
	elseif($S['ADDITIONAL_PAYMENT'] != 0)
// Если у данного раздела есть наценка - то пушим в стэк
	{
		 $k++;
		 $Stack[$k] = $S;
	}
	elseif($S['RIGHT'] > $Stack[$k]['RIGHT'])
// Если предыдущий элемент был с меньшим RightMargin, а текущий - с бОльшим,
// нежели в стеке, то мы заполнили все поддерево относительно вершины стека.
// Делаем Pull
	{
		unset($Stack[$k]);
		$k--;
	}
	
	if($S['ADDITIONAL_PAYMENT'] == 0 && $S['RIGHT'] < $Stack[$k]['RIGHT'])
	{
// Если у текущего раздела наценка не установлена
// и мы внутри поддерева относительно вершины стека, 
// то тут нам нужно обновить данные
		
		$S['ADDITIONAL_PAYMENT'] = $Stack[$k]['ADDITIONAL_PAYMENT'];
	}
	
	$RightPrev = $S['RIGHT'];
}

Результат работы - в том, что для каждого элемента из массива Sections будет проставлено корректное значение параметра UF_ADDITIONAL_PAYMENT, при необходимости подтянутое из родительского раздела. Как именно эти значения использовать дальше - это уже зависит от каждого конкретного проекта.

Аналогичный код задействован на интернет-магазине, в котором на момент тестирования находилось несколько десятков тысяч товаров и несколько тысяч разделов и подразделов.

Преимущества:

  • Минимум запросов к базе.
  • Быстрая обработка даже при достаточно большом количестве разделов

Недостаток:

  • При большом количестве разделов массив $Section может скушать достаточное количество памяти.

На мой взгляд, наиболее подходящая область применения кода - это обработчики событий, агенты и скрипты, выносимые на CRON.

<< К блогу Добавить комментарий

 

comments powered by Disqus

Самые популярные

К прочтению обязательно