PageRenderTime 72ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/sale/lib/tradingplatform/vk/feed/data/converters/product.php

https://gitlab.com/alexprowars/bitrix
PHP | 438 lines | 283 code | 74 blank | 81 comment | 55 complexity | a8b5c7bb8170c5f2d315e38912d64e73 MD5 | raw file
  1. <?php
  2. namespace Bitrix\Sale\TradingPlatform\Vk\Feed\Data\Converters;
  3. use Bitrix\Main\ArgumentNullException;
  4. use Bitrix\Main\Localization\Loc;
  5. use Bitrix\Sale\TradingPlatform\Vk;
  6. use Bitrix\Highloadblock\HighloadBlockTable;
  7. Loc::loadMessages(__FILE__);
  8. class Product extends DataConverter
  9. {
  10. protected $selectOfferProps;
  11. protected $sectionsList;
  12. private $result;
  13. const DESCRIPTION_LENGHT_MIN = 10;
  14. const DESCRIPTION_LENGHT_MAX = 3300; // it is not entirely accurate value, but in doc i can't find true info
  15. const NAME_LENGHT_MIN = 4;
  16. const NAME_LENGHT_MAX = 100;
  17. /**
  18. * Product constructor.
  19. * @param $exportId - int ID of export
  20. */
  21. public function __construct($exportId)
  22. {
  23. if (!isset($exportId) || $exportId == '')
  24. throw new ArgumentNullException("EXPORT_ID");
  25. $this->exportId = $exportId;
  26. $this->sectionsList = new Vk\SectionsList($this->exportId);
  27. }
  28. /**
  29. * Main method for convert
  30. *
  31. * @param $data - Array of albums data from source.
  32. * @return array
  33. */
  34. public function convert($data)
  35. {
  36. $logger = new Vk\Logger($this->exportId);
  37. $this->result = array();
  38. // get common SKU and notSKU data
  39. $this->result = $this->getNotSkuItemData($data);
  40. // product WITH SKU
  41. $offersDescription = '';
  42. if (isset($data["OFFERS"]) && is_array($data["OFFERS"]) && !empty($data["OFFERS"]))
  43. {
  44. //adding desc and additional photos from SKU
  45. $this->selectOfferProps = $data["SELECT_OFFER_PROPS"];
  46. $this->result["PHOTOS_OFFERS"] = array();
  47. $this->result["PHOTOS_OFFERS_FOR_VK"] = array();
  48. $offersConverted = array();
  49. foreach ($data["OFFERS"] as $offer)
  50. {
  51. $resultOffer = $this->getItemDataOffersOffer($offer);
  52. if (!empty($resultOffer["PHOTOS"]))
  53. $this->result["PHOTOS_OFFERS"] += $resultOffer["PHOTOS"];
  54. if (!empty($resultOffer["PHOTOS_FOR_VK"]))
  55. $this->result["PHOTOS_OFFERS_FOR_VK"] += $resultOffer["PHOTOS_FOR_VK"];
  56. $offersConverted[] = $resultOffer;
  57. }
  58. $offersDescription = $this->createOffersDescriptionByPrices($offersConverted);
  59. }
  60. // check price. After offers convertions price may be changed
  61. if (!$this->result["PRICE"])
  62. {
  63. $logger->addError('PRODUCT_EMPTY_PRICE', $data["ID"]);
  64. return NULL;
  65. }
  66. // if exist offers descriptions - add title for them
  67. if ($offersDescription <> '')
  68. $this->result["description"] .= "\n\n" . Loc::getMessage("SALE_VK_PRODUCT_VARIANTS") . "\n" . $offersDescription;
  69. // sorted photos array in right order
  70. // todo: move this operation in Photoresizer
  71. $photosSorted = $this->sortPhotosArray();
  72. // CHECK photo sizes and count
  73. $photosChecked = Vk\PhotoResizer::checkPhotos($photosSorted, 'PRODUCT');
  74. if (empty($photosChecked))
  75. {
  76. $logger->addError("PRODUCT_WRONG_PHOTOS", $data["ID"]);
  77. return NULL;
  78. }
  79. $this->result["PHOTO_MAIN_BX_ID"] = $photosChecked["PHOTO_MAIN_BX_ID"];
  80. $this->result["PHOTO_MAIN_URL"] = $photosChecked["PHOTO_MAIN_URL"];
  81. $this->result["PHOTOS"] = $photosChecked["PHOTOS"];
  82. // add item to log, if image was be resized
  83. if ($photosChecked['RESIZE_UP'])
  84. $logger->addError('PRODUCT_PHOTOS_RESIZE_UP', $data["ID"]);
  85. if ($photosChecked['RESIZE_DOWN'])
  86. $logger->addError('PRODUCT_PHOTOS_RESIZE_DOWN', $data["ID"]);
  87. // cleaing DESCRIPTION
  88. $this->result["description"] = html_entity_decode($this->result["description"]);
  89. $this->result["description"] = preg_replace('/\t*/', '', $this->result["description"]);
  90. $this->result["description"] = strip_tags($this->result["description"]);
  91. $this->result['description'] = $this->validateDescription($this->result['description'], $logger);
  92. $this->result["description"] = self::convertToUtf8($this->result["description"]);
  93. $this->result['NAME'] = $this->validateName($this->result['NAME'], $logger);
  94. $this->result['NAME'] = self::convertToUtf8($this->result['NAME']);
  95. return array($data["ID"] => $this->result);
  96. }
  97. /**
  98. * Valid length of name
  99. *
  100. * @param $name
  101. * @param Vk\Logger|NULL $logger
  102. * @return string
  103. */
  104. private function validateName($name, Vk\Logger $logger = NULL)
  105. {
  106. $newName = $name;
  107. if (($length = self::matchLength($name)) < self::NAME_LENGHT_MIN)
  108. {
  109. $newName = self::extendString($name, $length, self::NAME_LENGHT_MIN);
  110. if ($logger)
  111. {
  112. $logger->addError('PRODUCT_SHORT_NAME', $this->result["BX_ID"]);
  113. }
  114. }
  115. if (($length = self::matchLength($name)) > self::NAME_LENGHT_MAX)
  116. {
  117. $newName = self::reduceString($name, $length, self::NAME_LENGHT_MAX);
  118. if ($logger)
  119. {
  120. $logger->addError('PRODUCT_LONG_NAME', $this->result["BX_ID"]);
  121. }
  122. }
  123. return $newName;
  124. }
  125. /**
  126. * Valid length of description
  127. *
  128. * @param $name
  129. * @param Vk\Logger|NULL $logger
  130. * @return string
  131. */
  132. private function validateDescription($desc, Vk\Logger $logger = NULL)
  133. {
  134. $newDesc = $desc;
  135. if (mb_strlen($desc) < self::DESCRIPTION_LENGHT_MIN)
  136. {
  137. $newDesc = $this->result['NAME'] . ': ' . $desc;
  138. if (mb_strlen($newDesc) < self::DESCRIPTION_LENGHT_MIN)
  139. {
  140. $newDesc = self::mb_str_pad($newDesc, self::DESCRIPTION_LENGHT_MIN, self::PAD_STRING);
  141. // ending space trim fix
  142. if ($newDesc[mb_strlen($newDesc) - 1] == ' ')
  143. {
  144. $newDesc .= self::PAD_STRING;
  145. }
  146. if ($logger)
  147. {
  148. $logger->addError('PRODUCT_SHORT_DESCRIPTION', $this->result["BX_ID"]);
  149. }
  150. }
  151. }
  152. if (mb_strlen($newDesc) > self::DESCRIPTION_LENGHT_MAX)
  153. {
  154. $newDesc = mb_substr($newDesc, 0, self::DESCRIPTION_LENGHT_MAX).'...';
  155. }
  156. return $newDesc;
  157. }
  158. /**
  159. * Create description of SKU depending of prices.
  160. * If all SKU prices equal main price - hide them.
  161. * If prices a different - add them to description
  162. *
  163. * @param $offers
  164. * @return string - string of SKUs description
  165. */
  166. private function createOffersDescriptionByPrices($offers)
  167. {
  168. $mainPrice = isset($this->result['PRICE']) && $this->result['PRICE'] ? $this->result['PRICE'] : 0;
  169. $needSkuPriceDescription = false;
  170. // compare main price and SKU prices. Find minimum, check difference
  171. foreach ($offers as $offer)
  172. {
  173. if ($offer['PRICE'])
  174. {
  175. // if not set main price - get them from SKU prices
  176. if ($mainPrice == 0)
  177. $mainPrice = $offer['PRICE'];
  178. // add price to SKU descriptions only of prices is different
  179. if ($offer['PRICE'] != $mainPrice)
  180. $needSkuPriceDescription = true;
  181. $mainPrice = ($mainPrice != 0) ? min($offer['PRICE'], $mainPrice) : $offer['PRICE'];
  182. }
  183. }
  184. // update SKU DESRIPTIONS if needed
  185. $offersDescription = '';
  186. if ($needSkuPriceDescription)
  187. {
  188. foreach ($offers as $offer)
  189. {
  190. $offersDescription .= $offer["DESCRIPTION_PROPERTIES"] . " - " . Loc::getMessage("SALE_VK_PRODUCT_PRICE") . " " . $offer['PRICE'] . " " . Loc::getMessage("SALE_VK_PRODUCT_CURRENCY") . "\n";
  191. }
  192. }
  193. else
  194. {
  195. foreach ($offers as $offer)
  196. {
  197. $offersDescription .= $offer["DESCRIPTION_PROPERTIES"] . "\n";
  198. }
  199. }
  200. $this->result['PRICE'] = $mainPrice;
  201. return $offersDescription;
  202. }
  203. /**
  204. * Sorted different photos types by priority
  205. *
  206. * @return array - Array of sorted photos
  207. */
  208. private function sortPhotosArray()
  209. {
  210. $newPhotos = array();
  211. if (isset($this->result['PHOTOS_FOR_VK']) && !empty($this->result['PHOTOS_FOR_VK']))
  212. $newPhotos += $this->result['PHOTOS_FOR_VK'];
  213. if (isset($this->result['PHOTOS_OFFERS_FOR_VK']) && !empty($this->result['PHOTOS_OFFERS_FOR_VK']))
  214. $newPhotos += $this->result['PHOTOS_OFFERS_FOR_VK'];
  215. if (isset($this->result['PHOTO_MAIN']) && !empty($this->result['PHOTO_MAIN']))
  216. $newPhotos += $this->result['PHOTO_MAIN'];
  217. if (isset($this->result['PHOTOS']) && !empty($this->result['PHOTOS']))
  218. $newPhotos += $this->result['PHOTOS'];
  219. if (isset($this->result['PHOTOS_OFFERS']) && !empty($this->result['PHOTOS_OFFERS']))
  220. $newPhotos += $this->result['PHOTOS_OFFERS'];
  221. // delete wasted photos
  222. unset(
  223. $this->result['PHOTOS_FOR_VK'],
  224. $this->result['PHOTOS_OFFERS_FOR_VK'],
  225. $this->result['PHOTOS'],
  226. $this->result['PHOTO_MAIN'],
  227. $this->result['PHOTOS_OFFERS']
  228. );
  229. return $newPhotos;
  230. }
  231. /**
  232. * Get description, prices and photos by SKUs
  233. *
  234. * @param $data
  235. * @return array
  236. * @throws \Bitrix\Main\ArgumentException
  237. * @throws \Bitrix\Main\SystemException
  238. */
  239. private function getItemDataOffersOffer($data)
  240. {
  241. $result = array("DESCRIPTION" => "");
  242. // create description for SKU PROPERTIES
  243. $propertyDescriptions = array();
  244. foreach ($this->selectOfferProps as $prop)
  245. {
  246. if ($propValue = $data["PROPERTIES"][$prop]["VALUE"])
  247. {
  248. // check if HIGLOADBLOCKS
  249. if ($data["PROPERTIES"][$prop]["USER_TYPE"] == 'directory')
  250. {
  251. if (\CModule::IncludeModule('highloadblock'))
  252. {
  253. // get ID for hl-block
  254. $resHlBlocks = HighloadBlockTable::getList(array(
  255. 'filter' => array('=TABLE_NAME' => $data["PROPERTIES"][$prop]["USER_TYPE_SETTINGS"]["TABLE_NAME"]),
  256. ));
  257. $hlBlockItemId = $resHlBlocks->fetch();
  258. $hlBlockItemId = $hlBlockItemId['ID'];
  259. // HL directory may not exist in some strange situations
  260. if(!$hlBlockItemId)
  261. continue;
  262. // get entity class for current hl
  263. $hlBlock = HighloadBlockTable::getById($hlBlockItemId)->fetch();
  264. $hlEntity = HighloadBlockTable::compileEntity($hlBlock);
  265. $strEntityDataClass = $hlEntity->getDataClass();
  266. // get value for current hl
  267. $resData = $strEntityDataClass::getList(array(
  268. 'select' => array('ID', 'UF_NAME'),
  269. 'filter' => array('=UF_XML_ID' => $propValue),
  270. ));
  271. $propValue = $resData->fetch();
  272. $propValue = $propValue['UF_NAME'];
  273. }
  274. }
  275. if(is_array($propValue))
  276. $propValue = implode(', ', $propValue);
  277. $propertyDescriptions[] = $data["PROPERTIES"][$prop]["NAME"] . ": " . $propValue;
  278. }
  279. }
  280. if (!empty($propertyDescriptions))
  281. $result["DESCRIPTION_PROPERTIES"] = implode("; ", $propertyDescriptions);
  282. // adding MAIN DESCRIPTION
  283. $description = strip_tags($data["~DETAIL_TEXT"] <> '' ? $data["~DETAIL_TEXT"] : $data["~PREVIEW_TEXT"]);
  284. if ($description)
  285. $result["DESCRIPTION"] .= $description;
  286. // adding PRICE. Ib desc we adding prices later
  287. $result['PRICE'] = $data["PRICES"]["MIN_RUB"];
  288. // adding PHOTOS
  289. $photoId = ($data["DETAIL_PICTURE"] <> '') ? $data["DETAIL_PICTURE"] : $data["PREVIEW_PICTURE"];
  290. if ($photoId)
  291. $result["PHOTOS"] = array($photoId => array("PHOTO_BX_ID" => $photoId));
  292. // adding special VK photos
  293. $vkPhotosKey = 'PHOTOS_FOR_VK_' . $data["IBLOCK_ID"];
  294. $resOfferProps = new \_CIBElement();
  295. $resOfferProps->fields = array("IBLOCK_ID" => $data["IBLOCK_ID"], "ID" => $data["ID"]);
  296. $resOfferProps = $resOfferProps->GetProperties(array(), array("CODE" => $vkPhotosKey));
  297. if (!empty($resOfferProps[$vkPhotosKey]["VALUE"]))
  298. {
  299. foreach ($resOfferProps[$vkPhotosKey]["VALUE"] as $ph)
  300. {
  301. $result["PHOTOS_FOR_VK"][$ph] = array(
  302. "PHOTO_BX_ID" => $ph,
  303. );
  304. }
  305. }
  306. return $result;
  307. }
  308. /**
  309. * Get main (not SKU) data.
  310. *
  311. * @param $data
  312. * @return array
  313. */
  314. private function getNotSkuItemData($data)
  315. {
  316. $result = array();
  317. $result["BX_ID"] = $data["ID"];
  318. $result["IBLOCK_ID"] = $data["IBLOCK_ID"];
  319. $result["NAME"] = $data["~NAME"];
  320. $result["SECTION_ID"] = $data["IBLOCK_SECTION_ID"];
  321. $result["CATEGORY_VK"] = $this->sectionsList->getVkCategory($data["IBLOCK_SECTION_ID"]);
  322. // todo: DELETED should depended by AVAILABLE
  323. $result["deleted"] = 0;
  324. $result["PRICE"] = $data["PRICES"]["MIN_RUB"]; // price converted in roubles
  325. $result["description"] = $data["~DETAIL_TEXT"] <> '' ? $data["~DETAIL_TEXT"] : $data["~PREVIEW_TEXT"];
  326. $result["description"] = trim(preg_replace('/\s{2,}/', "\n", $result["description"]));
  327. // get main photo from preview or detail
  328. $photoMainBxId = $data["DETAIL_PICTURE"] <> '' ? $data["DETAIL_PICTURE"] : $data["PREVIEW_PICTURE"];
  329. $photoMainUrl = $data["DETAIL_PICTURE_URL"] <> '' ? $data["DETAIL_PICTURE_URL"] : $data["PREVIEW_PICTURE_URL"];
  330. if ($photoMainBxId && $photoMainUrl)
  331. $result["PHOTO_MAIN"] = array(
  332. $photoMainBxId => array(
  333. "PHOTO_BX_ID" => $photoMainBxId,
  334. "PHOTO_URL" => $photoMainUrl,
  335. ),
  336. );
  337. // adding MORE PHOTOS to the all_photos array/ Later we will checked sizes
  338. if (isset($data["PROPERTIES"]["MORE_PHOTO"]["VALUE"]) &&
  339. is_array($data["PROPERTIES"]["MORE_PHOTO"]["VALUE"]) &&
  340. !empty($data["PROPERTIES"]["MORE_PHOTO"]["VALUE"])
  341. )
  342. {
  343. foreach ($data["PROPERTIES"]["MORE_PHOTO"]["VALUE"] as $ph)
  344. {
  345. $result["PHOTOS"][$ph] = array("PHOTO_BX_ID" => $ph);
  346. }
  347. }
  348. // take special VK photos
  349. $vkPhotosKey = 'PHOTOS_FOR_VK_' . $data["IBLOCK_ID"];
  350. if (isset($data["PROPERTIES"][$vkPhotosKey]["VALUE"]) &&
  351. is_array($data["PROPERTIES"][$vkPhotosKey]["VALUE"]) &&
  352. !empty($data["PROPERTIES"][$vkPhotosKey]["VALUE"])
  353. )
  354. {
  355. foreach ($data["PROPERTIES"][$vkPhotosKey]["VALUE"] as $ph)
  356. {
  357. $result["PHOTOS_FOR_VK"][$ph] = array(
  358. "PHOTO_BX_ID" => $ph,
  359. );
  360. }
  361. }
  362. return $result;
  363. }
  364. }