W
W
wagwandude2018-04-16 05:02:28
1C-Bitrix
wagwandude, 2018-04-16 05:02:28

How to make adding products to cart only from one parent category?

Hello.
It is necessary to make sure that only products from the main parent category can be added to the cart.
At the same time, take into account that a product can be added to several subcategories at once, which are located in different parent categories.
Let's say the categories are:

  • Moscow (id: 1)
    • Devices (id: 2)

  • St. Petersburg (id: 3)
    • Tools (id: 4)


If I am in the "Devices" category (parent "Moscow") and add an item to the cart, then going to the "Tools" category (parent "St. Petersburg"), and adding the local item to the cart, I will get an error message .
I have ideas how to do it in /ajax/item.php file:
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']);

This is how I get the ID of the parent category.
But it's bad luck, if the product is simultaneously added to "Devices" and "Tools", then the category "Moscow" will always act as the parent.
I'll try to explain:
  • if this product is added to the cart from "Devices", then the parent category will be "Moscow"
  • if added from "Tools" - also "Moscow" ..

In general, I'm confused. I sat all night, but did not find a solution.
Hope for help. Thanks in advance.

Answer the question

In order to leave comments, you need to log in

3 answer(s)
A
Andrey Nikolaev, 2018-04-16
@wagwandude

Let's talk a little about something else.
Suppose you have the following structure:
Moscow (id: 1)
Devices (id: 2)
Tools (id: 5)
St. Petersburg (id: 3)
Tools (id: 4)
If a product belongs to category 5, then it is from Moscow and, accordingly, when adding a product from id: 2, it should be added.
And if you have the goods at the same time and there and there? Well, let's say the product was added to both id:4 and id:5 - the situation can be like this. In your code, if the product was listed in this way, then when adding the parent section, it can be both St. Petersburg and Moscow, then your logic will not work in one of the cities, and this is wrong.
I sketched a couple of examples here and made a couple of assumptions:
1) Some product is already in the cart
2) You already know the ID of the city from which the product was added to the cart
The whole code (without magic) responsible for checking the ability to add a product:

/**
 * @var integer Идентификатор товара из CITY_PRODUCTS_IBLOCK_ID, добавляемого в корзину
 */
$iProductId = 650;

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

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

As you can see, there is nothing interesting in this code. All the magic starts in the canAddByCity function (yes, it’s not written optimally, it needs to be optimized, etc., etc., but it’s fine for explanation):
/**
 * @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;
}

D
Dmitry, 2018-04-16
@mrsoul

The product has a main section to which it is attached, it can be changed through the admin panel ( https://dev.1c-bitrix.ru/learning/course/?COURSE_I... Therefore, when you receive a parent section for a product, you will always receive one and the same main section.As mentioned above, you need to look at the section in which the user is now, if it does not match the main section of the product, then such a product should not be added to the basket

S
serginhold, 2018-04-16
@serginhold

you can immediately see the level of typical "bitrixoids".
in general, as I understand it, ajax, adding to the cart, and you know nothing but the id of the product in the script.
Of course, you can use all sorts of CIBlockElement::GetElementGroups and CIBlockSection::GetNavChain, but this will lead to a lot of requests in a cycle, the larger the section, the more requests and the slower everything works.
I understand that you need to find out all the parent categories of the first level, knowing only the product id.
we look at the sources of the Bitrix core, and we understand that
b_iblock_section is a section table
b_iblock_section_element is a table of elements and sections links
Way to store the section tree in Bitrix - nested sets
google what it is, read it and figure it out, believe me, this is more useful information than remembering the ancient Bitrix api,
and having figured it out, with one request from the database we get all the first-level categories to which the product is linked, even if it is not tied specifically to these categories, and is tied to subcategories of these categories of the first level

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

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question