SaveHandler.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Bundle\Model\Product;
  7. use Magento\Bundle\Model\Option\SaveAction;
  8. use Magento\Catalog\Api\Data\ProductInterface;
  9. use Magento\Bundle\Api\ProductOptionRepositoryInterface as OptionRepository;
  10. use Magento\Bundle\Api\ProductLinkManagementInterface;
  11. use Magento\Framework\App\ObjectManager;
  12. use Magento\Framework\EntityManager\MetadataPool;
  13. use Magento\Framework\EntityManager\Operation\ExtensionInterface;
  14. /**
  15. * Class SaveHandler
  16. */
  17. class SaveHandler implements ExtensionInterface
  18. {
  19. /**
  20. * @var OptionRepository
  21. */
  22. protected $optionRepository;
  23. /**
  24. * @var ProductLinkManagementInterface
  25. */
  26. protected $productLinkManagement;
  27. /**
  28. * @var MetadataPool
  29. */
  30. private $metadataPool;
  31. /**
  32. * @var SaveAction
  33. */
  34. private $optionSave;
  35. /**
  36. * @param OptionRepository $optionRepository
  37. * @param ProductLinkManagementInterface $productLinkManagement
  38. * @param SaveAction $optionSave
  39. * @param MetadataPool|null $metadataPool
  40. */
  41. public function __construct(
  42. OptionRepository $optionRepository,
  43. ProductLinkManagementInterface $productLinkManagement,
  44. SaveAction $optionSave,
  45. MetadataPool $metadataPool = null
  46. ) {
  47. $this->optionRepository = $optionRepository;
  48. $this->productLinkManagement = $productLinkManagement;
  49. $this->optionSave = $optionSave;
  50. $this->metadataPool = $metadataPool
  51. ?: ObjectManager::getInstance()->get(MetadataPool::class);
  52. }
  53. /**
  54. * Perform action on Bundle product relation/extension attribute
  55. *
  56. * @param object $entity
  57. * @param array $arguments
  58. *
  59. * @return ProductInterface|object
  60. *
  61. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  62. */
  63. public function execute($entity, $arguments = [])
  64. {
  65. /** @var \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions */
  66. $bundleProductOptions = $entity->getExtensionAttributes()->getBundleProductOptions() ?: [];
  67. //Only processing bundle products.
  68. if ($entity->getTypeId() !== Type::TYPE_CODE || empty($bundleProductOptions)) {
  69. return $entity;
  70. }
  71. $existingBundleProductOptions = $this->optionRepository->getList($entity->getSku());
  72. $existingOptionsIds = !empty($existingBundleProductOptions)
  73. ? $this->getOptionIds($existingBundleProductOptions)
  74. : [];
  75. $optionIds = !empty($bundleProductOptions)
  76. ? $this->getOptionIds($bundleProductOptions)
  77. : [];
  78. if (!$entity->getCopyFromView()) {
  79. $this->processRemovedOptions($entity, $existingOptionsIds, $optionIds);
  80. $newOptionsIds = array_diff($optionIds, $existingOptionsIds);
  81. $this->saveOptions($entity, $bundleProductOptions, $newOptionsIds);
  82. } else {
  83. //save only labels and not selections + product links
  84. $this->saveOptions($entity, $bundleProductOptions);
  85. $entity->setCopyFromView(false);
  86. }
  87. return $entity;
  88. }
  89. /**
  90. * Remove option product links
  91. *
  92. * @param string $entitySku
  93. * @param \Magento\Bundle\Api\Data\OptionInterface $option
  94. * @return void
  95. */
  96. protected function removeOptionLinks($entitySku, $option)
  97. {
  98. $links = $option->getProductLinks();
  99. if (!empty($links)) {
  100. foreach ($links as $link) {
  101. $this->productLinkManagement->removeChild($entitySku, $option->getId(), $link->getSku());
  102. }
  103. }
  104. }
  105. /**
  106. * Perform save for all options entities.
  107. *
  108. * @param object $entity
  109. * @param array $options
  110. * @param array $newOptionsIds
  111. * @return void
  112. */
  113. private function saveOptions($entity, array $options, array $newOptionsIds = []): void
  114. {
  115. foreach ($options as $option) {
  116. if (in_array($option->getOptionId(), $newOptionsIds, true)) {
  117. $option->setOptionId(null);
  118. }
  119. $this->optionSave->save($entity, $option);
  120. }
  121. }
  122. /**
  123. * Get options ids from array of the options entities.
  124. *
  125. * @param array $options
  126. * @return array
  127. */
  128. private function getOptionIds(array $options): array
  129. {
  130. $optionIds = [];
  131. if (!empty($options)) {
  132. /** @var \Magento\Bundle\Api\Data\OptionInterface $option */
  133. foreach ($options as $option) {
  134. if ($option->getOptionId()) {
  135. $optionIds[] = $option->getOptionId();
  136. }
  137. }
  138. }
  139. return $optionIds;
  140. }
  141. /**
  142. * Removes old options that no longer exists.
  143. *
  144. * @param ProductInterface $entity
  145. * @param array $existingOptionsIds
  146. * @param array $optionIds
  147. * @return void
  148. */
  149. private function processRemovedOptions(ProductInterface $entity, array $existingOptionsIds, array $optionIds): void
  150. {
  151. $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
  152. $parentId = $entity->getData($metadata->getLinkField());
  153. foreach (array_diff($existingOptionsIds, $optionIds) as $optionId) {
  154. $option = $this->optionRepository->get($entity->getSku(), $optionId);
  155. $option->setParentId($parentId);
  156. $this->removeOptionLinks($entity->getSku(), $option);
  157. $this->optionRepository->delete($option);
  158. }
  159. }
  160. }