Your IP : 18.223.159.143


Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/iblock/lib/model/
Upload File :
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/iblock/lib/model/propertyfeature.php

<?php
namespace Bitrix\Iblock\Model;

use Bitrix\Main;
use Bitrix\Main\Localization\Loc;
use Bitrix\Iblock;

Loc::loadMessages(__FILE__);

class PropertyFeature
{
	const EVENT_ID_FEATURE_LIST = 'OnPropertyFeatureBuildList'; // event name for build feature list

	const FEATURE_ID_LIST_PAGE_SHOW = 'LIST_PAGE_SHOW'; // show property in element list
	const FEATURE_ID_DETAIL_PAGE_SHOW = 'DETAIL_PAGE_SHOW'; // detail page show property

	/**
	 * Add features for new property. Do not check features in database.
	 *
	 * @param int $propertyId	Property id.
	 * @param array $features	Feature list.
	 * @return Main\Entity\Result
	 */
	public static function addFeatures($propertyId, array $features): Main\Entity\Result
	{
		$result = new Main\Entity\Result();

		$propertyId = (int)$propertyId;
		if ($propertyId <= 0)
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_BAD_PROPERTY_ID')
			));
			return $result;
		}

		if (empty($features))
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_EMPTY_FEATURE_LIST')
			));
			return $result;
		}
		$features = self::checkFeatureList($features);
		if ($features === null)
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_BAD_FEATURE_LIST')
			));
			return $result;
		}

		$resultIds = [];
		foreach ($features as $row)
		{
			$row['PROPERTY_ID'] = $propertyId;
			$internalResult = Iblock\PropertyFeatureTable::add($row);
			if (!$internalResult->isSuccess())
			{
				$result->addErrors($internalResult->getErrors());
				return $result;
			}

			$resultIds[] = $internalResult->getId();
		}
		unset($internalResult, $row);

		$result->setData($resultIds);
		return $result;
	}

	/**
	 * Upsert features for exist property.
	 *
	 * @param int $propertyId	Property id.
	 * @param array $features	Feature list.
	 * @return Main\Entity\Result
	 */
	public static function updateFeatures($propertyId, array $features): Main\Entity\Result
	{
		$result = new Main\Entity\Result();

		$propertyId = (int)$propertyId;
		if ($propertyId <= 0)
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_BAD_PROPERTY_ID')
			));
			return $result;
		}

		if (empty($features))
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_EMPTY_FEATURE_LIST')
			));
			return $result;
		}

		$features = self::checkFeatureList($features);
		if ($features === null)
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_BAD_FEATURE_LIST')
			));
			return $result;
		}

		$currentList = [];
		$iterator = Iblock\PropertyFeatureTable::getList([
			'select' => ['*'],
			'filter' => ['=PROPERTY_ID' => $propertyId]
		]);
		while ($row = $iterator->fetch())
		{
			$currentList[self::getIndex($row)] = $row;
		}
		unset($row, $iterator);

		foreach ($features as $index => $row)
		{
			if (isset($currentList[$index]))
			{
				$internalResult = Iblock\PropertyFeatureTable::update(
					$currentList[$index]['ID'],
					['IS_ENABLED' => $row['IS_ENABLED']]
				);
			}
			else
			{
				$row['PROPERTY_ID'] = $propertyId;
				$internalResult = Iblock\PropertyFeatureTable::add($row);
			}
			if (!$internalResult->isSuccess())
			{
				$result->addErrors($internalResult->getErrors());
				return $result;
			}
		}
		unset($internalResult, $index, $row);
		unset($currentList);

		return $result;
	}

	/**
	 * Replace property features.
	 *
	 * @param int $propertyId	Property id.
	 * @param array $features	Feature list.
	 * @return Main\Entity\Result
	 */
	public static function setFeatures($propertyId, array $features): Main\Entity\Result
	{
		$result = new Main\Entity\Result();

		$propertyId = (int)$propertyId;
		if ($propertyId <= 0)
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_BAD_PROPERTY_ID')
			));
			return $result;
		}

		if (empty($features))
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_EMPTY_FEATURE_LIST')
			));
			return $result;
		}
		$features = self::checkFeatureList($features);
		if ($features === null)
		{
			$result->addError(new Main\Error(
				Loc::getMessage('PROPERTY_FEATURE_ERR_BAD_FEATURE_LIST')
			));
			return $result;
		}

		$currentList = [];
		$idList = [];
		$iterator = Iblock\PropertyFeatureTable::getList([
			'select' => ['*'],
			'filter' => ['=PROPERTY_ID' => $propertyId]
		]);
		while ($row = $iterator->fetch())
		{
			$row['ID'] = (int)$row['ID'];
			$currentList[self::getIndex($row)] = $row;
			$idList[$row['ID']] = $row['ID'];
		}
		unset($row, $iterator);

		foreach ($features as $index => $row)
		{
			if (isset($currentList[$index]))
			{
				$internalResult = Iblock\PropertyFeatureTable::update(
					$currentList[$index]['ID'],
					['IS_ENABLED' => $row['IS_ENABLED']]
				);
				if ($internalResult->isSuccess())
					unset($idList[$currentList[$index]['ID']]);
			}
			else
			{
				$row['PROPERTY_ID'] = $propertyId;
				$internalResult = Iblock\PropertyFeatureTable::add($row);
			}
			if (!$internalResult->isSuccess())
			{
				$result->addErrors($internalResult->getErrors());
				return $result;
			}
		}
		unset($internalResult, $index, $row);
		unset($currentList);

		if (!empty($idList))
		{
			$conn = Main\Application::getConnection();
			$helper = $conn->getSqlHelper();
			$query = 'delete from '.$helper->quote(Iblock\PropertyFeatureTable::getTableName()).
				' where '.$helper->quote('PROPERTY_ID').' = '.$propertyId.
				' and '.$helper->quote('ID').' in ('.implode(',', $idList).')';
			$conn->queryExecute($query);
			unset($query, $helper, $conn);
		}
		unset($idList);

		return $result;
	}

	/**
	 * Returns verified list of features for add, update or set.
	 *
	 * @param array $list	Raw features.
	 * @return array|null
	 */
	protected static function checkFeatureList(array $list): ?array
	{
		if (empty($list))
			return null;

		$result = [];
		foreach ($list as $rawRow)
		{
			$row = self::checkFeature($rawRow);
			if ($row === null)
				return null;
			$result[self::getIndex($row)] = $row;
		}
		unset($rawRow);

		return $result;
	}

	/**
	 * Checks feature parameters. Returns normalized data or null.
	 *
	 * @param array $feature	Raw feature parameters.
	 * @return array|null
	 */
	protected static function checkFeature(array $feature): ?array
	{
		if (empty($feature))
			return null;
		if (!isset($feature['MODULE_ID']))
			return null;
		$feature['MODULE_ID'] = trim((string)$feature['MODULE_ID']);
		if ($feature['MODULE_ID'] === '')
			return null;
		if (!isset($feature['FEATURE_ID']))
			return null;
		$feature['FEATURE_ID'] = trim((string)$feature['FEATURE_ID']);
		if ($feature['FEATURE_ID'] === '')
			return null;
		if (!isset($feature['IS_ENABLED']))
			return null;
		$feature['IS_ENABLED'] = trim((string)$feature['IS_ENABLED']);
		if ($feature['IS_ENABLED'] !== 'Y' && $feature['IS_ENABLED'] !== 'N')
			return null;

		return [
			'MODULE_ID' => $feature['MODULE_ID'],
			'FEATURE_ID' => $feature['FEATURE_ID'],
			'IS_ENABLED' => $feature['IS_ENABLED']
		];
	}

	/**
	 * Returns unique feature index for search.
	 *
	 * @param array $feature	Normalize feature parameters.
	 * @return string
	 */
	public static function getIndex(array $feature): string
	{
		return $feature['MODULE_ID'].':'.$feature['FEATURE_ID'];
	}

	/**
	 * Parses the index into parts.
	 *
	 * @param string $index
	 *
	 * @return array|null if index correct, return array in format `[MODULE_ID => , FEATURE_ID =>]`
	 */
	public static function parseIndex(string $index): ?array
	{
		$parts = explode(':', $index);
		if (count($parts) === 2)
		{
			return [
				'MODULE_ID' => $parts[0],
				'FEATURE_ID' => $parts[1],
			];
		}

		return null;
	}

	/**
	 * Build a list of available features for a property.
	 *
	 * @param array $property		Property description.
	 * @param array $description	Additional description.
	 * @return array
	 */
	public static function getPropertyFeatureList(array $property, array $description = []): array
	{
		$result = self::getIblockFeatureList();

		$event = new Main\Event(
			'iblock',
			__CLASS__.'::'.self::EVENT_ID_FEATURE_LIST,
			['property' => $property, 'description' => $description]
		);
		$event->send();
		foreach($event->getResults() as $eventResult)
		{
			if ($eventResult->getType() !== Main\EventResult::SUCCESS)
				continue;
			$list = $eventResult->getParameters();
			if (empty($list) || !is_array($list))
				continue;
			foreach ($list as $item)
			{
				if (empty($item) || !is_array($item))
					continue;
				$item = self::prepareFeatureDescription($item);
				if (empty($item))
					continue;
				$result[] = $item;
			}
			unset($item, $list);
		}
		unset($eventResult);

		return $result;
	}

	/**
	 * Returns iblock properties identifiers (ID or CODE), showed in element list.
	 *
	 * @param int $iblockId			Iblock identifier.
	 * @param array $parameters		Options
	 * 	keys are case sensitive:
	 *		<ul>
	 * 		<li>CODE	Return symbolic code as identifier (Y/N, default N).
	 *		</ul>
	 * @return array|null
	 */
	public static function getListPageShowPropertyCodes($iblockId, array $parameters = []): ?array
	{
		$iblockId = (int)$iblockId;
		if ($iblockId <= 0)
			return null;

		return self::getFilteredPropertyCodes(
			$iblockId,
			[
				'=MODULE_ID' => 'iblock',
				'=FEATURE_ID' => self::FEATURE_ID_LIST_PAGE_SHOW
			],
			$parameters
		);
	}

	/**
	 * Returns iblock properties identifiers (ID or CODE), showed on detail element page.
	 *
	 * @param int $iblockId			Iblock identifier.
	 * @param array $parameters		Options
	 * 	keys are case sensitive:
	 *		<ul>
	 * 		<li>CODE	Return symbolic code as identifier (Y/N, default N).
	 *		</ul>
	 * @return array|null
	 */
	public static function getDetailPageShowPropertyCodes($iblockId, array $parameters = []): ?array
	{
		$iblockId = (int)$iblockId;
		if ($iblockId <= 0)
			return null;

		return self::getFilteredPropertyCodes(
			$iblockId,
			[
				'=MODULE_ID' => 'iblock',
				'=FEATURE_ID' => self::FEATURE_ID_DETAIL_PAGE_SHOW
			],
			$parameters
		);
	}

	/**
	 * @deprecated deprecated since iblock 19.0.0
	 * @see self::getDetailPageShowPropertyCodes
	 *
	 * @param int $iblockId			Iblock identifier.
	 * @param array $parameters		Options
	 * 	keys are case sensitive:
	 *		<ul>
	 * 		<li>CODE	Return symbolic code as identifier (Y/N, default N).
	 *		</ul>
	 * @return array|null
	 */
	public static function getDetailPageShowProperties($iblockId, array $parameters = []): ?array
	{
		return self::getDetailPageShowPropertyCodes($iblockId, $parameters);
	}

	/**
	 * Internal method for getting the list of features by filter (within one information block).
	 *
	 * @param int $iblockId			Iblock identifier.
	 * @param array $filter			Feature filter.
	 * @param array $parameters		Options
	 * 	keys are case sensitive:
	 *		<ul>
	 * 		<li>CODE	Return symbolic code as identifier (Y/N, default N).
	 *		</ul>
	 * @return array|null
	 */
	protected static function getFilteredPropertyCodes(int $iblockId, array $filter, array $parameters = []): ?array
	{
		if (Main\Config\Option::get('iblock', 'property_features_enabled') !== 'Y')
		{
			return null;
		}

		if ($iblockId <= 0)
		{
			return null;
		}
		if (!isset($filter['=MODULE_ID']) || !isset($filter['=FEATURE_ID']))
		{
			return null;
		}

		$getCode = (isset($parameters['CODE']) && $parameters['CODE'] == 'Y');

		$result = [];
		$filter['=PROPERTY.IBLOCK_ID'] = $iblockId;
		$filter['=PROPERTY.ACTIVE'] = 'Y';
		$filter['=IS_ENABLED'] = 'Y';
		$iterator = Iblock\PropertyFeatureTable::getList([
			'select' => [
				'IBLOCK_PROPERTY_ID' => 'PROPERTY.ID',
				'IBLOCK_PROPERTY_CODE' => 'PROPERTY.CODE',
				'IBLOCK_PROPERTY_SORT' => 'PROPERTY.SORT',
			],
			'filter' => $filter,
			'order' => [
				'IBLOCK_PROPERTY_SORT' => 'ASC',
				'IBLOCK_PROPERTY_ID' => 'ASC',
			],
		]);
		while ($row = $iterator->fetch())
		{
			$result[(int)$row['IBLOCK_PROPERTY_ID']] = self::getPropertyCode(
				[
					'ID' => $row['IBLOCK_PROPERTY_ID'],
					'CODE' => $row['IBLOCK_PROPERTY_CODE']
				],
				$getCode
			);
		}
		unset($row, $iterator);
		unset($filter, $getCode);

		return (!empty($result) ? array_values($result) : null);
	}

	/**
	 * Returns property identifier. Internal method.
	 *
	 * @param array $property	Property description (the ID and CODE fields are definitely needed)
	 * @param bool $getCode		if true, returns property code or ID (if CODE is empty). Other - returns ID.
	 * @return string
	 */
	protected static function getPropertyCode(array $property, bool $getCode = false): string
	{
		$property['ID'] = (string)$property['ID'];
		if ($getCode)
		{
			$property['CODE'] = trim((string)$property['CODE']);
			if ($property['CODE'] === '')
			{
				$property['CODE'] = null;
			}

			return ($property['CODE'] ?? $property['ID']);
		}
		else
		{
			return $property['ID'];
		}
	}

	/**
	 * Check and normalize feature description. Internal method.
	 *
	 * @param array $row	Feature description.
	 * @return array
	 */
	private static function prepareFeatureDescription(array $row): array
	{
		if (empty($row))
			return [];
		if (
			!isset($row['MODULE_ID'])
			|| !isset($row['FEATURE_ID'])
			|| !isset($row['FEATURE_NAME'])
		)
			return [];

		return [
			'MODULE_ID' => $row['MODULE_ID'],
			'FEATURE_ID' => $row['FEATURE_ID'],
			'FEATURE_NAME' => $row['FEATURE_NAME']
		];
	}

	/**
	 * Returns a list of features available to any information block.
	 * Used when building a list of features for an information block property.
	 *
	 * @return array
	 */
	private static function getIblockFeatureList(): array
	{
		return [
			[
				'MODULE_ID' => 'iblock',
				'FEATURE_ID' => self::FEATURE_ID_LIST_PAGE_SHOW,
				'FEATURE_NAME' => Loc::getMessage('PROPERTY_FEATURE_NAME_LIST_PAGE_SHOW')
			],
			[
				'MODULE_ID' => 'iblock',
				'FEATURE_ID' => self::FEATURE_ID_DETAIL_PAGE_SHOW,
				'FEATURE_NAME' => Loc::getMessage('PROPERTY_FEATURE_NAME_DETAIL_PAGE_SHOW')
			]
		];
	}

	/**
	 * Returns three if the feature engine is enabled.
	 *
	 * @return bool
	 */
	public static function isEnabledFeatures(): bool
	{
		return (Main\Config\Option::get('iblock', 'property_features_enabled') == 'Y');
	}

	/**
	 * Returns true, if property features exist.
	 *
	 * @return bool
	 */
	public static function isPropertyFeaturesExist(): bool
	{
		return Iblock\PropertyFeatureTable::getCount([], ['ttl' => 86400]) > 0;
	}
}