PageRenderTime 72ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php

https://gitlab.com/svillegas/magento2
PHP | 449 lines | 260 code | 47 blank | 142 comment | 9 complexity | 34bf813f8b2b5a99f985540c7094721d MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\ConfigurableProduct\Ui\DataProvider\Product\Form\Modifier\Data;
  7. use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableType;
  8. use Magento\Catalog\Api\ProductRepositoryInterface;
  9. use Magento\Catalog\Model\Locator\LocatorInterface;
  10. use Magento\Catalog\Model\Product;
  11. use Magento\Framework\Exception\NoSuchEntityException;
  12. use Magento\CatalogInventory\Api\StockRegistryInterface;
  13. use Magento\ConfigurableProduct\Model\Product\Type\VariationMatrix;
  14. use Magento\Framework\UrlInterface;
  15. use Magento\Framework\Locale\CurrencyInterface;
  16. use Magento\Framework\Json\Helper\Data as JsonHelper;
  17. use Magento\Catalog\Helper\Image as ImageHelper;
  18. /**
  19. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  20. */
  21. class AssociatedProducts
  22. {
  23. /**
  24. * @var LocatorInterface
  25. */
  26. protected $locator;
  27. /**
  28. * @var ConfigurableType
  29. */
  30. protected $configurableType;
  31. /**
  32. * @var ProductRepositoryInterface
  33. */
  34. protected $productRepository;
  35. /**
  36. * @var StockRegistryInterface
  37. */
  38. protected $stockRegistry;
  39. /**
  40. * @var array
  41. */
  42. protected $productMatrix = [];
  43. /**
  44. * @var array
  45. */
  46. protected $productAttributes = [];
  47. /**
  48. * @var array
  49. */
  50. protected $productIds = [];
  51. /**
  52. * @var VariationMatrix
  53. */
  54. protected $variationMatrix;
  55. /**
  56. * @var UrlInterface
  57. */
  58. protected $urlBuilder;
  59. /**
  60. * @var CurrencyInterface
  61. */
  62. protected $localeCurrency;
  63. /**
  64. * @var JsonHelper
  65. */
  66. protected $jsonHelper;
  67. /**
  68. * @var ImageHelper
  69. */
  70. protected $imageHelper;
  71. /**
  72. * @param LocatorInterface $locator
  73. * @param UrlInterface $urlBuilder
  74. * @param ConfigurableType $configurableType
  75. * @param ProductRepositoryInterface $productRepository
  76. * @param StockRegistryInterface $stockRegistry
  77. * @param VariationMatrix $variationMatrix
  78. * @param CurrencyInterface $localeCurrency
  79. * @param JsonHelper $jsonHelper
  80. * @param ImageHelper $imageHelper
  81. */
  82. public function __construct(
  83. LocatorInterface $locator,
  84. UrlInterface $urlBuilder,
  85. ConfigurableType $configurableType,
  86. ProductRepositoryInterface $productRepository,
  87. StockRegistryInterface $stockRegistry,
  88. VariationMatrix $variationMatrix,
  89. CurrencyInterface $localeCurrency,
  90. JsonHelper $jsonHelper,
  91. ImageHelper $imageHelper
  92. ) {
  93. $this->locator = $locator;
  94. $this->urlBuilder = $urlBuilder;
  95. $this->configurableType = $configurableType;
  96. $this->productRepository = $productRepository;
  97. $this->stockRegistry = $stockRegistry;
  98. $this->variationMatrix = $variationMatrix;
  99. $this->localeCurrency = $localeCurrency;
  100. $this->jsonHelper = $jsonHelper;
  101. $this->imageHelper = $imageHelper;
  102. }
  103. /**
  104. * Get variations product matrix
  105. *
  106. * @return array
  107. */
  108. public function getProductMatrix()
  109. {
  110. if ($this->productMatrix === []) {
  111. $this->prepareVariations();
  112. }
  113. return $this->productMatrix;
  114. }
  115. /**
  116. * Get product attributes
  117. *
  118. * @return array
  119. */
  120. public function getProductAttributes()
  121. {
  122. if ($this->productAttributes === []) {
  123. $this->prepareVariations();
  124. }
  125. return $this->productAttributes;
  126. }
  127. /**
  128. * Get ids of associated products
  129. *
  130. * @return array
  131. */
  132. public function getProductIds()
  133. {
  134. if ($this->productIds === []) {
  135. $this->prepareVariations();
  136. }
  137. return $this->productIds;
  138. }
  139. /**
  140. * Get ids of product attributes
  141. *
  142. * @return array
  143. */
  144. public function getProductAttributesIds()
  145. {
  146. $result = [];
  147. foreach ($this->getProductAttributes() as $attribute) {
  148. $result[] = $attribute['id'];
  149. }
  150. return $result;
  151. }
  152. /**
  153. * Get codes of product attributes
  154. *
  155. * @return array
  156. */
  157. public function getProductAttributesCodes()
  158. {
  159. $result = [];
  160. foreach ($this->getProductAttributes() as $attribute) {
  161. $result[] = $attribute['code'];
  162. }
  163. return $result;
  164. }
  165. /**
  166. * Get full data of configurable product attributes
  167. *
  168. * @return array
  169. */
  170. public function getConfigurableAttributesData()
  171. {
  172. $result = [];
  173. foreach ($this->getProductAttributes() as $attribute) {
  174. $result[$attribute['id']] = [
  175. 'attribute_id' => $attribute['id'],
  176. 'code' => $attribute['code'],
  177. 'label' => $attribute['label'],
  178. 'position' => $attribute['position'],
  179. ];
  180. foreach ($attribute['chosen'] as $chosenOption) {
  181. $result[$attribute['id']]['values'][$chosenOption['value']] = [
  182. 'include' => 1,
  183. 'value_index' => $chosenOption['value'],
  184. ];
  185. }
  186. }
  187. return $result;
  188. }
  189. /**
  190. * Prepare variations
  191. *
  192. * @return void
  193. * @throws \Zend_Currency_Exception
  194. */
  195. protected function prepareVariations()
  196. {
  197. $variations = $this->getVariations();
  198. $productMatrix = [];
  199. $attributes = [];
  200. $productIds = [];
  201. if ($variations) {
  202. $usedProductAttributes = $this->getUsedAttributes();
  203. $productByUsedAttributes = $this->getAssociatedProducts();
  204. $currency = $this->localeCurrency->getCurrency($this->locator->getBaseCurrencyCode());
  205. $configurableAttributes = $this->getAttributes();
  206. foreach ($variations as $variation) {
  207. $attributeValues = [];
  208. foreach ($usedProductAttributes as $attribute) {
  209. $attributeValues[$attribute->getAttributeCode()] = $variation[$attribute->getId()]['value'];
  210. }
  211. $key = implode('-', $attributeValues);
  212. if (isset($productByUsedAttributes[$key])) {
  213. $product = $productByUsedAttributes[$key];
  214. $price = $product->getPrice();
  215. $variationOptions = [];
  216. foreach ($usedProductAttributes as $attribute) {
  217. if (!isset($attributes[$attribute->getAttributeId()])) {
  218. $attributes[$attribute->getAttributeId()] = [
  219. 'code' => $attribute->getAttributeCode(),
  220. 'label' => $attribute->getStoreLabel(),
  221. 'id' => $attribute->getAttributeId(),
  222. 'position' => $configurableAttributes[$attribute->getAttributeId()]['position'],
  223. 'chosen' => [],
  224. ];
  225. foreach ($attribute->getOptions() as $option) {
  226. if (!empty($option->getValue())) {
  227. $attributes[$attribute->getAttributeId()]['options'][$option->getValue()] = [
  228. 'attribute_code' => $attribute->getAttributeCode(),
  229. 'attribute_label' => $attribute->getStoreLabel(0),
  230. 'id' => $option->getValue(),
  231. 'label' => $option->getLabel(),
  232. 'value' => $option->getValue(),
  233. ];
  234. }
  235. }
  236. }
  237. $optionId = $variation[$attribute->getId()]['value'];
  238. $variationOption = [
  239. 'attribute_code' => $attribute->getAttributeCode(),
  240. 'attribute_label' => $attribute->getStoreLabel(0),
  241. 'id' => $optionId,
  242. 'label' => $variation[$attribute->getId()]['label'],
  243. 'value' => $optionId,
  244. ];
  245. $variationOptions[] = $variationOption;
  246. $attributes[$attribute->getAttributeId()]['chosen'][$optionId] = $variationOption;
  247. }
  248. $productMatrix[] = [
  249. 'id' => $product->getId(),
  250. 'product_link' => '<a href="' . $this->urlBuilder->getUrl(
  251. 'catalog/product/edit',
  252. ['id' => $product->getId()]
  253. ) . '" target="_blank">' . $product->getName() . '</a>',
  254. 'sku' => $product->getSku(),
  255. 'name' => $product->getName(),
  256. 'qty' => $this->getProductStockQty($product),
  257. 'price' => $currency->toCurrency(sprintf("%f", $price), ['display' => false]),
  258. 'price_string' => $currency->toCurrency(sprintf("%f", $price)),
  259. 'price_currency' => $this->locator->getStore()->getBaseCurrency()->getCurrencySymbol(),
  260. 'configurable_attribute' => $this->getJsonConfigurableAttributes($variationOptions),
  261. 'weight' => $product->getWeight(),
  262. 'status' => $product->getStatus(),
  263. 'variationKey' => $this->getVariationKey($variationOptions),
  264. 'canEdit' => 0,
  265. 'newProduct' => 0,
  266. 'attributes' => $this->getTextAttributes($variationOptions),
  267. 'thumbnail_image' => $this->imageHelper->init($product, 'product_thumbnail_image')->getUrl(),
  268. ];
  269. $productIds[] = $product->getId();
  270. }
  271. }
  272. }
  273. $this->productMatrix = $productMatrix;
  274. $this->productIds = $productIds;
  275. $this->productAttributes = array_values($attributes);
  276. }
  277. /**
  278. * Get JSON string that contains attribute code and value
  279. *
  280. * @param array $options
  281. * @return string
  282. */
  283. protected function getJsonConfigurableAttributes(array $options = [])
  284. {
  285. $result = [];
  286. foreach ($options as $option) {
  287. $result[$option['attribute_code']] = $option['value'];
  288. }
  289. return $this->jsonHelper->jsonEncode($result);
  290. }
  291. /**
  292. * Prepares text list of used attributes
  293. *
  294. * @param array $options
  295. * @return string
  296. */
  297. protected function getTextAttributes(array $options = [])
  298. {
  299. $text = '';
  300. foreach ($options as $option) {
  301. if ($text) {
  302. $text .= ', ';
  303. }
  304. $text .= $option['attribute_label'] . ': ' . $option['label'];
  305. }
  306. return $text;
  307. }
  308. /**
  309. * Get variation key
  310. *
  311. * @param array $options
  312. * @return string
  313. */
  314. protected function getVariationKey(array $options = [])
  315. {
  316. $result = [];
  317. foreach ($options as $option) {
  318. $result[] = $option['value'];
  319. }
  320. asort($result);
  321. return implode('-', $result);
  322. }
  323. /**
  324. * Retrieve actual list of associated products, array key is obtained from varying attributes values
  325. *
  326. * @return Product[]
  327. */
  328. protected function getAssociatedProducts()
  329. {
  330. $productByUsedAttributes = [];
  331. foreach ($this->_getAssociatedProducts() as $product) {
  332. $keys = [];
  333. foreach ($this->getUsedAttributes() as $attribute) {
  334. /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
  335. $keys[] = $product->getData($attribute->getAttributeCode());
  336. }
  337. $productByUsedAttributes[implode('-', $keys)] = $product;
  338. }
  339. return $productByUsedAttributes;
  340. }
  341. /**
  342. * Retrieve actual list of associated products (i.e. if product contains variations matrix form data
  343. * - previously saved in database relations are not considered)
  344. *
  345. * @return Product[]
  346. */
  347. protected function _getAssociatedProducts()
  348. {
  349. $product = $this->locator->getProduct();
  350. $ids = $this->locator->getProduct()->getAssociatedProductIds();
  351. if ($ids === null) {
  352. // form data overrides any relations stored in database
  353. return $this->configurableType->getUsedProducts($product);
  354. }
  355. $products = [];
  356. foreach ($ids as $productId) {
  357. try {
  358. $products[] = $this->productRepository->getById($productId);
  359. } catch (NoSuchEntityException $e) {
  360. continue;
  361. }
  362. }
  363. return $products;
  364. }
  365. /**
  366. * Get used product attributes
  367. *
  368. * @return array
  369. */
  370. protected function getUsedAttributes()
  371. {
  372. return $this->configurableType->getUsedProductAttributes($this->locator->getProduct());
  373. }
  374. /**
  375. * Retrieve qty of product
  376. *
  377. * @param Product $product
  378. * @return float
  379. */
  380. protected function getProductStockQty(Product $product)
  381. {
  382. return $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId())->getQty();
  383. }
  384. /**
  385. * Retrieve all possible attribute values combinations
  386. *
  387. * @return array
  388. */
  389. protected function getVariations()
  390. {
  391. return $this->variationMatrix->getVariations($this->getAttributes());
  392. }
  393. /**
  394. * Retrieve attributes data
  395. *
  396. * @return array
  397. */
  398. protected function getAttributes()
  399. {
  400. return (array)$this->configurableType->getConfigurableAttributesAsArray($this->locator->getProduct());
  401. }
  402. }