DataProvider.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Customer\Model\Customer;
  7. use Magento\Customer\Api\Data\AddressInterface;
  8. use Magento\Customer\Api\Data\CustomerInterface;
  9. use Magento\Customer\Model\Address;
  10. use Magento\Customer\Model\Attribute;
  11. use Magento\Customer\Model\Customer;
  12. use Magento\Customer\Model\FileProcessorFactory;
  13. use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites;
  14. use Magento\Customer\Model\ResourceModel\Customer\Collection;
  15. use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory;
  16. use Magento\Eav\Api\Data\AttributeInterface;
  17. use Magento\Eav\Model\Config;
  18. use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
  19. use Magento\Eav\Model\Entity\Type;
  20. use Magento\Framework\App\ObjectManager;
  21. use Magento\Framework\Session\SessionManagerInterface;
  22. use Magento\Framework\View\Element\UiComponent\ContextInterface;
  23. use Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool;
  24. use Magento\Ui\Component\Form\Element\Multiline;
  25. use Magento\Ui\Component\Form\Field;
  26. use Magento\Ui\DataProvider\EavValidationRules;
  27. use Magento\Customer\Model\FileUploaderDataResolver;
  28. /**
  29. * Supplies the data for the customer UI component
  30. *
  31. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  32. * @SuppressWarnings(PHPMD.TooManyFields)
  33. *
  34. * @deprecated 102.0.1 \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses is used instead
  35. * @api
  36. * @since 100.0.2
  37. */
  38. class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
  39. {
  40. /**
  41. * Maximum file size allowed for file_uploader UI component
  42. */
  43. const MAX_FILE_SIZE = 2097152;
  44. /**
  45. * @var Collection
  46. */
  47. protected $collection;
  48. /**
  49. * @var Config
  50. */
  51. protected $eavConfig;
  52. /**
  53. * @var FilterPool
  54. */
  55. protected $filterPool;
  56. /**
  57. * @var array
  58. */
  59. protected $loadedData;
  60. /**
  61. * @var CountryWithWebsites
  62. */
  63. private $countryWithWebsiteSource;
  64. /**
  65. * @var \Magento\Customer\Model\Config\Share
  66. */
  67. private $shareConfig;
  68. /**
  69. * EAV attribute properties to fetch from meta storage
  70. * @var array
  71. */
  72. protected $metaProperties = [
  73. 'dataType' => 'frontend_input',
  74. 'visible' => 'is_visible',
  75. 'required' => 'is_required',
  76. 'label' => 'frontend_label',
  77. 'sortOrder' => 'sort_order',
  78. 'notice' => 'note',
  79. 'default' => 'default_value',
  80. 'size' => 'multiline_count',
  81. ];
  82. /**
  83. * Form element mapping
  84. *
  85. * @var array
  86. */
  87. protected $formElement = [
  88. 'text' => 'input',
  89. 'hidden' => 'input',
  90. 'boolean' => 'checkbox',
  91. ];
  92. /**
  93. * @var EavValidationRules
  94. */
  95. protected $eavValidationRules;
  96. /**
  97. * @var SessionManagerInterface
  98. * @since 100.1.0
  99. */
  100. protected $session;
  101. /**
  102. * Customer fields that must be removed
  103. *
  104. * @var array
  105. */
  106. private $forbiddenCustomerFields = [
  107. 'password_hash',
  108. 'rp_token',
  109. 'confirmation',
  110. ];
  111. /*
  112. * @var ContextInterface
  113. */
  114. private $context;
  115. /**
  116. * Allow to manage attributes, even they are hidden on storefront
  117. *
  118. * @var bool
  119. */
  120. private $allowToShowHiddenAttributes;
  121. /**
  122. * @var FileUploaderDataResolver
  123. */
  124. private $fileUploaderDataResolver;
  125. /**
  126. * @param string $name
  127. * @param string $primaryFieldName
  128. * @param string $requestFieldName
  129. * @param EavValidationRules $eavValidationRules
  130. * @param CustomerCollectionFactory $customerCollectionFactory
  131. * @param Config $eavConfig
  132. * @param FilterPool $filterPool
  133. * @param FileProcessorFactory $fileProcessorFactory
  134. * @param array $meta
  135. * @param array $data
  136. * @param ContextInterface $context
  137. * @param bool $allowToShowHiddenAttributes
  138. * @param FileUploaderDataResolver|null $fileUploaderDataResolver
  139. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  140. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  141. */
  142. public function __construct(
  143. $name,
  144. $primaryFieldName,
  145. $requestFieldName,
  146. EavValidationRules $eavValidationRules,
  147. CustomerCollectionFactory $customerCollectionFactory,
  148. Config $eavConfig,
  149. FilterPool $filterPool,
  150. FileProcessorFactory $fileProcessorFactory = null,
  151. array $meta = [],
  152. array $data = [],
  153. ContextInterface $context = null,
  154. $allowToShowHiddenAttributes = true,
  155. $fileUploaderDataResolver = null
  156. ) {
  157. parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
  158. $this->eavValidationRules = $eavValidationRules;
  159. $this->collection = $customerCollectionFactory->create();
  160. $this->collection->addAttributeToSelect('*');
  161. $this->eavConfig = $eavConfig;
  162. $this->filterPool = $filterPool;
  163. $this->context = $context ?: ObjectManager::getInstance()->get(ContextInterface::class);
  164. $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes;
  165. $this->fileUploaderDataResolver = $fileUploaderDataResolver
  166. ?: ObjectManager::getInstance()->get(FileUploaderDataResolver::class);
  167. $this->meta['customer']['children'] = $this->getAttributesMeta(
  168. $this->eavConfig->getEntityType('customer')
  169. );
  170. $this->meta['address']['children'] = $this->getAttributesMeta(
  171. $this->eavConfig->getEntityType('customer_address')
  172. );
  173. }
  174. /**
  175. * Get session object
  176. *
  177. * @return SessionManagerInterface
  178. * @deprecated 100.1.3
  179. * @since 100.1.0
  180. */
  181. protected function getSession()
  182. {
  183. if ($this->session === null) {
  184. $this->session = ObjectManager::getInstance()->get(
  185. \Magento\Framework\Session\SessionManagerInterface::class
  186. );
  187. }
  188. return $this->session;
  189. }
  190. /**
  191. * Get data
  192. *
  193. * @return array
  194. */
  195. public function getData()
  196. {
  197. if (isset($this->loadedData)) {
  198. return $this->loadedData;
  199. }
  200. $items = $this->collection->getItems();
  201. /** @var Customer $customer */
  202. foreach ($items as $customer) {
  203. $result['customer'] = $customer->getData();
  204. $this->fileUploaderDataResolver->overrideFileUploaderData($customer, $result['customer']);
  205. $result['customer'] = array_diff_key(
  206. $result['customer'],
  207. array_flip($this->forbiddenCustomerFields)
  208. );
  209. unset($result['address']);
  210. /** @var Address $address */
  211. foreach ($customer->getAddresses() as $address) {
  212. $addressId = $address->getId();
  213. $address->load($addressId);
  214. $result['address'][$addressId] = $address->getData();
  215. $this->prepareAddressData($addressId, $result['address'], $result['customer']);
  216. $this->fileUploaderDataResolver->overrideFileUploaderData($address, $result['address'][$addressId]);
  217. }
  218. $this->loadedData[$customer->getId()] = $result;
  219. }
  220. $data = $this->getSession()->getCustomerFormData();
  221. if (!empty($data)) {
  222. $customerId = isset($data['customer']['entity_id']) ? $data['customer']['entity_id'] : null;
  223. $this->loadedData[$customerId] = $data;
  224. $this->getSession()->unsCustomerFormData();
  225. }
  226. return $this->loadedData;
  227. }
  228. /**
  229. * Get attributes meta
  230. *
  231. * @param Type $entityType
  232. * @return array
  233. * @throws \Magento\Framework\Exception\LocalizedException
  234. */
  235. protected function getAttributesMeta(Type $entityType)
  236. {
  237. $meta = [];
  238. $attributes = $entityType->getAttributeCollection();
  239. /* @var AbstractAttribute $attribute */
  240. foreach ($attributes as $attribute) {
  241. $this->processFrontendInput($attribute, $meta);
  242. $code = $attribute->getAttributeCode();
  243. // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value
  244. foreach ($this->metaProperties as $metaName => $origName) {
  245. $value = $attribute->getDataUsingMethod($origName);
  246. $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value;
  247. if ('frontend_input' === $origName) {
  248. $meta[$code]['arguments']['data']['config']['formElement'] = isset($this->formElement[$value])
  249. ? $this->formElement[$value]
  250. : $value;
  251. }
  252. }
  253. if ($attribute->usesSource()) {
  254. if ($code == AddressInterface::COUNTRY_ID) {
  255. $meta[$code]['arguments']['data']['config']['options'] = $this->getCountryWithWebsiteSource()
  256. ->getAllOptions();
  257. } else {
  258. $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions();
  259. }
  260. }
  261. $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']);
  262. if (!empty($rules)) {
  263. $meta[$code]['arguments']['data']['config']['validation'] = $rules;
  264. }
  265. $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME;
  266. $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute);
  267. $this->fileUploaderDataResolver->overrideFileUploaderMetadata(
  268. $entityType,
  269. $attribute,
  270. $meta[$code]['arguments']['data']['config']
  271. );
  272. }
  273. $this->processWebsiteMeta($meta);
  274. return $meta;
  275. }
  276. /**
  277. * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc...
  278. *
  279. * @param Attribute $customerAttribute
  280. * @return bool
  281. */
  282. private function canShowAttributeInForm(AbstractAttribute $customerAttribute)
  283. {
  284. $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null;
  285. if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') {
  286. return is_array($customerAttribute->getUsedInForms()) &&
  287. (
  288. (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) ||
  289. (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration)
  290. );
  291. } else {
  292. return is_array($customerAttribute->getUsedInForms()) &&
  293. in_array('customer_address_edit', $customerAttribute->getUsedInForms());
  294. }
  295. }
  296. /**
  297. * Detect can we show attribute on specific form or not
  298. *
  299. * @param Attribute $customerAttribute
  300. * @return bool
  301. */
  302. private function canShowAttribute(AbstractAttribute $customerAttribute)
  303. {
  304. $userDefined = (bool) $customerAttribute->getIsUserDefined();
  305. if (!$userDefined) {
  306. return $customerAttribute->getIsVisible();
  307. }
  308. $canShowOnForm = $this->canShowAttributeInForm($customerAttribute);
  309. return ($this->allowToShowHiddenAttributes && $canShowOnForm) ||
  310. (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible());
  311. }
  312. /**
  313. * Retrieve Country With Websites Source
  314. *
  315. * @return CountryWithWebsites
  316. * @deprecated 101.0.0
  317. */
  318. private function getCountryWithWebsiteSource()
  319. {
  320. if (!$this->countryWithWebsiteSource) {
  321. $this->countryWithWebsiteSource = ObjectManager::getInstance()->get(CountryWithWebsites::class);
  322. }
  323. return $this->countryWithWebsiteSource;
  324. }
  325. /**
  326. * Retrieve Customer Config Share
  327. *
  328. * @return \Magento\Customer\Model\Config\Share
  329. * @deprecated 100.1.3
  330. */
  331. private function getShareConfig()
  332. {
  333. if (!$this->shareConfig) {
  334. $this->shareConfig = ObjectManager::getInstance()->get(\Magento\Customer\Model\Config\Share::class);
  335. }
  336. return $this->shareConfig;
  337. }
  338. /**
  339. * Add global scope parameter and filter options to website meta
  340. *
  341. * @param array $meta
  342. * @return void
  343. */
  344. private function processWebsiteMeta(&$meta)
  345. {
  346. if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->getShareConfig()->isGlobalScope()) {
  347. $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1;
  348. }
  349. if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->getShareConfig()->isGlobalScope()) {
  350. $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [
  351. 'target' => '${ $.provider }:data.customer.website_id',
  352. 'field' => 'website_ids'
  353. ];
  354. }
  355. }
  356. /**
  357. * Process attributes by frontend input type
  358. *
  359. * @param AttributeInterface $attribute
  360. * @param array $meta
  361. * @return array
  362. */
  363. private function processFrontendInput(AttributeInterface $attribute, array &$meta)
  364. {
  365. $code = $attribute->getAttributeCode();
  366. if ($attribute->getFrontendInput() === 'boolean') {
  367. $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle';
  368. $meta[$code]['arguments']['data']['config']['valueMap'] = [
  369. 'true' => '1',
  370. 'false' => '0',
  371. ];
  372. }
  373. }
  374. /**
  375. * Prepare address data
  376. *
  377. * @param int $addressId
  378. * @param array $addresses
  379. * @param array $customer
  380. * @return void
  381. */
  382. protected function prepareAddressData($addressId, array &$addresses, array $customer)
  383. {
  384. if (isset($customer['default_billing'])
  385. && $addressId == $customer['default_billing']
  386. ) {
  387. $addresses[$addressId]['default_billing'] = $customer['default_billing'];
  388. }
  389. if (isset($customer['default_shipping'])
  390. && $addressId == $customer['default_shipping']
  391. ) {
  392. $addresses[$addressId]['default_shipping'] = $customer['default_shipping'];
  393. }
  394. foreach ($this->meta['address']['children'] as $attributeName => $attributeMeta) {
  395. if ($attributeMeta['arguments']['data']['config']['dataType'] === Multiline::NAME
  396. && isset($addresses[$addressId][$attributeName])
  397. && !is_array($addresses[$addressId][$attributeName])
  398. ) {
  399. $addresses[$addressId][$attributeName] = explode("\n", $addresses[$addressId][$attributeName]);
  400. }
  401. }
  402. }
  403. }