/modules/catalog/lib/product/propertycatalogfeature.php

https://gitlab.com/alexprowars/bitrix · PHP · 446 lines · 330 code · 32 blank · 84 comment · 60 complexity · 60e8cdb23edbf25f1fbee4271ba2cb11 MD5 · raw file

  1. <?php
  2. namespace Bitrix\Catalog\Product;
  3. use Bitrix\Main,
  4. Bitrix\Main\Loader,
  5. Bitrix\Main\Localization\Loc,
  6. Bitrix\Iblock;
  7. Loc::loadMessages(__FILE__);
  8. if (Loader::includeModule('iblock'))
  9. {
  10. class PropertyCatalogFeature extends Iblock\Model\PropertyFeature
  11. {
  12. const FEATURE_ID_BASKET_PROPERTY = 'IN_BASKET'; // property added to basket
  13. const FEATURE_ID_OFFER_TREE_PROPERTY = 'OFFER_TREE'; // property is used to select offers
  14. /**
  15. * Event handler. Returns catalog feature list for iblock.
  16. *
  17. * @param Main\Event $event Event data.
  18. * @return Main\EventResult
  19. */
  20. public static function handlerPropertyFeatureBuildList(Main\Event $event): Main\EventResult
  21. {
  22. $list = [];
  23. $property = $event->getParameter('property');
  24. $description = $event->getParameter('description');
  25. if (self::checkBasketProperty($property, $description))
  26. {
  27. $list[] = [
  28. 'MODULE_ID' => 'catalog',
  29. 'FEATURE_ID' => self::FEATURE_ID_BASKET_PROPERTY,
  30. 'FEATURE_NAME' => Loc::getMessage('PROPERTY_CATALOG_FEATURE_NAME_BASKET_PROPERTY')
  31. ];
  32. }
  33. if (self::checkOfferTreeProperty($property, $description))
  34. {
  35. $list[] = [
  36. 'MODULE_ID' => 'catalog',
  37. 'FEATURE_ID' => self::FEATURE_ID_OFFER_TREE_PROPERTY,
  38. 'FEATURE_NAME' => Loc::getMessage('PROPERTY_CATALOG_FEATURE_NAME_SKU_TREE_PROPERTY')
  39. ];
  40. }
  41. unset($description, $property);
  42. return new Main\EventResult(Main\EventResult::SUCCESS, $list);
  43. }
  44. /**
  45. * Unified method to get properties added to basket.
  46. *
  47. * @param int $iblockId Iblock identifier.
  48. * @param array $parameters Options
  49. * keys are case sensitive:
  50. * <ul>
  51. * <li>CODE Return symbolic code as identifier (Y/N, default N).
  52. * </ul>
  53. * @return array|null
  54. * @throws Main\ArgumentException
  55. * @throws Main\ArgumentNullException
  56. * @throws Main\ArgumentOutOfRangeException
  57. * @throws Main\ObjectPropertyException
  58. * @throws Main\SystemException
  59. */
  60. public static function getBasketPropertyCodes($iblockId, array $parameters = []): ?array
  61. {
  62. $iblockId = (int)$iblockId;
  63. if ($iblockId <= 0)
  64. return null;
  65. $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
  66. if (empty($catalog))
  67. return null;
  68. if (!self::isEnabledFeatures())
  69. return self::getBasketPropertyByTypes($catalog, $parameters);
  70. $filter = null;
  71. switch ($catalog['CATALOG_TYPE'])
  72. {
  73. case \CCatalogSku::TYPE_CATALOG:
  74. case \CCatalogSku::TYPE_PRODUCT:
  75. case \CCatalogSku::TYPE_FULL:
  76. $filter = [
  77. [
  78. 'LOGIC' => 'OR',
  79. [
  80. '=PROPERTY.MULTIPLE' => 'Y',
  81. '@PROPERTY.PROPERTY_TYPE' => [
  82. Iblock\PropertyTable::TYPE_ELEMENT,
  83. Iblock\PropertyTable::TYPE_SECTION,
  84. Iblock\PropertyTable::TYPE_LIST,
  85. Iblock\PropertyTable::TYPE_NUMBER,
  86. Iblock\PropertyTable::TYPE_STRING
  87. ]
  88. ],
  89. [
  90. '=PROPERTY.MULTIPLE' => 'N',
  91. '@PROPERTY.PROPERTY_TYPE' => [
  92. Iblock\PropertyTable::TYPE_ELEMENT,
  93. Iblock\PropertyTable::TYPE_LIST
  94. ]
  95. ]
  96. ]
  97. ];
  98. break;
  99. case \CCatalogSku::TYPE_OFFERS:
  100. $filter = [
  101. '!=PROPERTY.PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_FILE,
  102. '!=PROPERTY.ID' => $catalog['SKU_PROPERTY_ID']
  103. ];
  104. break;
  105. }
  106. unset($catalog);
  107. if ($filter === null)
  108. return null;
  109. $filter['=MODULE_ID'] = 'catalog';
  110. $filter['=FEATURE_ID'] = self::FEATURE_ID_BASKET_PROPERTY;
  111. return self::getFilteredPropertyCodes($iblockId, $filter, $parameters);
  112. }
  113. /**
  114. * Unified method for obtaining properties used to select offers.
  115. *
  116. * @param int $iblockId Iblock identifier.
  117. * @param array $parameters Options
  118. * keys are case sensitive:
  119. * <ul>
  120. * <li>CODE Return symbolic code as identifier (Y/N, default N).
  121. * </ul>
  122. * @return array|null
  123. * @throws Main\ArgumentException
  124. * @throws Main\ArgumentNullException
  125. * @throws Main\ArgumentOutOfRangeException
  126. * @throws Main\ObjectPropertyException
  127. * @throws Main\SystemException
  128. */
  129. public static function getOfferTreePropertyCodes($iblockId, array $parameters = []): ?array
  130. {
  131. $iblockId = (int)$iblockId;
  132. if ($iblockId <= 0)
  133. return null;
  134. $catalog = \CCatalogSku::GetInfoByOfferIBlock($iblockId);
  135. if (empty($catalog))
  136. return null;
  137. if (!self::isEnabledFeatures())
  138. return self::getOfferTreePropertyByTypes($catalog, $parameters);
  139. $filter = [
  140. '=MODULE_ID' => 'catalog',
  141. '=FEATURE_ID' => self::FEATURE_ID_OFFER_TREE_PROPERTY,
  142. '=PROPERTY.MULTIPLE' => 'N',
  143. '!=PROPERTY.ID' => $catalog['SKU_PROPERTY_ID'],
  144. [
  145. 'LOGIC' => 'OR',
  146. [
  147. '@PROPERTY.PROPERTY_TYPE' => [
  148. Iblock\PropertyTable::TYPE_ELEMENT,
  149. Iblock\PropertyTable::TYPE_LIST
  150. ]
  151. ],
  152. [
  153. '=PROPERTY.PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_STRING,
  154. '=PROPERTY.USER_TYPE' => 'directory'
  155. ]
  156. ]
  157. ];
  158. return self::getFilteredPropertyCodes($iblockId, $filter, $parameters);
  159. }
  160. /**
  161. * Getting properties added to basket when feature engine is off.
  162. * Internal method.
  163. *
  164. * @param array $catalog Catalog description.
  165. * @param array $parameters Options
  166. * keys are case sensitive:
  167. * <ul>
  168. * <li>CODE Return symbolic code as identifier (Y/N, default N).
  169. * </ul>
  170. * @return array|null
  171. * @throws Main\ArgumentException
  172. * @throws Main\ObjectPropertyException
  173. * @throws Main\SystemException
  174. */
  175. private static function getBasketPropertyByTypes(array $catalog, array $parameters = []): ?array
  176. {
  177. $result = [];
  178. $getCode = (isset($parameters['CODE']) && $parameters['CODE'] == 'Y');
  179. $filter = [];
  180. switch ($catalog['CATALOG_TYPE'])
  181. {
  182. case \CCatalogSku::TYPE_CATALOG:
  183. $filter = [
  184. '=IBLOCK_ID' => $catalog['IBLOCK_ID'],
  185. [
  186. 'LOGIC' => 'OR',
  187. [
  188. '=MULTIPLE' => 'Y',
  189. '@PROPERTY_TYPE' => [
  190. Iblock\PropertyTable::TYPE_ELEMENT,
  191. Iblock\PropertyTable::TYPE_SECTION,
  192. Iblock\PropertyTable::TYPE_LIST,
  193. Iblock\PropertyTable::TYPE_NUMBER,
  194. Iblock\PropertyTable::TYPE_STRING
  195. ]
  196. ],
  197. [
  198. '=MULTIPLE' => 'N',
  199. '@PROPERTY_TYPE' => [
  200. Iblock\PropertyTable::TYPE_ELEMENT,
  201. Iblock\PropertyTable::TYPE_LIST
  202. ]
  203. ]
  204. ]
  205. ];
  206. break;
  207. case \CCatalogSku::TYPE_PRODUCT:
  208. case \CCatalogSku::TYPE_FULL:
  209. $filter = [
  210. '=IBLOCK_ID' => $catalog['PRODUCT_IBLOCK_ID'],
  211. [
  212. 'LOGIC' => 'OR',
  213. [
  214. '=MULTIPLE' => 'Y',
  215. '@PROPERTY_TYPE' => [
  216. Iblock\PropertyTable::TYPE_ELEMENT,
  217. Iblock\PropertyTable::TYPE_SECTION,
  218. Iblock\PropertyTable::TYPE_LIST,
  219. Iblock\PropertyTable::TYPE_NUMBER,
  220. Iblock\PropertyTable::TYPE_STRING
  221. ]
  222. ],
  223. [
  224. '=MULTIPLE' => 'N',
  225. '@PROPERTY_TYPE' => [
  226. Iblock\PropertyTable::TYPE_ELEMENT,
  227. Iblock\PropertyTable::TYPE_LIST
  228. ]
  229. ]
  230. ]
  231. ];
  232. break;
  233. case \CCatalogSku::TYPE_OFFERS:
  234. $filter = [
  235. '=IBLOCK_ID' => $catalog['IBLOCK_ID'],
  236. '!=PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_FILE,
  237. '!=ID' => $catalog['SKU_PROPERTY_ID']
  238. ];
  239. break;
  240. }
  241. $filter['=ACTIVE'] = 'Y';
  242. $iterator = Iblock\PropertyTable::getList([
  243. 'select' => ['ID', 'CODE', 'SORT'],
  244. 'filter' => $filter,
  245. 'order' => ['SORT' => 'ASC', 'ID' => 'ASC']
  246. ]);
  247. while ($row = $iterator->fetch())
  248. $result[(int)$row['ID']] = self::getPropertyCode($row, $getCode);
  249. unset($row, $iterator);
  250. unset($filter, $getCode);
  251. return (!empty($result) ? array_values($result) : null);
  252. }
  253. /**
  254. * Getting the properties used to select offers when feature engine is off.
  255. * Internal method.
  256. *
  257. * @param array $catalog Catalog description.
  258. * @param array $parameters Options
  259. * keys are case sensitive:
  260. * <ul>
  261. * <li>CODE Return symbolic code as identifier (Y/N, default N).
  262. * </ul>
  263. * @return array|null
  264. * @throws Main\ArgumentException
  265. * @throws Main\ObjectPropertyException
  266. * @throws Main\SystemException
  267. */
  268. private static function getOfferTreePropertyByTypes(array $catalog, array $parameters = []): ?array
  269. {
  270. $result = [];
  271. $getCode = (isset($parameters['CODE']) && $parameters['CODE'] == 'Y');
  272. $filter = [
  273. '=IBLOCK_ID' => $catalog['IBLOCK_ID'],
  274. '!=ID' => $catalog['SKU_PROPERTY_ID'],
  275. '=ACTIVE' => 'Y',
  276. '=MULTIPLE' => 'N',
  277. [
  278. 'LOGIC' => 'OR',
  279. [
  280. '@PROPERTY_TYPE' => [
  281. Iblock\PropertyTable::TYPE_ELEMENT,
  282. Iblock\PropertyTable::TYPE_LIST
  283. ]
  284. ],
  285. [
  286. '=PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_STRING,
  287. '=USER_TYPE' => 'directory'
  288. ]
  289. ]
  290. ];
  291. $iterator = Iblock\PropertyTable::getList([
  292. 'select' => ['ID', 'CODE', 'SORT'],
  293. 'filter' => $filter,
  294. 'order' => ['SORT' => 'ASC', 'ID' => 'ASC']
  295. ]);
  296. while ($row = $iterator->fetch())
  297. $result[(int)$row['ID']] = self::getPropertyCode($row, $getCode);
  298. unset($row, $iterator);
  299. unset($filter, $getCode);
  300. return (!empty($result) ? array_values($result) : null);
  301. }
  302. /**
  303. * Check - can the property be added to the basket.
  304. * Internal method.
  305. *
  306. * @param array $property Property description.
  307. * @param array $description Additional description.
  308. * @return bool
  309. */
  310. private static function checkBasketProperty(array $property, array $description): bool
  311. {
  312. if (!isset($property['IBLOCK_ID']))
  313. return false;
  314. $catalogType = null;
  315. if ((int)$property['IBLOCK_ID'] > 0)
  316. {
  317. $catalog = \CCatalogSku::GetInfoByIBlock($property['IBLOCK_ID']);
  318. if (empty($catalog))
  319. return false;
  320. $catalogType = $catalog['CATALOG_TYPE'];
  321. }
  322. else
  323. {
  324. return false;
  325. }
  326. if ($catalogType === null)
  327. return false;
  328. if (!isset($property['PROPERTY_TYPE']))
  329. return false;
  330. if (!isset($property['MULTIPLE']))
  331. return false;
  332. switch ($catalogType)
  333. {
  334. case \CCatalogSku::TYPE_PRODUCT:
  335. case \CCatalogSku::TYPE_CATALOG:
  336. case \CCatalogSku::TYPE_FULL:
  337. if ($property['MULTIPLE'] == 'Y')
  338. {
  339. if (
  340. $property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_ELEMENT
  341. && $property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_SECTION
  342. && $property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_LIST
  343. && $property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_NUMBER
  344. && $property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_STRING
  345. )
  346. return false;
  347. }
  348. elseif ($property['MULTIPLE'] == 'N')
  349. {
  350. if (
  351. $property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_ELEMENT
  352. && $property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_LIST
  353. )
  354. return false;
  355. }
  356. else
  357. {
  358. return false;
  359. }
  360. break;
  361. case \CCatalogSku::TYPE_OFFERS:
  362. if ($property['PROPERTY_TYPE'] == Iblock\PropertyTable::TYPE_FILE)
  363. return false;
  364. break;
  365. }
  366. return true;
  367. }
  368. /**
  369. * Check - can the property be used to select trade offers.
  370. * Internal method.
  371. *
  372. * @param array $property Property description.
  373. * @param array $description Additional description.
  374. * @return bool
  375. */
  376. private static function checkOfferTreeProperty(array $property, array $description): bool
  377. {
  378. if (!isset($property['IBLOCK_ID']))
  379. return false;
  380. if ((int)$property['IBLOCK_ID'] > 0)
  381. {
  382. $catalog = \CCatalogSku::GetInfoByOfferIBlock($property['IBLOCK_ID']);
  383. if (empty($catalog))
  384. return false;
  385. }
  386. else
  387. {
  388. return false;
  389. }
  390. if (
  391. !isset($property['PROPERTY_TYPE'])
  392. || (
  393. $property['PROPERTY_TYPE'] != Iblock\PropertyTable::TYPE_ELEMENT
  394. && $property['PROPERTY_TYPE'] != Iblock\PropertyTable::TYPE_LIST
  395. && $property['PROPERTY_TYPE'] != Iblock\PropertyTable::TYPE_STRING
  396. )
  397. )
  398. return false;
  399. if ($property['PROPERTY_TYPE'] == Iblock\PropertyTable::TYPE_ELEMENT)
  400. {
  401. if (isset($property['USER_TYPE']) && $property['USER_TYPE'] == \CIBlockPropertySKU::USER_TYPE)
  402. return false;
  403. }
  404. if ($property['PROPERTY_TYPE'] == Iblock\PropertyTable::TYPE_STRING)
  405. {
  406. if (!isset($property['USER_TYPE']) || $property['USER_TYPE'] != 'directory')
  407. return false;
  408. }
  409. if (!isset($property['MULTIPLE']) || $property['MULTIPLE'] != 'N')
  410. return false;
  411. return true;
  412. }
  413. }
  414. }