Your IP : 3.148.210.23


Current Path : /var/www/axolotl/data/www/yar.axolotls.ru/bitrix/modules/disk/lib/internals/
Upload File :
Current File : /var/www/axolotl/data/www/yar.axolotls.ru/bitrix/modules/disk/lib/internals/objectpath.php

<?php
namespace Bitrix\Disk\Internals;

use Bitrix\Main\Application;
use Bitrix\Main\ArgumentOutOfRangeException;
use Bitrix\Main\Entity;
use Bitrix\Main\Type\Collection;

/**
 * Class ObjectPathTable
 *
 * Fields:
 * <ul>
 * <li> ID int mandatory
 * <li> PARENT_ID int mandatory
 * <li> OBJECT_ID int mandatory
 * <li> DEPTH_LEVEL int optional
 * </ul>
 *
 * @package Bitrix\Disk
 **/

final class ObjectPathTable extends DataManager
{
	public static function getTableName()
	{
		return 'b_disk_object_path';
	}

	public static function getMap()
	{
		return array(
			'ID' => array(
				'data_type' => 'integer',
				'primary' => true,
				'autocomplete' => true,
			),
			'PARENT_ID' => array(
				'data_type' => 'integer',
			),
			'OBJECT_ID' => array(
				'data_type' => 'integer',
			),
			'DEPTH_LEVEL' => array(
				'data_type' => 'integer',
			),
		);
	}

	/**
	 * Create root in closure.
	 * @param $objectId
	 * @throws \Bitrix\Main\Config\ConfigurationException
	 * @throws \Exception
	 * @return Entity\AddResult
	 */
	public static function addAsRoot($objectId)
	{
		$objectId = (int)$objectId;

		return static::add(array(
			'PARENT_ID' => $objectId,
			'OBJECT_ID' => $objectId,
			'DEPTH_LEVEL' => 0,
		));
	}

	/**
	 * Append target to node,
	 * @param $objectId
	 * @param $appendToNodeId
	 * @throws \Bitrix\Main\Config\ConfigurationException
	 * @throws \Bitrix\Main\ArgumentTypeException
	 * @throws \Bitrix\Main\ArgumentNullException
	 */
	public static function appendTo($objectId, $appendToNodeId)
	{
		$objectId = (int)$objectId;
		$appendToNodeId = (int)$appendToNodeId;

		$table = static::getTableName();
		$sql = "
			INSERT INTO {$table} (PARENT_ID, OBJECT_ID, DEPTH_LEVEL)
			SELECT PARENT_ID, {$objectId}, DEPTH_LEVEL+1 FROM {$table} WHERE OBJECT_ID = {$appendToNodeId}
			UNION ALL SELECT {$objectId}, {$objectId}, 0
		";

		//todo? return nothing? Or return AddResult? GetId?
		$connection = Application::getInstance()->getConnection();
		$connection->queryExecute($sql);
	}

	/**
	 * Action move object to another node.
	 * Recalculated paths.
	 * @param $objectId
	 * @param $toNodeId
	 * @throws \Bitrix\Main\Config\ConfigurationException
	 * @throws \Bitrix\Main\ArgumentTypeException
	 * @throws \Bitrix\Main\ArgumentNullException
	 */
	public static function moveTo($objectId, $toNodeId)
	{
		$objectId = (int)$objectId;
		$toNodeId = (int)$toNodeId;

		$connection = Application::getInstance()->getConnection();
		$table = static::getTableName();

		$sql = "
			DELETE a FROM {$table} a
				JOIN {$table} d
					ON a.OBJECT_ID = d.OBJECT_ID
				LEFT JOIN {$table} x
					ON x.PARENT_ID = d.PARENT_ID AND x.OBJECT_ID = a.PARENT_ID
				WHERE d.PARENT_ID = {$objectId} AND x.PARENT_ID IS NULL
		";

		$connection->queryExecute($sql);

		$sql = "
			INSERT INTO {$table} (PARENT_ID, OBJECT_ID, DEPTH_LEVEL)
				SELECT stree.PARENT_ID, subtree.OBJECT_ID, stree.DEPTH_LEVEL+subtree.DEPTH_LEVEL+1
				FROM {$table} stree JOIN {$table} subtree ON subtree.PARENT_ID = {$objectId} AND stree.OBJECT_ID = {$toNodeId}
		";

		$connection->queryExecute($sql);
	}

	/**
	 * Get descendants objects of $objectId.
	 * PARENT_ID not really! Be careful :)
	 * @param     $objectId
	 * @param int $orderDepthLevel SORT_ASC | SORT_DESC
	 * @throws \Bitrix\Main\ArgumentOutOfRangeException
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Exception
	 * @return array
	 */
	public static function getDescendants($objectId, $orderDepthLevel = SORT_ASC)
	{
		$objectId = (int)$objectId;

		$objectPaths = static::getList(array(
			'select' => array('ID', 'PARENT_ID', 'OBJECT_ID', 'DEPTH_LEVEL'),
			'filter' => array(
				'PARENT_ID' => $objectId,
				'!=OBJECT_ID' => $objectId,
			),
		))->fetchAll();

		Collection::sortByColumn($objectPaths, array('DEPTH_LEVEL' => $orderDepthLevel));

		return $objectPaths;
	}

	/**
	 * Get direct children objects of $objectId.
	 * @param $objectId
	 * @return array
	 * @throws \Bitrix\Main\ArgumentException
	 */
	public static function getChildren($objectId)
	{
		$objectId = (int)$objectId;

		$objectPaths = static::getList(array(
			'select' => array('ID', 'PARENT_ID', 'OBJECT_ID', 'DEPTH_LEVEL'),
			'filter' => array(
				'PARENT_ID' => $objectId,
				'DEPTH_LEVEL' => 1,
			),
		))->fetchAll();

		return $objectPaths;
	}

	/**
	 * Get ancestors objects of $objectId.
	 * PARENT_ID not really! Be careful :)
	 * @param     $objectId
	 * @param int $orderDepthLevel SORT_ASC | SORT_DESC
	 * @throws \Bitrix\Main\ArgumentOutOfRangeException
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Exception
	 * @return array
	 */
	public static function getAncestors($objectId, $orderDepthLevel = SORT_ASC)
	{
		$objectId = (int)$objectId;

		$objectPaths = static::getList(array(
			'select' => array('ID', 'PARENT_ID', 'OBJECT_ID', 'DEPTH_LEVEL'),
			'filter' => array(
				'OBJECT_ID' => $objectId,
				'!=PARENT_ID' => $objectId,
			),
		))->fetchAll();

		Collection::sortByColumn($objectPaths, array('DEPTH_LEVEL' => $orderDepthLevel));

		return $objectPaths;
	}

	public static function deleteByObject($objectId)
	{
		//todo Is leaf? If this is not leaf - throw Exception?
		$objectId = (int)$objectId;

		$table = static::getTableName();
		$sql = "DELETE FROM {$table} WHERE OBJECT_ID = {$objectId}";

		Application::getInstance()->getConnection()->queryExecute($sql);
	}

	/**
	 * Object is leaf?
	 * @param $objectId
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Exception
	 * @return bool
	 */
	public static function isLeaf($objectId)
	{
		$objectId = (int)$objectId;

		$paths = static::getList(array(
			'select' => array('ID'),
			'filter' => array(
				'!=OBJECT_ID' => $objectId,
				'PARENT_ID' => $objectId,
			),
			'limit' => 1,
		))->fetch();

		return empty($paths);
	}

	/**
	 * Recalculate object paths by parentId in Object.
	 * Uses inner joins to find relations, without recursive walks by parentId.
	 * Attention! This method truncate table b_disk_object_path and recalculates them.
	 * @internal
	 */
	public static function recalculate()
	{
		set_time_limit(0);
		$maxInnerJoinDepth = 32;
		$currentDepth = 0;
		$emptyInsert = false;
		$connection = Application::getConnection();

		$connection->queryExecute("TRUNCATE TABLE b_disk_object_path");
		$connection->queryExecute("
			INSERT INTO b_disk_object_path (PARENT_ID, OBJECT_ID, DEPTH_LEVEL)
			SELECT ID, ID, 0 FROM b_disk_object
		");

		while($currentDepth < $maxInnerJoinDepth && !$emptyInsert)
		{
			$query = "
				INSERT INTO b_disk_object_path (OBJECT_ID, PARENT_ID, DEPTH_LEVEL)
					SELECT b.ID, t.ID, " . ($currentDepth+1) . " FROM b_disk_object t
			";

			$finalQuery = $query;
			for($i = 0;$i < $currentDepth;$i++)
			{
				$finalQuery .= " INNER JOIN b_disk_object t" . ($i+1) . " ON t" . ($i?: '') . ".ID=t" . ($i+1) . ".PARENT_ID ";

			}
			$lastJoin = " INNER JOIN b_disk_object b ON t" . ($currentDepth?:'' ) . ".ID=b.PARENT_ID ";
			$finalQuery = $finalQuery .$lastJoin;

			$connection->queryExecute($finalQuery);
			$emptyInsert = $connection->getAffectedRowsCount() <= 0;

			$currentDepth++;
		}
	}

	/**
	 * Recalculates paths for the storage.
	 *
	 * @param int $storageId Storage id.
	 * @throws ArgumentOutOfRangeException
	 * @return void
	 */
	public static function recalculateByStorage($storageId)
	{
		$storageId = (int)$storageId;
		if ($storageId <= 0)
		{
			throw new ArgumentOutOfRangeException('storageId');
		}

		set_time_limit(0);
		$maxInnerJoinDepth = 32;
		$currentDepth = 0;
		$emptyInsert = false;
		$connection = Application::getConnection();

		$connection->queryExecute("
			DELETE p
			FROM b_disk_object_path p
			INNER JOIN b_disk_object object ON object.ID = p.OBJECT_ID
			WHERE object.STORAGE_ID = {$storageId}
		");
		$connection->queryExecute("
			DELETE p
			FROM b_disk_object_path p
			INNER JOIN b_disk_object object_p ON object_p.ID = p.PARENT_ID
			WHERE object_p.STORAGE_ID = {$storageId}
		");

		$connection->queryExecute("
			INSERT INTO b_disk_object_path (PARENT_ID, OBJECT_ID, DEPTH_LEVEL)
			SELECT ID, ID, 0 FROM b_disk_object WHERE STORAGE_ID = {$storageId}
		");

		while($currentDepth < $maxInnerJoinDepth && !$emptyInsert)
		{
			$query = "
				INSERT INTO b_disk_object_path (OBJECT_ID, PARENT_ID, DEPTH_LEVEL)
					SELECT b.ID, t.ID, " . ($currentDepth+1) . " FROM b_disk_object t
			";

			$finalQuery = $query;
			for($i = 0;$i < $currentDepth;$i++)
			{
				$finalQuery .= " INNER JOIN b_disk_object t" . ($i+1) . " ON t" . ($i?: '') . ".ID=t" . ($i+1) . ".PARENT_ID ";

			}
			$lastJoin = " INNER JOIN b_disk_object b ON t" . ($currentDepth?:'' ) . ".ID=b.PARENT_ID ";
			$finalQuery = $finalQuery . $lastJoin . " WHERE t.STORAGE_ID = {$storageId}";

			$connection->queryExecute($finalQuery);
			$emptyInsert = $connection->getAffectedRowsCount() <= 0;

			$currentDepth++;
		}
	}
}