• Модуль: iblock
  • Путь к файлу: ~/bitrix/modules/iblock/classes/general/iblocksection.php
  • Класс: CAllIBlockSection
  • Вызов: CAllIBlockSection::recountTreeAfterUpdate
static function recountTreeAfterUpdate($arFields, $db_record)
{
	global $DB;

	$ID = $arFields['ID'];

	$move_distance = 0;

	//Move inside the tree
	if ((isset($arFields["SORT"]) && $arFields["SORT"] != $db_record["SORT"])
		|| (isset($arFields["NAME"]) && $arFields["NAME"] != $db_record["NAME"])
		|| (isset($arFields["IBLOCK_SECTION_ID"]) && $arFields["IBLOCK_SECTION_ID"] != $db_record["IBLOCK_SECTION_ID"]))
	{
		//First "delete" from the tree
		$distance = (int)$db_record["RIGHT_MARGIN"] - (int)$db_record["LEFT_MARGIN"] + 1;
		$DB->Query("
			UPDATE b_iblock_section SET
				TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
				,LEFT_MARGIN = -LEFT_MARGIN
				,RIGHT_MARGIN = -RIGHT_MARGIN
			WHERE
				IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
				AND LEFT_MARGIN >= ".(int)$db_record["LEFT_MARGIN"]."
				AND LEFT_MARGIN <= ".(int)$db_record["RIGHT_MARGIN"]."
		");

		$DB->Query("
			UPDATE b_iblock_section SET
				TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
				,RIGHT_MARGIN = RIGHT_MARGIN - ".$distance."
			WHERE
				IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
				AND RIGHT_MARGIN > ".$db_record["RIGHT_MARGIN"]."
		");

		$DB->Query("
			UPDATE b_iblock_section SET
				TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
				,LEFT_MARGIN = LEFT_MARGIN - ".$distance."
			WHERE
				IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
				AND LEFT_MARGIN > ".$db_record["LEFT_MARGIN"]."
		");

		//Next insert into the the tree almost as we do when inserting the new one

		$PARENT_ID = (int)($arFields["IBLOCK_SECTION_ID"] ?? $db_record["IBLOCK_SECTION_ID"]);
		$NAME = $arFields["NAME"] ?? $db_record["NAME"];
		$SORT = (int)($arFields["SORT"] ?? $db_record["SORT"]);

		$arParents = array();
		$strSql = "
			SELECT BS.ID, BS.ACTIVE, BS.GLOBAL_ACTIVE, BS.DEPTH_LEVEL, BS.LEFT_MARGIN, BS.RIGHT_MARGIN
			FROM b_iblock_section BS
			WHERE BS.IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
			AND BS.ID in (".(int)$db_record["IBLOCK_SECTION_ID"].", ".$PARENT_ID.")
		";
		$rsParents = $DB->Query($strSql);
		while ($arParent = $rsParents->Fetch())
		{
			$arParents[$arParent["ID"]] = $arParent;
		}

		//Find rightmost child of the parent
		$strSql = "
			SELECT BS.ID, BS.RIGHT_MARGIN, BS.DEPTH_LEVEL
			FROM b_iblock_section BS
			WHERE BS.IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
			AND ".($PARENT_ID > 0 ? "BS.IBLOCK_SECTION_ID=".$PARENT_ID : "BS.IBLOCK_SECTION_ID IS NULL")."
			AND (
				(BS.SORT < ".$SORT.")
				OR (BS.SORT = ".$SORT." AND BS.NAME < '".$DB->ForSQL($NAME)."')
			)
			AND BS.ID <> ".$ID."
			ORDER BY BS.SORT DESC, BS.NAME DESC
		";
		$rsChild = $DB->Query($strSql);
		if ($arChild = $rsChild->Fetch())
		{
			//We found the left neighbour
			$arUpdate = array(
				"LEFT_MARGIN" => (int)$arChild["RIGHT_MARGIN"] + 1,
				"DEPTH_LEVEL" => (int)$arChild["DEPTH_LEVEL"],
			);
		}
		else
		{
			//If we have parent, when take its left_margin
			if (isset($arParents[$PARENT_ID]) && $arParents[$PARENT_ID])
			{
				$arUpdate = array(
					"LEFT_MARGIN" => (int)$arParents[$PARENT_ID]["LEFT_MARGIN"] + 1,
					"DEPTH_LEVEL" => (int)$arParents[$PARENT_ID]["DEPTH_LEVEL"] + 1,
				);
			}
			else
			{
				//We are only one/leftmost section in the iblock.
				$arUpdate = array(
					"LEFT_MARGIN" => 1,
					"DEPTH_LEVEL" => 1,
				);
			}
		}

		$move_distance = (int)$db_record["LEFT_MARGIN"] - $arUpdate["LEFT_MARGIN"];

		$DB->Query("
			UPDATE b_iblock_section SET
				TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
				,LEFT_MARGIN = LEFT_MARGIN + ".$distance."
				,RIGHT_MARGIN = RIGHT_MARGIN + ".$distance."
			WHERE
				IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
				AND LEFT_MARGIN >= ".$arUpdate["LEFT_MARGIN"]."
		");

		$DB->Query("
			UPDATE b_iblock_section SET
				TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
				,LEFT_MARGIN = -LEFT_MARGIN - ".$move_distance."
				,RIGHT_MARGIN = -RIGHT_MARGIN - ".$move_distance."
				".($arUpdate["DEPTH_LEVEL"] != (int)$db_record["DEPTH_LEVEL"] ? ",DEPTH_LEVEL = DEPTH_LEVEL - ".($db_record["DEPTH_LEVEL"] - $arUpdate["DEPTH_LEVEL"]) : "")."
			WHERE
				IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
				AND LEFT_MARGIN <= ".(-(int)$db_record["LEFT_MARGIN"])."
				AND LEFT_MARGIN >= ".(-(int)$db_record["RIGHT_MARGIN"])."
		");

		if (isset($arParents[$PARENT_ID]))
		{
			$DB->Query("
				UPDATE b_iblock_section SET
					TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
					,RIGHT_MARGIN = RIGHT_MARGIN + ".$distance."
				WHERE
					IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
					AND LEFT_MARGIN <= ".$arParents[$PARENT_ID]["LEFT_MARGIN"]."
					AND RIGHT_MARGIN >= ".$arParents[$PARENT_ID]["RIGHT_MARGIN"]."
			");
		}
	}

	//Check if parent was changed
	if (isset($arFields["IBLOCK_SECTION_ID"]) && $arFields["IBLOCK_SECTION_ID"] != $db_record["IBLOCK_SECTION_ID"])
	{
		$rsSection = CIBlockSection::GetList(
			array(),
			array("ID" => $ID, "CHECK_PERMISSIONS" => "N"),
			false,
			array(
				"ID", "IBLOCK_ID", "IBLOCK_SECTION_ID",
				"LEFT_MARGIN", "RIGHT_MARGIN",
				"ACTIVE", "GLOBAL_ACTIVE"
			)
		);

		$arSection = $rsSection->Fetch();
		unset($rsSection);

		$strSql = "
			SELECT ID, GLOBAL_ACTIVE
			FROM b_iblock_section
			WHERE IBLOCK_ID = ".$arSection["IBLOCK_ID"]."
			AND ID = ".(int)$arFields["IBLOCK_SECTION_ID"]."
		";
		$rsParent = $DB->Query($strSql);
		$arParent = $rsParent->Fetch();

		//If new parent is not globally active
		//or we are not active either
		//we must be not globally active too
		if (($arParent && $arParent["GLOBAL_ACTIVE"] == "N") || ($arFields["ACTIVE"] == "N"))
		{
			$DB->Query("
				UPDATE b_iblock_section SET
					TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
					,GLOBAL_ACTIVE = 'N'
				WHERE
					IBLOCK_ID = ".$arSection["IBLOCK_ID"]."
					AND LEFT_MARGIN >= ".(int)$arSection["LEFT_MARGIN"]."
					AND RIGHT_MARGIN <= ".(int)$arSection["RIGHT_MARGIN"]."
			");
		}
		//New parent is globally active
		//And we WAS NOT active
		//But is going to be
		elseif ($arSection["ACTIVE"] == "N" && $arFields["ACTIVE"] == "Y")
		{
			static::RecalcGlobalActiveFlag($arSection);
		}
		//New parent is globally active
		//And we WAS active but NOT globally active
		//But is going to be
		elseif (
			(!$arParent || $arParent["GLOBAL_ACTIVE"] == "Y")
			&& $arSection["GLOBAL_ACTIVE"] == "N"
			&& ($arSection["ACTIVE"] == "Y" || $arFields["ACTIVE"] == "Y")
		)
		{
			static::RecalcGlobalActiveFlag($arSection);
		}
		//Otherwise we may not to change anything
	}
	//Parent not changed
	//but we are going to change activity flag
	elseif (isset($arFields["ACTIVE"]) && $arFields["ACTIVE"] != $db_record["ACTIVE"])
	{
		//Make all children globally inactive
		if ($arFields["ACTIVE"] == "N")
		{
			$DB->Query("
				UPDATE b_iblock_section SET
					TIMESTAMP_X=".($DB->type == "ORACLE" ? "NULL" : "TIMESTAMP_X")."
					,GLOBAL_ACTIVE = 'N'
				WHERE
					IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
					AND LEFT_MARGIN >= ".(int)$db_record["LEFT_MARGIN"]."
					AND RIGHT_MARGIN <= ".(int)$db_record["RIGHT_MARGIN"]."
			");
		}
		else
		{
			//Check for parent activity
			$strSql = "
				SELECT ID, GLOBAL_ACTIVE
				FROM b_iblock_section
				WHERE IBLOCK_ID = ".$db_record["IBLOCK_ID"]."
				AND ID = ".(int)$db_record["IBLOCK_SECTION_ID"]."
			";
			$rsParent = $DB->Query($strSql);
			$arParent = $rsParent->Fetch();

			//Parent is active
			//and we changed
			//so need to recalc
			if (!$arParent || $arParent["GLOBAL_ACTIVE"] == "Y")
			{
				static::RecalcGlobalActiveFlag($db_record, -$move_distance);
			}
		}
	}
}