Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/ilovecveti.ru/bitrix/modules/catalog/lib/restview/ |
Upload File : |
<?php namespace Bitrix\Catalog\RestView; use Bitrix\Catalog; use Bitrix\Catalog\ProductTable; use Bitrix\Iblock; use Bitrix\Iblock\PropertyTable; use Bitrix\Main\Config\Option; use Bitrix\Main\Engine\Response\Converter; use Bitrix\Main\Error; use Bitrix\Main\Localization\Loc; use Bitrix\Main\ORM\Fields\ScalarField; use Bitrix\Main\Result; use Bitrix\Main\Type\Date; use Bitrix\Main\Type\DateTime; use Bitrix\Rest\Integration\View\Attributes; use Bitrix\Rest\Integration\View\Base; use Bitrix\Rest\Integration\View\DataType; final class Product extends Base { public const BOOLEAN_VALUE_YES = 'Y'; public const BOOLEAN_VALUE_NO = 'N'; private array $productFieldNames = []; /** * @return array * return fields all type product */ public function getFields(): array { $this->loadFieldNames(); return array_merge($this->getFieldsIBlockElement(), $this->getFieldsCatalogProduct()); } /** * @param array $info * @param array $attributs * @return array */ protected function prepareFieldAttributs($info, $attributs): array { return EntityFieldType::prepareProductField( parent::prepareFieldAttributs($info, $attributs), $info, $attributs ); } /** * @return array */ private function getFieldsIBlockElement(): array { $fieldList = [ 'ID' => [ 'TYPE' => DataType::TYPE_INT, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'CREATED_BY' => [ 'TYPE' => DataType::TYPE_INT, ], 'DATE_CREATE' => [ 'TYPE' => DataType::TYPE_DATETIME, ], 'MODIFIED_BY' => [ 'TYPE' => DataType::TYPE_INT, ], 'TIMESTAMP_X' => [ 'TYPE' => DataType::TYPE_DATETIME, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'ACTIVE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'DATE_ACTIVE_FROM' => [ 'TYPE' => DataType::TYPE_DATETIME, ], 'DATE_ACTIVE_TO' => [ 'TYPE' => DataType::TYPE_DATETIME, ], 'NAME' => [ 'TYPE' => DataType::TYPE_STRING, 'ATTRIBUTES' => [ Attributes::REQUIRED_ADD, ], ], 'CODE' => [ 'TYPE' => DataType::TYPE_STRING, ], 'SORT' => [ 'TYPE' => DataType::TYPE_INT, ], 'PREVIEW_TEXT' => [ 'TYPE' => DataType::TYPE_STRING, ], 'PREVIEW_TEXT_TYPE' => [ 'TYPE' => DataType::TYPE_STRING, ], 'PREVIEW_PICTURE' => [ 'TYPE' => DataType::TYPE_FILE, ], 'DETAIL_TEXT' => [ 'TYPE' => DataType::TYPE_STRING, ], 'DETAIL_TEXT_TYPE' => [ 'TYPE' => DataType::TYPE_STRING, ], 'DETAIL_PICTURE' => [ 'TYPE' => DataType::TYPE_FILE, ], 'IBLOCK_ID' => [ 'TYPE' => DataType::TYPE_INT, 'ATTRIBUTES' => [ Attributes::REQUIRED, Attributes::IMMUTABLE, ], ], 'IBLOCK_SECTION_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'IBLOCK_SECTION' => [ 'TYPE' => DataType::TYPE_LIST, ], 'XML_ID' => [ 'TYPE' => DataType::TYPE_STRING, ], ]; return $this->fillFieldNames($fieldList); } /** * @param array $filter * @return Result */ private function getFieldsIBlockPropertyValuesByFilter(array $filter): Result { $result = new Result(); $fieldsInfo = []; $iblockId = (int)($filter['IBLOCK_ID'] ?? 0); if ($iblockId <= 0) { $result->addError(new Error('parameter - iblockId is empty')); } if ($result->isSuccess()) { $catalogInfo = \CCatalogSku::GetInfoByOfferIBlock($iblockId); $skuPropertyId = $catalogInfo['SKU_PROPERTY_ID'] ?? null; unset($catalogInfo); $allowedTypes = array_fill_keys(self::getUserType(), true); $cache = [ 'ttl' => 86400, ]; $iterator = PropertyTable::getList([ 'select' => [ 'ID', 'IBLOCK_ID', 'NAME', 'SORT', 'PROPERTY_TYPE', 'LIST_TYPE', 'MULTIPLE', 'LINK_IBLOCK_ID', 'IS_REQUIRED', 'USER_TYPE', ], 'filter' => [ '=IBLOCK_ID' => $iblockId, '=ACTIVE' => 'Y', ], 'order' => [ 'SORT' => 'ASC', 'ID' => 'ASC', ], 'cache' => $cache, ]); while ($property = $iterator->fetch()) { $property['ID'] = (int)$property['ID']; $userType = (string)$property['USER_TYPE']; if ( $userType !== '' && !isset($allowedTypes[$property['PROPERTY_TYPE'] . ':' . $userType]) ) { continue; } $info = [ 'TYPE' => EntityFieldType::PRODUCT_PROPERTY, 'PROPERTY_TYPE' => $property['PROPERTY_TYPE'], 'USER_TYPE' => $property['USER_TYPE'], 'ATTRIBUTES' => [Attributes::DYNAMIC], 'NAME' => $property['NAME'], ]; if ($property['MULTIPLE'] === 'Y') { $info['ATTRIBUTES'][] = Attributes::MULTIPLE; } if ($property['IS_REQUIRED'] === 'Y') { $info['ATTRIBUTES'][] = Attributes::REQUIRED; } if ( $property['PROPERTY_TYPE'] === PropertyTable::TYPE_LIST && $userType === '' && $property['MULTIPLE'] === 'N' ) { $enumFilter = [ '=PROPERTY_ID' => $property['ID'], ]; if (Iblock\PropertyEnumerationTable::getCount($enumFilter, $cache) === 1) { $variant = Iblock\PropertyEnumerationTable::getRow([ 'select' => [ 'ID', 'PROPERTY_ID', 'VALUE', ], 'filter' => $enumFilter, 'cache' => $cache, ]); $info['BOOLEAN_VALUE_YES'] = [ 'ID' => $variant['ID'], 'VALUE' => $variant['VALUE'], ]; } } if ($this->isPropertyBoolean($info)) { $info['USER_TYPE'] = Catalog\Controller\Enum::PROPERTY_USER_TYPE_BOOL_ENUM; } $canonicalName = 'PROPERTY_' . $property['ID']; if ($property['ID'] === $skuPropertyId) { $info['CANONICAL_NAME'] = $canonicalName; $fieldsInfo['PARENT_ID'] = $info; } else { $fieldsInfo[$canonicalName] = $info; } unset($canonicalName); } unset($property, $iterator); $fieldsInfo['PROPERTY_*'] = [ 'TYPE' => EntityFieldType::PRODUCT_PROPERTY, 'ATTRIBUTES' => [ Attributes::READONLY, Attributes::DYNAMIC, ], ]; $result->setData($fieldsInfo); } return $result; } /** * @return array */ private function getFieldsCatalogProductCommonFields(): array { $fieldList = [ 'ID' => [ 'TYPE' => DataType::TYPE_INT, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'TIMESTAMP_X' => [ 'TYPE' => DataType::TYPE_DATETIME, ], 'PRICE_TYPE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'TYPE' => [ 'TYPE' => DataType::TYPE_INT, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'BUNDLE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], ]; return $this->fillFieldNames($fieldList); } public function isAllowedProductTypeByIBlockId($productTypeId, $iblockId): Result { $result = $this->getCatalogDescription((int)$iblockId); if (!$result->isSuccess()) { return $result; } $iblockData = $result->getData(); $allowedTypes = self::getProductTypes($iblockData['CATALOG_TYPE']); if (!isset($allowedTypes[$productTypeId])) { $result->addError(new Error('productType is not allowed for this catalog')); } return $result; } /** * Returns catalog description, if exists. * * @param int $iblockId * @return Result */ public function getCatalogDescription(int $iblockId): Result { $result = new Result(); $iblockData = \CCatalogSku::GetInfoByIBlock($iblockId); if (empty($iblockData)) { $result->addError(new Error('iblock is not catalog')); } else { $result->setData($iblockData); } return $result; } /** * @param array $filter * @return Result */ private function getFieldsCatalogProductByFilter(array $filter): Result { $result = new Result(); $iblockId = (int)($filter['IBLOCK_ID'] ?? 0); $productTypeId = (int)($filter['PRODUCT_TYPE'] ?? 0); if ($iblockId <= 0) { $result->addError(new Error('parameter - iblockId is empty')); } if ($productTypeId <= 0) { $result->addError(new Error('parameter - productType is empty')); } if ($result->isSuccess()) { $r = $this->isAllowedProductTypeByIBlockId($productTypeId, $iblockId); if ($r->isSuccess()) { $result->setData($this->getFieldsCatalogProductByType($productTypeId)); } else { $result->addErrors($r->getErrors()); } } return $result; } /** * @return array */ private function getFieldsCatalogProduct(): array { $fieldList = [ 'TYPE' => [ 'TYPE' => DataType::TYPE_INT, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'AVAILABLE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'BUNDLE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'QUANTITY' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'QUANTITY_RESERVED' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'QUANTITY_TRACE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'CAN_BUY_ZERO' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'SUBSCRIBE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'VAT_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'VAT_INCLUDED' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'PURCHASING_PRICE' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'PURCHASING_CURRENCY' => [ 'TYPE' => DataType::TYPE_STRING, ], 'BARCODE_MULTI' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'WEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'LENGTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'WIDTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'HEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'MEASURE' => [ 'TYPE' => DataType::TYPE_INT, ], 'RECUR_SCHEME_LENGTH' => [ 'TYPE' => DataType::TYPE_INT, ], 'RECUR_SCHEME_TYPE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'TRIAL_PRICE_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'WITHOUT_ORDER' => [ 'TYPE' => DataType::TYPE_CHAR, ], ]; if (Catalog\Config\State::isUsedInventoryManagement()) { $lockFields = [ 'QUANTITY', 'QUANTITY_RESERVED', 'PURCHASING_PRICE', 'PURCHASING_CURRENCY', ]; foreach ($lockFields as $fieldName) { if (!isset($fieldList[$fieldName]['ATTRIBUTES'])) { $fieldList[$fieldName]['ATTRIBUTES'] = [ Attributes::READONLY, ]; } else { $fieldList[$fieldName]['ATTRIBUTES'][] = Attributes::READONLY; $fieldList[$fieldName]['ATTRIBUTES'] = array_unique($fieldList[$fieldName]['ATTRIBUTES']); } } } return $this->fillFieldNames($fieldList); } /** * @param int $id * @return array */ private function getFieldsCatalogProductByType(int $id): array { return match ($id) { ProductTable::TYPE_SERVICE => $this->getFieldsCatalogProductByTypeService(), ProductTable::TYPE_PRODUCT => $this->getFieldsCatalogProductByTypeProduct(), ProductTable::TYPE_SET => $this->getFieldsCatalogProductByTypeSet(), ProductTable::TYPE_SKU, ProductTable::TYPE_EMPTY_SKU => $this->getFieldsCatalogProductByTypeSKU(), ProductTable::TYPE_OFFER, ProductTable::TYPE_FREE_OFFER => $this->getFieldsCatalogProductByTypeOffer(), default => [], }; } /** * @return array */ private function getFieldsCatalogProductByTypeService(): array { $fieldList = [ 'AVAILABLE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'MEASURE' => [ 'TYPE' => DataType::TYPE_INT, ], 'VAT_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'VAT_INCLUDED' => [ 'TYPE' => DataType::TYPE_CHAR, ], ]; return $this->fillFieldNames($fieldList); } /** * @return array */ private function getFieldsCatalogProductByTypeProduct(): array { $fieldList = [ 'AVAILABLE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'PURCHASING_PRICE' => [ 'TYPE' => DataType::TYPE_STRING, ], 'PURCHASING_CURRENCY' => [ 'TYPE' => DataType::TYPE_STRING, ], 'VAT_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'VAT_INCLUDED' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'QUANTITY' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'QUANTITY_RESERVED' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'MEASURE' => [ 'TYPE' => DataType::TYPE_INT, ], 'QUANTITY_TRACE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'CAN_BUY_ZERO' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'NEGATIVE_AMOUNT_TRACE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'SUBSCRIBE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'WEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'LENGTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'WIDTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'HEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'BARCODE_MULTI' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'RECUR_SCHEME_LENGTH' => [ 'TYPE' => DataType::TYPE_INT, ], 'RECUR_SCHEME_TYPE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'TRIAL_PRICE_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'WITHOUT_ORDER' => [ 'TYPE' => DataType::TYPE_CHAR, ], ]; return $this->fillFieldNames($fieldList); } /** * @return array */ private function getFieldsCatalogProductByTypeSKU(): array { $fieldList = [ 'AVAILABLE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], ]; if (Option::get('catalog', 'show_catalog_tab_with_offers') === 'Y') { $fieldListCatalogTabWithOffers = [ 'PURCHASING_PRICE' => [ 'TYPE' => DataType::TYPE_STRING, ], 'PURCHASING_CURRENCY' => [ 'TYPE' => DataType::TYPE_STRING, ], 'VAT_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'VAT_INCLUDED' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'QUANTITY' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'MEASURE' => [ 'TYPE' => DataType::TYPE_INT, ], 'CAN_BUY_ZERO' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'SUBSCRIBE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'WEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'LENGTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'WIDTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'HEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, ], ]; $fieldList = array_merge($fieldList, $fieldListCatalogTabWithOffers); } return $this->fillFieldNames($fieldList); } /** * @return array */ private function getFieldsCatalogProductByTypeOffer(): array { return $this->getFieldsCatalogProductByTypeProduct(); } /** * @return array */ private function getFieldsCatalogProductByTypeSet(): array { $fieldList = [ 'AVAILABLE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'PURCHASING_PRICE' => [ 'TYPE' => DataType::TYPE_STRING, ], 'PURCHASING_CURRENCY' => [ 'TYPE' => DataType::TYPE_STRING, ], 'VAT_ID' => [ 'TYPE' => DataType::TYPE_INT, ], 'VAT_INCLUDED' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'QUANTITY' => [ 'TYPE' => DataType::TYPE_FLOAT, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'MEASURE' => [ 'TYPE' => DataType::TYPE_INT, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'QUANTITY_TRACE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'CAN_BUY_ZERO' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'NEGATIVE_AMOUNT_TRACE' => [ 'TYPE' => DataType::TYPE_CHAR, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'SUBSCRIBE' => [ 'TYPE' => DataType::TYPE_CHAR, ], 'WEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, 'ATTRIBUTES' => [ Attributes::READONLY, ], ], 'LENGTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'WIDTH' => [ 'TYPE' => DataType::TYPE_FLOAT, ], 'HEIGHT' => [ 'TYPE' => DataType::TYPE_FLOAT, ], ]; return $this->fillFieldNames($fieldList); } /** * @param array $filter * @return Result */ public function getFieldsByFilter(array $filter): Result { $result = new Result(); $iblockId = (int)($filter['IBLOCK_ID'] ?? 0); $productTypeId = (int)($filter['PRODUCT_TYPE'] ?? 0); if ($iblockId <= 0) { $result->addError(new Error('parameter - iblockId is empty')); } if ($productTypeId <= 0) { $result->addError(new Error('parameter - productType is empty')); } if ($result->isSuccess()) { $this->loadFieldNames(); $r = $this->isAllowedProductTypeByIBlockId($productTypeId, $iblockId); if ($r->isSuccess()) { $propertyValues = $this->getFieldsIBlockPropertyValuesByFilter(['IBLOCK_ID' => $iblockId]); $properties = []; if ($propertyValues->isSuccess()) { $properties = $propertyValues->getData(); unset($properties['PROPERTY_*']); } unset($propertyValues); $result->setData( array_merge( $this->getFieldsIBlockElement(), $properties, $this->getFieldsCatalogProductCommonFields(), $this->getFieldsCatalogProductByType($productTypeId) ) ); unset($properties); } else { $result->addErrors($r->getErrors()); } } return $result; } /** * @param $catalogType * @return array */ private static function getProductTypes($catalogType): array { //TODO: remove after create \Bitrix\Catalog\Model\CatalogIblock return match ($catalogType) { \CCatalogSku::TYPE_CATALOG => [ ProductTable::TYPE_SERVICE => true, ProductTable::TYPE_PRODUCT => true, ProductTable::TYPE_SET => true, ], \CCatalogSku::TYPE_OFFERS => [ ProductTable::TYPE_OFFER => true, ProductTable::TYPE_FREE_OFFER => true, ], \CCatalogSku::TYPE_FULL => [ ProductTable::TYPE_SERVICE => true, ProductTable::TYPE_PRODUCT => true, ProductTable::TYPE_SET => true, ProductTable::TYPE_SKU => true, ProductTable::TYPE_EMPTY_SKU => true, ], \CCatalogSku::TYPE_PRODUCT => [ ProductTable::TYPE_SKU => true, ProductTable::TYPE_EMPTY_SKU => true, ], default => [], }; } /** * @return string[] */ private static function getUserType(): array { return [ PropertyTable::TYPE_STRING . ':' . PROPERTYTable::USER_TYPE_DATE, PropertyTable::TYPE_STRING . ':' . PropertyTable::USER_TYPE_DATETIME, PropertyTable::TYPE_STRING . ':' . PropertyTable::USER_TYPE_HTML, PropertyTable::TYPE_STRING . ':' . PropertyTable::USER_TYPE_XML_ID, PropertyTable::TYPE_STRING . ':' . PropertyTable::USER_TYPE_DIRECTORY, PropertyTable::TYPE_STRING . ':Money', PropertyTable::TYPE_STRING . ':map_yandex', PropertyTable::TYPE_STRING . ':map_google', PropertyTable::TYPE_STRING . ':employee', PropertyTable::TYPE_STRING . ':ECrm', PropertyTable::TYPE_STRING . ':UserID', PropertyTable::TYPE_NUMBER . ':' . PropertyTable::USER_TYPE_SEQUENCE, PropertyTable::TYPE_ELEMENT . ':' . PropertyTable::USER_TYPE_ELEMENT_LIST, PropertyTable::TYPE_ELEMENT . ':' . PropertyTable::USER_TYPE_ELEMENT_AUTOCOMPLETE, PropertyTable::TYPE_ELEMENT . ':' . PropertyTable::USER_TYPE_SKU, PropertyTable::TYPE_SECTION . ':' . PropertyTable::USER_TYPE_SECTION_AUTOCOMPLETE, //TODO: support types //'S:video', //'S:TopicID', //'S:FileMan', //'S:DiskFile', ]; } public function internalizeFieldsList($arguments, $fieldsInfo = []): array { // param - IBLOCK_ID is reqired in filter $iblockId = (int)($arguments['filter']['IBLOCK_ID'] ?? 0); $propertyValues = $this->getFieldsIBlockPropertyValuesByFilter(['IBLOCK_ID' => $iblockId]); $fieldsInfo = array_merge( $this->getFields(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []) ); unset($propertyValues); return parent::internalizeFieldsList($arguments, $fieldsInfo); } public function internalizeFieldsAdd($fields, $fieldsInfo = []): array { // param - IBLOCK_ID is reqired in filter $iblockId = (int)($fields['IBLOCK_ID'] ?? 0); $productType = (int)($fields['TYPE'] ?? 0); $propertyValues = $this->getFieldsIBlockPropertyValuesByFilter(['IBLOCK_ID' => $iblockId]); $product = $this->getFieldsCatalogProductByFilter(['IBLOCK_ID' => $iblockId, 'PRODUCT_TYPE' => $productType]); if ($product->isSuccess()) { $fieldsInfo = array_merge( $this->getFieldsIBlockElement(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []), $this->getFieldsCatalogProductCommonFields(), $product->getData() ); } else { $fieldsInfo = array_merge( $this->getFields(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []) ); } unset($product, $propertyValues); return parent::internalizeFieldsAdd($fields, $fieldsInfo); } public function internalizeFieldsUpdate($fields, $fieldsInfo = []): array { // param - IBLOCK_ID is reqired in filter $iblockId = (int)($fields['IBLOCK_ID'] ?? 0); $productType = (int)($fields['TYPE'] ?? 0); $propertyValues = $this->getFieldsIBlockPropertyValuesByFilter(['IBLOCK_ID' => $iblockId]); $product = $this->getFieldsCatalogProductByFilter(['IBLOCK_ID' => $iblockId, 'PRODUCT_TYPE' => $productType]); if ($product->isSuccess()) { $fieldsInfo = array_merge( $this->getFieldsIBlockElement(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []), $this->getFieldsCatalogProductCommonFields(), $product->getData() ); } else { $fieldsInfo = array_merge( $this->getFields(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []) ); } unset($product, $propertyValues); return parent::internalizeFieldsUpdate($fields, $fieldsInfo); } protected function internalizeDateValue($value): Result { //API does not accept DataTime objects, so the ISO format is transformed into a format for a filter. $r = new Result(); $date = $this->internalizeDate($value); if ($date instanceof Date) { $value = $date->format('d.m.Y'); } else { $r->addError(new Error('Wrong type data')); } if ($r->isSuccess()) { $r->setData([$value]); } return $r; } protected function internalizeDateTimeValue($value): Result { //API does not accept DataTime objects, so the ISO format is transformed into a format for a filter. $r = new Result(); $date = $this->internalizeDateTime($value); if ($date instanceof DateTime) { $value = $date->format('d.m.Y H:i:s'); } else { $r->addError(new Error('Wrong type datetime')); } if ($r->isSuccess()) { $r->setData([$value]); } return $r; } protected function internalizeDateProductPropertyValue($value): Result { //API does not accept DataTime objects, so the ISO format is transformed into a format for a filter. $r = new Result(); $date = $this->internalizeDate($value); if ($date instanceof Date) { $value = $date->format('Y-m-d'); } else { $r->addError(new Error('Wrong type data')); } if ($r->isSuccess()) { $r->setData([$value]); } return $r; } protected function internalizeDateTimeProductPropertyValue($value): Result { //API does not accept DataTime objects, so the ISO format is transformed into a format for a filter. $r = new Result(); $date = $this->internalizeDateTime($value); if ($date instanceof DateTime) { $value = $date->format('Y-m-d H:i:s'); } else { $r->addError(new Error('Wrong type datetime')); } if ($r->isSuccess()) { $r->setData([$value]); } return $r; } protected function internalizeExtendedTypeValue($value, $info): Result { $r = new Result(); $type = $info['TYPE'] ?? ''; if ($type === EntityFieldType::PRODUCT_PROPERTY) { $propertyType = $info['PROPERTY_TYPE'] ?? ''; $userType = $info['USER_TYPE'] ?? ''; $attrs = $info['ATTRIBUTES'] ?? []; $isMultiple = in_array(Attributes::MULTIPLE, $attrs, true); $r = $isMultiple ? $this->checkIndexedMultipleValue($value) : new Result(); if ($r->isSuccess()) { $value = $isMultiple ? $value : [$value]; if (!is_array($value)) { $value = [$value]; } if ($propertyType === PropertyTable::TYPE_STRING && $userType === PropertyTable::USER_TYPE_DATE) { array_walk($value, function(&$item) use ($r) { $date = $this->internalizeDateProductPropertyValue($item['VALUE']); if ($date->isSuccess()) { $item['VALUE'] = $date->getData()[0]; } else { $r->addErrors($date->getErrors()); } }); } elseif ($propertyType === PropertyTable::TYPE_STRING && $userType === PropertyTable::USER_TYPE_DATETIME) { array_walk($value, function(&$item) use ($r) { $date = $this->internalizeDateTimeProductPropertyValue($item['VALUE']); if ($date->isSuccess()) { $item['VALUE'] = $date->getData()[0]; } else { $r->addErrors($date->getErrors()); } }); } elseif ($propertyType === PropertyTable::TYPE_FILE && empty($userType)) { array_walk($value, function(&$item) use ($r) { $date = $this->internalizeFileValue($item['VALUE']); if (!empty($date)) { $item['VALUE'] = $date; } else { $r->addError(new Error('Wrong file date')); } }); } elseif ($this->isPropertyBoolean($info)) { $booleanValue = $value[0]['VALUE']; if ($booleanValue === self::BOOLEAN_VALUE_YES) { $value[0]['VALUE'] = $info['BOOLEAN_VALUE_YES']['ID']; } elseif ($booleanValue === self::BOOLEAN_VALUE_NO) { $value[0]['VALUE'] = null; } } //elseif ($propertyType === 'S' && $userType === 'HTML'){} $value = $isMultiple? $value: $value[0]; } } if ($r->isSuccess()) { $r->setData([$value]); } return $r; } public function internalizeArguments($name, $arguments): array { if ( $name === 'getfieldsbyfilter' || $name === 'download' ) { return $arguments; } // Returns throw return parent::internalizeArguments($name, $arguments); } protected function externalizeEmptyValue($name, $value, $fields, $fieldsInfo) { $fieldInfo = $fieldsInfo[$name] ?? []; if ($this->isPropertyBoolean($fieldInfo)) { return self::BOOLEAN_VALUE_NO; } return parent::externalizeEmptyValue($name, $value, $fields, $fieldsInfo); } public function externalizeFieldsGet($fields, $fieldsInfo = []): array { // param - IBLOCK_ID is reqired in filter $iblockId = (int)($fields['IBLOCK_ID'] ?? 0); $productType = (int)($fields['TYPE'] ?? 0); $propertyValues = $this->getFieldsIBlockPropertyValuesByFilter(['IBLOCK_ID' => $iblockId]); $product = $this->getFieldsCatalogProductByFilter(['IBLOCK_ID' => $iblockId, 'PRODUCT_TYPE' => $productType]); if ($product->isSuccess()) { $fieldsInfo = array_merge( $this->getFieldsIBlockElement(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []), $this->getFieldsCatalogProductCommonFields(), $product->getData() ); } else { // if it was not possible to determine the view fields by product type, // we get the default fields, all fields of the catalog and fields of the Information Block $fieldsInfo = array_merge( $this->getFields(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []) ); } unset($product, $propertyValues); return parent::externalizeFieldsGet($fields, $fieldsInfo); } public function externalizeListFields($list, $fieldsInfo = []): array { // param - IBLOCK_ID is reqired in filter $iblockId = (int)($list[0]['IBLOCK_ID'] ?? 0); $propertyValues = $this->getFieldsIBlockPropertyValuesByFilter(['IBLOCK_ID' => $iblockId]); $fieldsInfo = array_merge( $this->getFields(), ($propertyValues->isSuccess() ? $propertyValues->getData() : []) ); unset($propertyValues); return parent::externalizeListFields($list, $fieldsInfo); } public function externalizeResult($name, $fields): array { if ( $name === 'getfieldsbyfilter' || $name === 'download' ) { return $fields; } // Returns throw return parent::externalizeResult($name, $fields); } public function convertKeysToSnakeCaseArguments($name, $arguments) { if ($name === 'getfieldsbyfilter') { if (isset($arguments['filter'])) { $filter = $arguments['filter']; if (!empty($filter)) { $arguments['filter'] = $this->convertKeysToSnakeCaseFilter($filter); } } } elseif ($name === 'download') { if (isset($arguments['fields'])) { $fields = $arguments['fields']; if (!empty($fields)) { $converter = new Converter( Converter::VALUES | Converter::TO_SNAKE | Converter::TO_SNAKE_DIGIT | Converter::TO_UPPER ); $converterForKey = new Converter( Converter::KEYS | Converter::TO_SNAKE | Converter::TO_SNAKE_DIGIT | Converter::TO_UPPER ); $result = []; foreach ($converter->process($fields) as $key => $value) { $result[$converterForKey->process($key)] = $value; } $arguments['fields'] = $result; } } } else { parent::convertKeysToSnakeCaseArguments($name, $arguments); } return $arguments; } public function checkFieldsList($arguments): Result { $r = new Result(); $select = $arguments['select'] ?? []; if (!is_array($select)) { $select = []; } $error = []; if (!in_array('ID', $select)) { $error[] = 'id'; } if (!in_array('IBLOCK_ID', $select)) { $error[] = 'iblockId'; } if (!empty($error)) { $r->addError(new Error('Required select fields: ' . implode(', ', $error))); } if (!isset($arguments['filter']['IBLOCK_ID'])) { $r->addError(new Error('Required filter fields: iblockId')); } return $r; } public function checkArguments($name, $arguments): Result { if ($name === 'download') { $fields = $arguments['fields']; return $this->checkFieldsDownload($fields); } else { return parent::checkArguments($name, $arguments); } } protected function checkFieldsDownload($fields): Result { $r = new Result(); $emptyFields = []; if (!isset($fields['FIELD_NAME'])) { $emptyFields[] = 'fieldName'; } if (!isset($fields['FILE_ID'])) { $emptyFields[] = 'fileId'; } if (!isset($fields['PRODUCT_ID'])) { $emptyFields[] = 'productId'; } if (!empty($emptyFields)) { $r->addError(new Error('Required fields: '.implode(', ', $emptyFields))); } return $r; } protected function getActionUriToDownload(): string { return '/rest/catalog.product.download'; } protected function externalizeFileValue($name, $value, $fields): array { $productId = null; if (isset($fields['PRODUCT_ID'])) { $productId = $fields['PRODUCT_ID']; } elseif (isset($fields['ID'])) { $productId = $fields['ID']; } $productId = (int)$productId; $data = [ 'fields' => [ 'fieldName' => Converter::toJson() ->process($name) , 'fileId' => $value, 'productId' => $productId, ], ]; $uri = new \Bitrix\Main\Web\Uri($this->getActionUriToDownload()); return [ 'ID' => $value, 'URL' => new \Bitrix\Main\Engine\Response\DataType\ContentUri( $uri->addParams($data) ->__toString() ), ]; } protected function externalizeExtendedTypeValue($name, $value, $fields, $fieldsInfo): Result { $r = new Result(); $info = $fieldsInfo[$name] ?? []; $type = $info['TYPE'] ?? ''; if ($type === EntityFieldType::PRODUCT_PROPERTY) { $attrs = $info['ATTRIBUTES'] ?? []; $isMultiple = in_array(Attributes::MULTIPLE, $attrs, true); $propertyType = $info['PROPERTY_TYPE'] ?? ''; $userType = $info['USER_TYPE'] ?? ''; $value = $isMultiple? $value: [$value]; if ($propertyType === PropertyTable::TYPE_STRING && $userType === PropertyTable::USER_TYPE_DATE) { array_walk($value, function(&$item)use($r) { $date = $this->externalizeDateValue($item['VALUE']); if ($date->isSuccess()) { $item['VALUE'] = $date->getData()[0]; } else { $r->addErrors($date->getErrors()); } }); } elseif ($propertyType === PropertyTable::TYPE_STRING && $userType === PropertyTable::USER_TYPE_DATETIME) { array_walk($value, function(&$item) use($r) { $date = $this->externalizeDateTimeValue($item['VALUE']); if ($date->isSuccess()) { $item['VALUE'] = $date->getData()[0]; } else { $r->addErrors($date->getErrors()); } }); } elseif ($propertyType === PropertyTable::TYPE_FILE && empty($userType)) { array_walk($value, function(&$item) use ($fields, $name) { $item['VALUE'] = $this->externalizeFileValue($name, $item['VALUE'], ['PRODUCT_ID' => $fields['ID']]); }); } elseif ($this->isPropertyBoolean($info)) { if ($value) { $value = self::BOOLEAN_VALUE_YES; } else { $value = self::BOOLEAN_VALUE_NO; } } $value = $isMultiple? $value: $value[0]; } if ($r->isSuccess()) { $r->setData([$value]); } return $r; } /** * Loads names for standart fields. * * @return void */ private function loadFieldNames(): void { if (!empty($this->productFieldNames)) { return; } $this->loadEntityFieldNames(Iblock\ElementTable::getMap()); $this->loadEntityFieldNames(Catalog\ProductTable::getMap()); $this->loadAdditionalFieldNames(); } /** * Loads names for entity scalar fields. * * @param array $fieldList * @return void */ private function loadEntityFieldNames(array $fieldList): void { /** @var \Bitrix\Main\ORM\Fields\Field $field */ foreach ($fieldList as $field) { if ($field instanceof ScalarField) { $name = $field->getName(); $title = $field->getTitle(); $this->productFieldNames[$name] = $title ?: $name; } } } private function loadAdditionalFieldNames(): void { $this->productFieldNames['IBLOCK_SECTION'] = Loc::getMessage('RESTVIEW_PRODUCT_FIELD_NAME_IBLOCK_SECTION'); } /** * Returns field list with name attribute. * * @param array $fieldList * @return array */ private function fillFieldNames(array $fieldList): array { foreach (array_keys($fieldList) as $id) { $fieldList[$id]['NAME'] = $this->productFieldNames[$id] ?? $id; } return $fieldList; } private function isPropertyBoolean(array $property): bool { if (($property['PROPERTY_TYPE'] ?? '') !== PropertyTable::TYPE_LIST) { return false; } $attributes = $property['ATTRIBUTES'] ?? []; if (!is_array($attributes)) { $attributes = []; } if (in_array(Attributes::MULTIPLE, $attributes, true)) { return false; } $userType = (string)($property['USER_TYPE'] ?? ''); if ($userType !== '' && $userType !== Catalog\Controller\Enum::PROPERTY_USER_TYPE_BOOL_ENUM) { return false; } return (!empty($property['BOOLEAN_VALUE_YES']) && is_array($property['BOOLEAN_VALUE_YES'])); } protected function checkIndexedMultipleValue($values): Result { $r = new Result(); return $this->isIndexedArray($values) ? $r : $r->addError(new Error('For type Multiple field - value must be an Indexed array')) ; } protected function isIndexedArray($ary): bool { if (!is_array($ary)) { return false; } $keys = array_keys($ary); foreach ($keys as $k) { if (!is_int($k)) { return false; } } return true; } }