DataObjectHelper.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Api;
  7. use Magento\Framework\Reflection\MethodsMap;
  8. /**
  9. * Data object helper.
  10. *
  11. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  12. */
  13. class DataObjectHelper
  14. {
  15. /**
  16. * @var ObjectFactory
  17. */
  18. protected $objectFactory;
  19. /**
  20. * @var \Magento\Framework\Reflection\DataObjectProcessor
  21. */
  22. protected $objectProcessor;
  23. /**
  24. * @var \Magento\Framework\Reflection\TypeProcessor
  25. */
  26. protected $typeProcessor;
  27. /**
  28. * @var \Magento\Framework\Api\ExtensionAttributesFactory
  29. */
  30. protected $extensionFactory;
  31. /**
  32. * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface
  33. */
  34. protected $joinProcessor;
  35. /**
  36. * @var MethodsMap
  37. */
  38. protected $methodsMapProcessor;
  39. /**
  40. * @param ObjectFactory $objectFactory
  41. * @param \Magento\Framework\Reflection\DataObjectProcessor $objectProcessor
  42. * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor
  43. * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
  44. * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor
  45. * @param MethodsMap $methodsMapProcessor
  46. */
  47. public function __construct(
  48. ObjectFactory $objectFactory,
  49. \Magento\Framework\Reflection\DataObjectProcessor $objectProcessor,
  50. \Magento\Framework\Reflection\TypeProcessor $typeProcessor,
  51. \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
  52. \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor,
  53. MethodsMap $methodsMapProcessor
  54. ) {
  55. $this->objectFactory = $objectFactory;
  56. $this->objectProcessor = $objectProcessor;
  57. $this->typeProcessor = $typeProcessor;
  58. $this->extensionFactory = $extensionFactory;
  59. $this->joinProcessor = $joinProcessor;
  60. $this->methodsMapProcessor = $methodsMapProcessor;
  61. }
  62. /**
  63. * Populate data object using data in array format.
  64. *
  65. * @param mixed $dataObject
  66. * @param array $data
  67. * @param string $interfaceName
  68. * @return $this
  69. */
  70. public function populateWithArray($dataObject, array $data, $interfaceName)
  71. {
  72. if ($dataObject instanceof ExtensibleDataInterface) {
  73. $data = $this->joinProcessor->extractExtensionAttributes(get_class($dataObject), $data);
  74. }
  75. $this->_setDataValues($dataObject, $data, $interfaceName);
  76. return $this;
  77. }
  78. /**
  79. * Update Data Object with the data from array
  80. *
  81. * @param mixed $dataObject
  82. * @param array $data
  83. * @param string $interfaceName
  84. * @return $this
  85. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  86. */
  87. protected function _setDataValues($dataObject, array $data, $interfaceName)
  88. {
  89. $dataObjectMethods = get_class_methods(get_class($dataObject));
  90. foreach ($data as $key => $value) {
  91. /* First, verify is there any setter for the key on the Service Data Object */
  92. $camelCaseKey = \Magento\Framework\Api\SimpleDataObjectConverter::snakeCaseToUpperCamelCase($key);
  93. $possibleMethods = [
  94. 'set' . $camelCaseKey,
  95. 'setIs' . $camelCaseKey,
  96. ];
  97. if ($key === CustomAttributesDataInterface::CUSTOM_ATTRIBUTES
  98. && ($dataObject instanceof ExtensibleDataInterface)
  99. && is_array($data[$key])
  100. && !empty($data[$key])
  101. ) {
  102. foreach ($data[$key] as $customAttribute) {
  103. $dataObject->setCustomAttribute(
  104. $customAttribute[AttributeInterface::ATTRIBUTE_CODE],
  105. $customAttribute[AttributeInterface::VALUE]
  106. );
  107. }
  108. } elseif ($methodNames = array_intersect($possibleMethods, $dataObjectMethods)) {
  109. $methodName = array_values($methodNames)[0];
  110. if (!is_array($value)) {
  111. if ($methodName === 'setExtensionAttributes' && $value === null) {
  112. // Cannot pass a null value to a method with a typed parameter
  113. } else {
  114. $dataObject->$methodName($value);
  115. }
  116. } else {
  117. $getterMethodName = 'get' . $camelCaseKey;
  118. $this->setComplexValue($dataObject, $getterMethodName, $methodName, $value, $interfaceName);
  119. }
  120. } elseif ($dataObject instanceof CustomAttributesDataInterface) {
  121. $dataObject->setCustomAttribute($key, $value);
  122. }
  123. }
  124. return $this;
  125. }
  126. /**
  127. * @param mixed $dataObject
  128. * @param string $getterMethodName
  129. * @param string $methodName
  130. * @param array $value
  131. * @param string $interfaceName
  132. * @return $this
  133. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  134. */
  135. protected function setComplexValue(
  136. $dataObject,
  137. $getterMethodName,
  138. $methodName,
  139. array $value,
  140. $interfaceName
  141. ) {
  142. if ($interfaceName == null) {
  143. $interfaceName = get_class($dataObject);
  144. }
  145. $returnType = $this->methodsMapProcessor->getMethodReturnType($interfaceName, $getterMethodName);
  146. if ($this->typeProcessor->isTypeSimple($returnType)) {
  147. $dataObject->$methodName($value);
  148. return $this;
  149. }
  150. if ($this->typeProcessor->isArrayType($returnType)) {
  151. $type = $this->typeProcessor->getArrayItemType($returnType);
  152. $objects = [];
  153. foreach ($value as $arrayElementData) {
  154. $object = $this->objectFactory->create($type, []);
  155. $this->populateWithArray($object, $arrayElementData, $type);
  156. $objects[] = $object;
  157. }
  158. $dataObject->$methodName($objects);
  159. return $this;
  160. }
  161. if (is_subclass_of($returnType, \Magento\Framework\Api\ExtensibleDataInterface::class)) {
  162. $object = $this->objectFactory->create($returnType, []);
  163. $this->populateWithArray($object, $value, $returnType);
  164. } elseif (is_subclass_of($returnType, \Magento\Framework\Api\ExtensionAttributesInterface::class)) {
  165. foreach ($value as $extensionAttributeKey => $extensionAttributeValue) {
  166. $extensionAttributeGetterMethodName
  167. = 'get' . \Magento\Framework\Api\SimpleDataObjectConverter::snakeCaseToUpperCamelCase(
  168. $extensionAttributeKey
  169. );
  170. $methodReturnType = $this->methodsMapProcessor->getMethodReturnType(
  171. $returnType,
  172. $extensionAttributeGetterMethodName
  173. );
  174. $extensionAttributeType = $this->typeProcessor->isArrayType($methodReturnType)
  175. ? $this->typeProcessor->getArrayItemType($methodReturnType)
  176. : $methodReturnType;
  177. if ($this->typeProcessor->isTypeSimple($extensionAttributeType)) {
  178. $value[$extensionAttributeKey] = $extensionAttributeValue;
  179. } else {
  180. if ($this->typeProcessor->isArrayType($methodReturnType)) {
  181. foreach ($extensionAttributeValue as $key => $extensionAttributeArrayValue) {
  182. $extensionAttribute = $this->objectFactory->create($extensionAttributeType, []);
  183. $this->populateWithArray(
  184. $extensionAttribute,
  185. $extensionAttributeArrayValue,
  186. $extensionAttributeType
  187. );
  188. $value[$extensionAttributeKey][$key] = $extensionAttribute;
  189. }
  190. } else {
  191. $value[$extensionAttributeKey] = $this->objectFactory->create(
  192. $extensionAttributeType,
  193. ['data' => $extensionAttributeValue]
  194. );
  195. }
  196. }
  197. }
  198. $object = $this->extensionFactory->create(get_class($dataObject), ['data' => $value]);
  199. } else {
  200. $object = $this->objectFactory->create($returnType, $value);
  201. }
  202. $dataObject->$methodName($object);
  203. return $this;
  204. }
  205. /**
  206. * Merges second object onto the first
  207. *
  208. * @param string $interfaceName
  209. * @param mixed $firstDataObject
  210. * @param mixed $secondDataObject
  211. * @return $this
  212. * @throws \LogicException
  213. */
  214. public function mergeDataObjects(
  215. $interfaceName,
  216. $firstDataObject,
  217. $secondDataObject
  218. ) {
  219. if (!$firstDataObject instanceof $interfaceName || !$secondDataObject instanceof $interfaceName) {
  220. throw new \LogicException('Wrong prototype object given. It can only be of "' . $interfaceName . '" type.');
  221. }
  222. $secondObjectArray = $this->objectProcessor->buildOutputDataArray($secondDataObject, $interfaceName);
  223. $this->_setDataValues($firstDataObject, $secondObjectArray, $interfaceName);
  224. return $this;
  225. }
  226. /**
  227. * Filter attribute value objects for a provided data interface type from an array of custom attribute value objects
  228. *
  229. * @param AttributeValue[] $attributeValues Array of custom attribute
  230. * @param string $type Data interface type
  231. * @return AttributeValue[]
  232. */
  233. public function getCustomAttributeValueByType(array $attributeValues, $type)
  234. {
  235. $attributeValueArray = [];
  236. if (empty($attributeValues)) {
  237. return $attributeValueArray;
  238. }
  239. foreach ($attributeValues as $attributeValue) {
  240. if ($attributeValue->getValue() instanceof $type) {
  241. $attributeValueArray[] = $attributeValue;
  242. }
  243. }
  244. return $attributeValueArray;
  245. }
  246. }