@wagwandude

Как сделать добавление товаров в корзину только из одной родительской категории?

Здравствуйте.

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

Допустим, категории:
  • Москва (id: 1)
    • Приборы (id: 2)

  • Санкт-Петербург (id: 3)
    • Инструменты (id: 4)



Если я нахожусь в категории "Приборы" (родительская "Москва") и добавляю товар в корзину, то перейдя в категорию "Инструменты" (родительская "Санкт-Петербург"), и, добавив тамошний товар в корзину, у меня высветиться сообщение об ошибке.

У меня есть идеи, как это сделать в файле /ajax/item.php:
function getParent($id) {
	$tt = CIBlockSection::GetList([], array('ID' => $id));
	$as = $tt->GetNext();

	static $a;

	if ($as['DEPTH_LEVEL'] == 1)
		$a = $as['ID'];
	else
		getParent($as['IBLOCK_SECTION_ID']);

	return $a;
}

$currentElement = CIBlockElement::GetList([], [
	"IBLOCK_ID" => $_REQUEST["iblockID"],
	"ID" => $_REQUEST["item"]
]);

$ob = $currentElement->GetNextElement();
echo getParent($ob->GetFields()['IBLOCK_SECTION_ID']);


Так я получаю ID родительской категории.

Но вот незадача, если товар одновременно добавлен в "Приборы" и "Инструменты", то в роли родительской всегда будет выступать категория "Москва".
Постараюсь объяснить:
  • если в корзину этот товар добавляется из "Приборы", то родительской категорией будет "Москва"
  • если добавляется из "Инструменты" - тоже "Москва"..


В общем, я в замешательстве. Сидел всю ночь, но так и не нашел решения.

Надеюсь на помощь. Заранее спасибо.
  • Вопрос задан
  • 543 просмотра
Решения вопроса 1
gromdron
@gromdron
Работаю с Bitrix24
Давай поговорим немножко о другом.
Предположим, что у тебя следующая структура:

Москва (id: 1)
Приборы (id: 2)
Инструменты (id: 5)
Санкт-Петербург (id: 3)
Инструменты (id: 4)

Если товар принадлежит к категории 5, то он точно из Москвы и соответственно при добавлении товара из id:2 он должен добавиться.
А если товар у тебя есть одновременно и там и там? Ну скажем, товар был добавлен и в id:4 и в id:5 - ситуация может быть и такой. В твоем коде, если товар так был занесен, то при добавлении родительский раздел может быть как СПБ, так и Москва, тогда в одном из городов твоя логика работать не будет, а это неправильно.

Я тут набросал пару примеров и сделал пару допущений:
1) Какой-то товар уже в корзине
2) Ты уже знаешь ID города, товар из которого добавлен в корзину

Весь код (без магии), отвечающий за проверку возможности добавлять товар:
/**
 * @var integer Идентификатор товара из CITY_PRODUCTS_IBLOCK_ID, добавляемого в корзину
 */
$iProductId = 650;

/**
 * @var integer Идентификатор города, товар которого уже добавлен в корзину
 */
$iCityId = 91;

if ( canAddByCity($iProductId, $iCityId) )
{
	echo "Можно добавить";
}
else
{
	echo "Нельзя добавить";
}


Как видишь, в этом коде нет ничего интересного. Вся магия начинается в функции canAddByCity (да-да, написано не оптимально, нужно оптимизировать и т.п. и т.д. но для пояснения сойдет):

/**
 * @var int Уровень вложенности, где находятся города (у меня 2)
 */
define('CITY_DEPTH_LEVEL', 2);

/**
 * @var integer Идентификатор инфоблока, где ищем товары
 */
define('CITY_PRODUCTS_IBLOCK_ID', 5);

/**
 * Возвращает true, если товар $product можно добавить в корзину
 * @param integer $product 
 * @param integer $city 
 * @return boolean
 */
function canAddByCity( $product, $city = 0 )
{
	if ( empty($city) )
	{
		return true;
	}

	if ( !\Bitrix\Main\Loader::IncludeModule('iblock') )
	{
		return false;
	}

	/**
	 * @var array Список категорий верхнего уровня для сравнения
	 */
	$arRootGroups = [];

	/**
	 * @var CDBResult
	 * @see https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/getelementgroups.php
	 */
	$resGroups = \CIBlockElement::GetElementGroups($product, true);

	while( $arGroup = $resGroups->fetch() )
	{
		/**
		 * А вдруг товар без категории и сразу добавлен в город
		 * т.е. без привязки?
		 */
		if ( $arGroup['DEPTH_LEVEL'] == \CITY_DEPTH_LEVEL )
		{
			$arRootGroups[] = $arGroup['ID'];
			continue;
		}

		/**
		 * @todo Запросы в цикле это очень плохо. 
		 * Лучше запомнить где-то разделы и лишние уровни и дополнительным
		 * циклом пройтись после - запросов будет меньше и будет быстрее работать
		 */
		$iRootSectionId = getSectionIdByDepthLevel($arGroup['ID']);

		if ( !empty($iRootSectionId) )
		{
			$arRootGroups[] = $iRootSectionId;
			continue;
		}
	}

	// Какие корневые группы имеет этот товар
	// можно посмотреть через код
	//var_dump($arRootGroups);

	return in_array($city, $arRootGroups);

}

/**
 * Возвращает код родительского раздела $section из инфоблока $iblockId
 * на уровне CITY_DEPTH_LEVEL. В случае отсутствия возвращает 0
 * @param integer $section 
 * @param integer $iblockId 
 * @return integer
 */
function getSectionIdByDepthLevel( $section, $iblockId = \CITY_PRODUCTS_IBLOCK_ID )
{
	$section = intval($section);

	if ( empty($section) )
	{
		return 0;
	}

	if ( empty($iblockId) )
	{
		return 0;
	}

	if ( !\Bitrix\Main\Loader::IncludeModule('iblock') )
	{
		return 0;
	}

	$resChain = \CIBlockSection::GetNavChain($iblockId, $section, ['ID','DEPTH_LEVEL','IBLOCK_SECTION_ID']); 

	while( $arSection = $resChain->fetch() )
	{
		if ( $arSection['DEPTH_LEVEL'] == \CITY_DEPTH_LEVEL)
		{
			return $arSection['ID'];
		}
	}

	return 0;
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
У товара есть основной раздел, к которому он привязан, его можно менять через админку (https://dev.1c-bitrix.ru/learning/course/?COURSE_I... Поэтому получая родительский раздел по товару, вы будете всегда получать один и тот же, основной раздел. Как выше сказали, смотреть нужно на раздел, в котором сейчас пользователь находится, если он не совпадает с основным разделом товара, то такой товар в корзину добавлять не следует
Ответ написан
Комментировать
serginhold
@serginhold
сразу видно уровень типичных "битриксойдов".

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

можно конечно использовать всякие CIBlockElement::GetElementGroups и CIBlockSection::GetNavChain, но это приведет к тьме запросов в цикле, чем больше разделом, тем больше запросов и тем медленнее все работает.

я так понимаю тебе нужно узнать все родительские категории первого уровня, зная только id товара.
смотрим исходники ядра битрикса, и понимаем что
b_iblock_section - таблица разделов
b_iblock_section_element - таблица связи элементов и разделов

способ хранения дерева разделов в битриксе - nested sets
гуглим что это, читаем и разбираемся, поверь это более полезная информация, чем запоминание древнего api битрикса

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

select 
    s.ID
from b_iblock_section_element se
inner join b_iblock_section l
    on l.ID = se.IBLOCK_SECTION_ID
inner join b_iblock_section s
    on s.IBLOCK_ID = l.IBLOCK_ID
    and s.LEFT_MARGIN <= l.LEFT_MARGIN
    and s.RIGHT_MARGIN >= l.RIGHT_MARGIN
    and s.DEPTH_LEVEL = 1
where se.iblock_element_id = 2212954
group by s.ID
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы