ReadHandler.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Eav\Model\ResourceModel;
  7. use Magento\Eav\Model\Config;
  8. use Magento\Framework\DataObject;
  9. use Magento\Framework\DB\Select;
  10. use Magento\Framework\DB\Sql\UnionExpression;
  11. use Magento\Framework\EntityManager\MetadataPool;
  12. use Magento\Framework\EntityManager\Operation\AttributeInterface;
  13. use Magento\Framework\Model\Entity\ScopeInterface;
  14. use Magento\Framework\Model\Entity\ScopeResolver;
  15. use Psr\Log\LoggerInterface;
  16. /**
  17. * EAV read handler
  18. */
  19. class ReadHandler implements AttributeInterface
  20. {
  21. /**
  22. * @var MetadataPool
  23. */
  24. protected $metadataPool;
  25. /**
  26. * @var ScopeResolver
  27. */
  28. protected $scopeResolver;
  29. /**
  30. * @var LoggerInterface
  31. */
  32. private $logger;
  33. /**
  34. * @var Config
  35. */
  36. private $config;
  37. /**
  38. * @param MetadataPool $metadataPool
  39. * @param ScopeResolver $scopeResolver
  40. * @param LoggerInterface $logger
  41. * @param Config $config
  42. */
  43. public function __construct(
  44. MetadataPool $metadataPool,
  45. ScopeResolver $scopeResolver,
  46. LoggerInterface $logger,
  47. Config $config
  48. ) {
  49. $this->metadataPool = $metadataPool;
  50. $this->scopeResolver = $scopeResolver;
  51. $this->logger = $logger;
  52. $this->config = $config;
  53. }
  54. /**
  55. * Get attribute of given entity type
  56. *
  57. * @param string $entityType
  58. * @return \Magento\Eav\Api\Data\AttributeInterface[]
  59. * @throws \Exception if for unknown entity type
  60. * @deprecated 101.0.5 Not used anymore
  61. * @see ReadHandler::getEntityAttributes
  62. */
  63. protected function getAttributes($entityType)
  64. {
  65. $metadata = $this->metadataPool->getMetadata($entityType);
  66. $eavEntityType = $metadata->getEavEntityType();
  67. return null === $eavEntityType ? [] : $this->config->getEntityAttributes($eavEntityType);
  68. }
  69. /**
  70. * Get attribute of given entity type
  71. *
  72. * @param string $entityType
  73. * @param DataObject $entity
  74. * @return \Magento\Eav\Api\Data\AttributeInterface[]
  75. * @throws \Exception if for unknown entity type
  76. */
  77. private function getEntityAttributes(string $entityType, DataObject $entity): array
  78. {
  79. $metadata = $this->metadataPool->getMetadata($entityType);
  80. $eavEntityType = $metadata->getEavEntityType();
  81. return null === $eavEntityType ? [] : $this->config->getEntityAttributes($eavEntityType, $entity);
  82. }
  83. /**
  84. * Get context variables
  85. *
  86. * @param ScopeInterface $scope
  87. * @return array
  88. */
  89. protected function getContextVariables(ScopeInterface $scope)
  90. {
  91. $data[] = $scope->getValue();
  92. if ($scope->getFallback()) {
  93. $data = array_merge($data, $this->getContextVariables($scope->getFallback()));
  94. }
  95. return $data;
  96. }
  97. /**
  98. * Execute read handler
  99. *
  100. * @param string $entityType
  101. * @param array $entityData
  102. * @param array $arguments
  103. * @return array
  104. * @throws \Exception
  105. * @throws \Magento\Framework\Exception\ConfigurationMismatchException
  106. * @throws \Magento\Framework\Exception\LocalizedException
  107. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  108. */
  109. public function execute($entityType, $entityData, $arguments = [])
  110. {
  111. $metadata = $this->metadataPool->getMetadata($entityType);
  112. if (!$metadata->getEavEntityType()) {//todo hasCustomAttributes
  113. return $entityData;
  114. }
  115. $context = $this->scopeResolver->getEntityContext($entityType, $entityData);
  116. $connection = $metadata->getEntityConnection();
  117. $attributeTables = [];
  118. $attributesMap = [];
  119. $selects = [];
  120. /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */
  121. foreach ($this->getEntityAttributes($entityType, new DataObject($entityData)) as $attribute) {
  122. if (!$attribute->isStatic()) {
  123. $attributeTables[$attribute->getBackend()->getTable()][] = $attribute->getAttributeId();
  124. $attributesMap[$attribute->getAttributeId()] = $attribute->getAttributeCode();
  125. }
  126. }
  127. if (count($attributeTables)) {
  128. $identifiers = null;
  129. foreach ($attributeTables as $attributeTable => $attributeIds) {
  130. $select = $connection->select()
  131. ->from(
  132. ['t' => $attributeTable],
  133. ['value' => 't.value', 'attribute_id' => 't.attribute_id']
  134. )
  135. ->where($metadata->getLinkField() . ' = ?', $entityData[$metadata->getLinkField()])
  136. ->where('attribute_id IN (?)', $attributeIds);
  137. $attributeIdentifiers = [];
  138. foreach ($context as $scope) {
  139. //TODO: if (in table exists context field)
  140. $select->where(
  141. $connection->quoteIdentifier($scope->getIdentifier()) . ' IN (?)',
  142. $this->getContextVariables($scope)
  143. );
  144. $attributeIdentifiers[] = $scope->getIdentifier();
  145. }
  146. $attributeIdentifiers = array_unique($attributeIdentifiers);
  147. $identifiers = array_intersect($identifiers ?? $attributeIdentifiers, $attributeIdentifiers);
  148. $selects[] = $select;
  149. }
  150. $this->applyIdentifierForSelects($selects, $identifiers);
  151. $unionSelect = new UnionExpression($selects, Select::SQL_UNION_ALL, '( %s )');
  152. $orderedUnionSelect = $connection->select();
  153. $orderedUnionSelect->from(['u' => $unionSelect]);
  154. $this->applyIdentifierForUnion($orderedUnionSelect, $identifiers);
  155. $attributes = $connection->fetchAll($orderedUnionSelect);
  156. foreach ($attributes as $attributeValue) {
  157. if (isset($attributesMap[$attributeValue['attribute_id']])) {
  158. $entityData[$attributesMap[$attributeValue['attribute_id']]] = $attributeValue['value'];
  159. } else {
  160. $this->logger->warning(
  161. "Attempt to load value of nonexistent EAV attribute '{$attributeValue['attribute_id']}'
  162. for entity type '$entityType'."
  163. );
  164. }
  165. }
  166. }
  167. return $entityData;
  168. }
  169. /**
  170. * Apply identifiers column on select array
  171. *
  172. * @param Select[] $selects
  173. * @param array $identifiers
  174. */
  175. private function applyIdentifierForSelects(array $selects, array $identifiers)
  176. {
  177. foreach ($selects as $select) {
  178. foreach ($identifiers as $identifier) {
  179. $select->columns($identifier, 't');
  180. }
  181. }
  182. }
  183. /**
  184. * Apply identifiers order on union select
  185. *
  186. * @param Select $unionSelect
  187. * @param array $identifiers
  188. */
  189. private function applyIdentifierForUnion(Select $unionSelect, array $identifiers)
  190. {
  191. foreach ($identifiers as $identifier) {
  192. $unionSelect->order($identifier);
  193. }
  194. }
  195. }