JoinProcessor.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Api\ExtensionAttribute;
  7. use Magento\Framework\Api\ExtensionAttribute\Config;
  8. use Magento\Framework\Api\ExtensionAttribute\Config\Converter as Converter;
  9. use Magento\Framework\Data\Collection\AbstractDb as DbCollection;
  10. use Magento\Framework\Reflection\TypeProcessor;
  11. use Magento\Framework\Api\ExtensibleDataInterface;
  12. use Magento\Framework\Api\ExtensionAttributesFactory;
  13. /**
  14. * Join processor allows to join extension attributes during collections loading.
  15. */
  16. class JoinProcessor implements \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface
  17. {
  18. /**
  19. * Object Manager instance
  20. *
  21. * @var \Magento\Framework\ObjectManagerInterface
  22. */
  23. protected $objectManager;
  24. /**
  25. * @var \Magento\Framework\Reflection\TypeProcessor
  26. */
  27. private $typeProcessor;
  28. /**
  29. * @var \Magento\Framework\Api\ExtensionAttributesFactory
  30. */
  31. private $extensionAttributesFactory;
  32. /**
  33. * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorHelper
  34. */
  35. private $joinProcessorHelper;
  36. /**
  37. * Initialize dependencies.
  38. *
  39. * @param \Magento\Framework\ObjectManagerInterface $objectManager
  40. * @param TypeProcessor $typeProcessor
  41. * @param ExtensionAttributesFactory $extensionAttributesFactory
  42. * @param JoinProcessorHelper $joinProcessorHelper
  43. */
  44. public function __construct(
  45. \Magento\Framework\ObjectManagerInterface $objectManager,
  46. TypeProcessor $typeProcessor,
  47. ExtensionAttributesFactory $extensionAttributesFactory,
  48. JoinProcessorHelper $joinProcessorHelper
  49. ) {
  50. $this->objectManager = $objectManager;
  51. $this->typeProcessor = $typeProcessor;
  52. $this->extensionAttributesFactory = $extensionAttributesFactory;
  53. $this->joinProcessorHelper = $joinProcessorHelper;
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. public function process(DbCollection $collection, $extensibleEntityClass = null)
  59. {
  60. $extensibleEntityClass = $extensibleEntityClass ?: $collection->getItemObjectClass();
  61. $joinDirectives = $this->getJoinDirectivesForType($extensibleEntityClass);
  62. foreach ($joinDirectives as $attributeCode => $directive) {
  63. /** @var JoinDataInterface $joinData */
  64. $joinData = $this->joinProcessorHelper->getJoinDataInterface();
  65. $joinData->setAttributeCode($attributeCode)
  66. ->setReferenceTable($directive[Converter::JOIN_REFERENCE_TABLE])
  67. ->setReferenceTableAlias($this->getReferenceTableAlias($attributeCode))
  68. ->setReferenceField($directive[Converter::JOIN_REFERENCE_FIELD])
  69. ->setJoinField($directive[Converter::JOIN_ON_FIELD]);
  70. $joinData->setSelectFields(
  71. $this->joinProcessorHelper->getSelectFieldsMap($attributeCode, $directive[Converter::JOIN_FIELDS])
  72. );
  73. $collection->joinExtensionAttribute($joinData, $this);
  74. }
  75. }
  76. /**
  77. * Generate reference table alias.
  78. *
  79. * @param string $attributeCode
  80. * @return string
  81. */
  82. private function getReferenceTableAlias($attributeCode)
  83. {
  84. return 'extension_attribute_' . $attributeCode;
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public function extractExtensionAttributes($extensibleEntityClass, array $data)
  90. {
  91. if (!$this->isExtensibleAttributesImplemented($extensibleEntityClass)) {
  92. /* do nothing as there are no extension attributes */
  93. return $data;
  94. }
  95. $joinDirectives = $this->getJoinDirectivesForType($extensibleEntityClass);
  96. $extensionData = [];
  97. foreach ($joinDirectives as $attributeCode => $directive) {
  98. $this->populateAttributeCodeWithDirective(
  99. $attributeCode,
  100. $directive,
  101. $data,
  102. $extensionData,
  103. $extensibleEntityClass
  104. );
  105. }
  106. if (!empty($extensionData)) {
  107. $extensionAttributes = $this->extensionAttributesFactory->create($extensibleEntityClass, $extensionData);
  108. $data[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY] = $extensionAttributes;
  109. }
  110. return $data;
  111. }
  112. /**
  113. * Populate a specific attribute code with join directive instructions.
  114. *
  115. * @param string $attributeCode
  116. * @param array $directive
  117. * @param array &$data
  118. * @param array &$extensionData
  119. * @param string $extensibleEntityClass
  120. * @return void
  121. */
  122. private function populateAttributeCodeWithDirective(
  123. $attributeCode,
  124. $directive,
  125. &$data,
  126. &$extensionData,
  127. $extensibleEntityClass
  128. ) {
  129. $attributeType = $directive[Converter::DATA_TYPE];
  130. $selectFields = $this->joinProcessorHelper
  131. ->getSelectFieldsMap($attributeCode, $directive[Converter::JOIN_FIELDS]);
  132. foreach ($selectFields as $selectField) {
  133. $internalAlias = $selectField[JoinDataInterface::SELECT_FIELD_INTERNAL_ALIAS];
  134. if (isset($data[$internalAlias])) {
  135. if ($this->typeProcessor->isArrayType($attributeType)) {
  136. throw new \LogicException(
  137. sprintf(
  138. 'Join directives cannot be processed for attribute (%s) of extensible entity (%s),'
  139. . ' which has an Array type (%s).',
  140. $attributeCode,
  141. $this->extensionAttributesFactory->getExtensibleInterfaceName($extensibleEntityClass),
  142. $attributeType
  143. )
  144. );
  145. } elseif ($this->typeProcessor->isTypeSimple($attributeType)) {
  146. $extensionData['data'][$attributeCode] = $data[$internalAlias];
  147. unset($data[$internalAlias]);
  148. break;
  149. } else {
  150. if (!isset($extensionData['data'][$attributeCode])) {
  151. $extensionData['data'][$attributeCode] = $this->objectManager->create($attributeType);
  152. }
  153. $setterName = $selectField[JoinDataInterface::SELECT_FIELD_SETTER];
  154. $extensionData['data'][$attributeCode]->$setterName($data[$internalAlias]);
  155. unset($data[$internalAlias]);
  156. }
  157. }
  158. }
  159. }
  160. /**
  161. * Returns the internal join directive config for a given type.
  162. *
  163. * Array returned has all of the \Magento\Framework\Api\ExtensionAttribute\Config\Converter JOIN* fields set.
  164. *
  165. * @param string $extensibleEntityClass
  166. * @return array
  167. */
  168. private function getJoinDirectivesForType($extensibleEntityClass)
  169. {
  170. $extensibleInterfaceName = $this->extensionAttributesFactory
  171. ->getExtensibleInterfaceName($extensibleEntityClass);
  172. $extensibleInterfaceName = ltrim($extensibleInterfaceName, '\\');
  173. $config = $this->joinProcessorHelper->getConfigData();
  174. if (!isset($config[$extensibleInterfaceName])) {
  175. return [];
  176. }
  177. $typeAttributesConfig = $config[$extensibleInterfaceName];
  178. $joinDirectives = [];
  179. foreach ($typeAttributesConfig as $attributeCode => $attributeConfig) {
  180. if (isset($attributeConfig[Converter::JOIN_DIRECTIVE])) {
  181. $joinDirectives[$attributeCode] = $attributeConfig[Converter::JOIN_DIRECTIVE];
  182. $joinDirectives[$attributeCode][Converter::DATA_TYPE] = $attributeConfig[Converter::DATA_TYPE];
  183. }
  184. }
  185. return $joinDirectives;
  186. }
  187. /**
  188. * Determine if the type is an actual extensible data interface.
  189. *
  190. * @param string $typeName
  191. * @return bool
  192. */
  193. private function isExtensibleAttributesImplemented($typeName)
  194. {
  195. try {
  196. $this->extensionAttributesFactory->getExtensibleInterfaceName($typeName);
  197. return true;
  198. } catch (\LogicException $e) {
  199. return false;
  200. }
  201. }
  202. }