AbstractData.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Eav\Model\Attribute\Data;
  7. use Magento\Framework\App\RequestInterface;
  8. use Magento\Framework\Exception\LocalizedException as CoreException;
  9. use Magento\Framework\Validator\EmailAddress;
  10. /**
  11. * EAV Attribute Abstract Data Model
  12. *
  13. * @api
  14. * @author Magento Core Team <core@magentocommerce.com>
  15. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  16. * @since 100.0.2
  17. */
  18. abstract class AbstractData
  19. {
  20. /**
  21. * Attribute instance
  22. *
  23. * @var \Magento\Eav\Model\Attribute
  24. */
  25. protected $_attribute;
  26. /**
  27. * Entity instance
  28. *
  29. * @var \Magento\Framework\Model\AbstractModel
  30. */
  31. protected $_entity;
  32. /**
  33. * Request Scope name
  34. *
  35. * @var string
  36. */
  37. protected $_requestScope;
  38. /**
  39. * Scope visibility flag
  40. *
  41. * @var bool
  42. */
  43. protected $_requestScopeOnly = true;
  44. /**
  45. * Is AJAX request flag
  46. *
  47. * @var bool
  48. */
  49. protected $_isAjax = false;
  50. /**
  51. * Array of full extracted data
  52. * Needed for depends attributes
  53. *
  54. * @var array
  55. */
  56. protected $_extractedData = [];
  57. /**
  58. * Date filter format
  59. *
  60. * @var string
  61. */
  62. protected $_dateFilterFormat;
  63. /**
  64. * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
  65. */
  66. protected $_localeDate;
  67. /**
  68. * @var \Magento\Framework\Locale\ResolverInterface
  69. */
  70. protected $_localeResolver;
  71. /**
  72. * @var \Psr\Log\LoggerInterface
  73. */
  74. protected $_logger;
  75. /**
  76. * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
  77. * @param \Psr\Log\LoggerInterface $logger
  78. * @param \Magento\Framework\Locale\ResolverInterface $localeResolver
  79. * @codeCoverageIgnore
  80. */
  81. public function __construct(
  82. \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
  83. \Psr\Log\LoggerInterface $logger,
  84. \Magento\Framework\Locale\ResolverInterface $localeResolver
  85. ) {
  86. $this->_localeDate = $localeDate;
  87. $this->_logger = $logger;
  88. $this->_localeResolver = $localeResolver;
  89. }
  90. /**
  91. * Set attribute instance
  92. *
  93. * @param \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute
  94. * @return $this
  95. * @codeCoverageIgnore
  96. */
  97. public function setAttribute(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute)
  98. {
  99. $this->_attribute = $attribute;
  100. return $this;
  101. }
  102. /**
  103. * Return Attribute instance
  104. *
  105. * @throws CoreException
  106. * @return \Magento\Eav\Model\Attribute
  107. */
  108. public function getAttribute()
  109. {
  110. if (!$this->_attribute) {
  111. throw new CoreException(__('Attribute object is undefined'));
  112. }
  113. return $this->_attribute;
  114. }
  115. /**
  116. * Set Request scope
  117. *
  118. * @param string $scope
  119. * @return $this
  120. * @codeCoverageIgnore
  121. */
  122. public function setRequestScope($scope)
  123. {
  124. $this->_requestScope = $scope;
  125. return $this;
  126. }
  127. /**
  128. * Set scope visibility
  129. *
  130. * Search value only in scope or search value in scope and global
  131. *
  132. * @param bool $flag
  133. * @return $this
  134. * @codeCoverageIgnore
  135. */
  136. public function setRequestScopeOnly($flag)
  137. {
  138. $this->_requestScopeOnly = (bool)$flag;
  139. return $this;
  140. }
  141. /**
  142. * Set entity instance
  143. *
  144. * @param \Magento\Framework\Model\AbstractModel $entity
  145. * @return $this
  146. * @codeCoverageIgnore
  147. */
  148. public function setEntity(\Magento\Framework\Model\AbstractModel $entity)
  149. {
  150. $this->_entity = $entity;
  151. return $this;
  152. }
  153. /**
  154. * Returns entity instance
  155. *
  156. * @return \Magento\Framework\Model\AbstractModel
  157. * @throws CoreException
  158. */
  159. public function getEntity()
  160. {
  161. if (!$this->_entity) {
  162. throw new CoreException(__('Entity object is undefined'));
  163. }
  164. return $this->_entity;
  165. }
  166. /**
  167. * Set array of full extracted data
  168. *
  169. * @param array $data
  170. * @return $this
  171. * @codeCoverageIgnore
  172. */
  173. public function setExtractedData(array $data)
  174. {
  175. $this->_extractedData = $data;
  176. return $this;
  177. }
  178. /**
  179. * Return extracted data
  180. *
  181. * @param string $index
  182. * @return mixed
  183. */
  184. public function getExtractedData($index = null)
  185. {
  186. if ($index !== null) {
  187. if (isset($this->_extractedData[$index])) {
  188. return $this->_extractedData[$index];
  189. }
  190. return null;
  191. }
  192. return $this->_extractedData;
  193. }
  194. /**
  195. * Apply attribute input filter to value
  196. *
  197. * @param string $value
  198. * @return string
  199. */
  200. protected function _applyInputFilter($value)
  201. {
  202. if ($value === false) {
  203. return false;
  204. }
  205. $filter = $this->_getFormFilter();
  206. if ($filter) {
  207. $value = $filter->inputFilter($value);
  208. }
  209. return $value;
  210. }
  211. /**
  212. * Return Data Form Input/Output Filter
  213. *
  214. * @return \Magento\Framework\Data\Form\Filter\FilterInterface|false
  215. */
  216. protected function _getFormFilter()
  217. {
  218. $filterCode = $this->getAttribute()->getInputFilter();
  219. if ($filterCode) {
  220. $filterClass = 'Magento\Framework\Data\Form\Filter\\' . ucfirst($filterCode);
  221. if ($filterCode == 'date') {
  222. $filter = new $filterClass($this->_dateFilterFormat(), $this->_localeResolver);
  223. } else {
  224. $filter = new $filterClass();
  225. }
  226. return $filter;
  227. }
  228. return false;
  229. }
  230. /**
  231. * Get/Set/Reset date filter format
  232. *
  233. * @param string|null|false $format
  234. * @return $this|string
  235. */
  236. protected function _dateFilterFormat($format = null)
  237. {
  238. if ($format === null) {
  239. // get format
  240. if ($this->_dateFilterFormat === null) {
  241. $this->_dateFilterFormat = \IntlDateFormatter::SHORT;
  242. }
  243. return $this->_localeDate->getDateFormat($this->_dateFilterFormat);
  244. } elseif ($format === false) {
  245. // reset value
  246. $this->_dateFilterFormat = null;
  247. return $this;
  248. }
  249. $this->_dateFilterFormat = $format;
  250. return $this;
  251. }
  252. /**
  253. * Apply attribute output filter to value
  254. *
  255. * @param string $value
  256. * @return string
  257. */
  258. protected function _applyOutputFilter($value)
  259. {
  260. $filter = $this->_getFormFilter();
  261. if ($filter) {
  262. $value = $filter->outputFilter($value);
  263. }
  264. return $value;
  265. }
  266. /**
  267. * Validate value by attribute input validation rule
  268. *
  269. * @param string $value
  270. * @return array|true
  271. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  272. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  273. */
  274. protected function _validateInputRule($value)
  275. {
  276. // skip validate empty value
  277. if (empty($value)) {
  278. return true;
  279. }
  280. $validateRules = $this->getAttribute()->getValidateRules();
  281. if (!empty($validateRules['input_validation'])) {
  282. $label = $this->getAttribute()->getStoreLabel();
  283. $allowWhiteSpace = false;
  284. switch ($validateRules['input_validation']) {
  285. case 'alphanum-with-spaces':
  286. $allowWhiteSpace = true;
  287. // Continue to alphanumeric validation
  288. case 'alphanumeric':
  289. $validator = new \Zend_Validate_Alnum($allowWhiteSpace);
  290. $validator->setMessage(__('"%1" invalid type entered.', $label), \Zend_Validate_Alnum::INVALID);
  291. $validator->setMessage(
  292. __('"%1" contains non-alphabetic or non-numeric characters.', $label),
  293. \Zend_Validate_Alnum::NOT_ALNUM
  294. );
  295. $validator->setMessage(__('"%1" is an empty string.', $label), \Zend_Validate_Alnum::STRING_EMPTY);
  296. if (!$validator->isValid($value)) {
  297. return $validator->getMessages();
  298. }
  299. break;
  300. case 'numeric':
  301. $validator = new \Zend_Validate_Digits();
  302. $validator->setMessage(__('"%1" invalid type entered.', $label), \Zend_Validate_Digits::INVALID);
  303. $validator->setMessage(
  304. __('"%1" contains non-numeric characters.', $label),
  305. \Zend_Validate_Digits::NOT_DIGITS
  306. );
  307. $validator->setMessage(
  308. __('"%1" is an empty string.', $label),
  309. \Zend_Validate_Digits::STRING_EMPTY
  310. );
  311. if (!$validator->isValid($value)) {
  312. return $validator->getMessages();
  313. }
  314. break;
  315. case 'alpha':
  316. $validator = new \Zend_Validate_Alpha(true);
  317. $validator->setMessage(__('"%1" invalid type entered.', $label), \Zend_Validate_Alpha::INVALID);
  318. $validator->setMessage(
  319. __('"%1" contains non-alphabetic characters.', $label),
  320. \Zend_Validate_Alpha::NOT_ALPHA
  321. );
  322. $validator->setMessage(__('"%1" is an empty string.', $label), \Zend_Validate_Alpha::STRING_EMPTY);
  323. if (!$validator->isValid($value)) {
  324. return $validator->getMessages();
  325. }
  326. break;
  327. case 'email':
  328. /**
  329. __("'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded")
  330. __("Invalid type given. String expected")
  331. __("'%value%' appears to be a DNS hostname but contains a dash in an invalid position")
  332. __("'%value%' does not match the expected structure for a DNS hostname")
  333. __("'%value%' appears to be a DNS hostname but cannot match against hostname schema
  334. * for TLD '%tld%'")
  335. __("'%value%' does not appear to be a valid local network name")
  336. __("'%value%' does not appear to be a valid URI hostname")
  337. __("'%value%' appears to be an IP address but IP addresses are not allowed")
  338. __("'%value%' appears to be a local network name but local network names are not allowed")
  339. __("'%value%' appears to be a DNS hostname but cannot extract TLD part")
  340. __("'%value%' appears to be a DNS hostname but cannot match TLD against known list")
  341. */
  342. $validator = new EmailAddress();
  343. $validator->setMessage(
  344. __('"%1" invalid type entered.', $label),
  345. \Zend_Validate_EmailAddress::INVALID
  346. );
  347. $validator->setMessage(
  348. __('"%1" is not a valid email address.', $label),
  349. \Zend_Validate_EmailAddress::INVALID_FORMAT
  350. );
  351. $validator->setMessage(
  352. __('"%1" is not a valid hostname.', $label),
  353. \Zend_Validate_EmailAddress::INVALID_HOSTNAME
  354. );
  355. $validator->setMessage(
  356. __('"%1" is not a valid hostname.', $label),
  357. \Zend_Validate_EmailAddress::INVALID_MX_RECORD
  358. );
  359. $validator->setMessage(
  360. __('"%1" is not a valid hostname.', $label),
  361. \Zend_Validate_EmailAddress::INVALID_MX_RECORD
  362. );
  363. $validator->setMessage(
  364. __('"%1" is not a valid email address.', $label),
  365. \Zend_Validate_EmailAddress::DOT_ATOM
  366. );
  367. $validator->setMessage(
  368. __('"%1" is not a valid email address.', $label),
  369. \Zend_Validate_EmailAddress::QUOTED_STRING
  370. );
  371. $validator->setMessage(
  372. __('"%1" is not a valid email address.', $label),
  373. \Zend_Validate_EmailAddress::INVALID_LOCAL_PART
  374. );
  375. $validator->setMessage(
  376. __('"%1" uses too many characters.', $label),
  377. \Zend_Validate_EmailAddress::LENGTH_EXCEEDED
  378. );
  379. $validator->setMessage(
  380. __("'%value%' looks like an IP address, which is not an acceptable format."),
  381. \Zend_Validate_Hostname::IP_ADDRESS_NOT_ALLOWED
  382. );
  383. $validator->setMessage(
  384. __("'%value%' looks like a DNS hostname but contains a dash in an invalid position."),
  385. \Zend_Validate_Hostname::INVALID_DASH
  386. );
  387. $validator->setMessage(
  388. __(
  389. "'%value%' looks like a DNS hostname but we cannot match it against the "
  390. . "hostname schema for TLD '%tld%'."
  391. ),
  392. \Zend_Validate_Hostname::INVALID_HOSTNAME_SCHEMA
  393. );
  394. $validator->setMessage(
  395. __("'%value%' looks like a DNS hostname but cannot extract TLD part."),
  396. \Zend_Validate_Hostname::UNDECIPHERABLE_TLD
  397. );
  398. $validator->setMessage(
  399. __("'%value%' does not look like a valid local network name."),
  400. \Zend_Validate_Hostname::INVALID_LOCAL_NAME
  401. );
  402. $validator->setMessage(
  403. __("'%value%' looks like a local network name, which is not an acceptable format."),
  404. \Zend_Validate_Hostname::LOCAL_NAME_NOT_ALLOWED
  405. );
  406. $validator->setMessage(
  407. __(
  408. "'%value%' appears to be a DNS hostname, but the given punycode notation cannot be decoded."
  409. ),
  410. \Zend_Validate_Hostname::CANNOT_DECODE_PUNYCODE
  411. );
  412. if (!$validator->isValid($value)) {
  413. return array_unique($validator->getMessages());
  414. }
  415. break;
  416. case 'url':
  417. $parsedUrl = parse_url($value);
  418. if ($parsedUrl === false || empty($parsedUrl['scheme']) || empty($parsedUrl['host'])) {
  419. return [__('"%1" is not a valid URL.', $label)];
  420. }
  421. $validator = new \Zend_Validate_Hostname();
  422. if (!$validator->isValid($parsedUrl['host'])) {
  423. return [__('"%1" is not a valid URL.', $label)];
  424. }
  425. break;
  426. case 'date':
  427. $validator = new \Zend_Validate_Date(
  428. [
  429. 'format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT,
  430. 'locale' => $this->_localeResolver->getLocale(),
  431. ]
  432. );
  433. $validator->setMessage(__('"%1" invalid type entered.', $label), \Zend_Validate_Date::INVALID);
  434. $validator->setMessage(__('"%1" is not a valid date.', $label), \Zend_Validate_Date::INVALID_DATE);
  435. $validator->setMessage(
  436. __('"%1" does not fit the entered date format.', $label),
  437. \Zend_Validate_Date::FALSEFORMAT
  438. );
  439. if (!$validator->isValid($value)) {
  440. return array_unique($validator->getMessages());
  441. }
  442. break;
  443. }
  444. }
  445. return true;
  446. }
  447. /**
  448. * Set is AJAX Request flag
  449. *
  450. * @param bool $flag
  451. * @return $this
  452. * @codeCoverageIgnore
  453. */
  454. public function setIsAjaxRequest($flag = true)
  455. {
  456. $this->_isAjax = (bool)$flag;
  457. return $this;
  458. }
  459. /**
  460. * Return is AJAX Request
  461. *
  462. * @return bool
  463. * @SuppressWarnings(PHPMD.BooleanGetMethodName)
  464. * @codeCoverageIgnore
  465. */
  466. public function getIsAjaxRequest()
  467. {
  468. return $this->_isAjax;
  469. }
  470. /**
  471. * Return Original Attribute value from Request
  472. *
  473. * @param RequestInterface $request
  474. * @return mixed
  475. */
  476. protected function _getRequestValue(RequestInterface $request)
  477. {
  478. $attrCode = $this->getAttribute()->getAttributeCode();
  479. if ($this->_requestScope) {
  480. if (strpos($this->_requestScope, '/') !== false) {
  481. $params = $request->getParams();
  482. $parts = explode('/', $this->_requestScope);
  483. foreach ($parts as $part) {
  484. if (isset($params[$part])) {
  485. $params = $params[$part];
  486. } else {
  487. $params = [];
  488. }
  489. }
  490. } else {
  491. $params = $request->getParam($this->_requestScope);
  492. }
  493. if (isset($params[$attrCode])) {
  494. $value = $params[$attrCode];
  495. } else {
  496. $value = false;
  497. }
  498. if (!$this->_requestScopeOnly && $value === false) {
  499. $value = $request->getParam($attrCode, false);
  500. }
  501. } else {
  502. $value = $request->getParam($attrCode, false);
  503. }
  504. return $value;
  505. }
  506. /**
  507. * Extract data from request and return value
  508. *
  509. * @param RequestInterface $request
  510. * @return array|string|bool
  511. */
  512. abstract public function extractValue(RequestInterface $request);
  513. /**
  514. * Validate data
  515. *
  516. * @param array|string $value
  517. * @throws CoreException
  518. * @return bool
  519. */
  520. abstract public function validateValue($value);
  521. /**
  522. * Export attribute value to entity model
  523. *
  524. * @param array|string $value
  525. * @return $this
  526. */
  527. abstract public function compactValue($value);
  528. /**
  529. * Restore attribute value from SESSION to entity model
  530. *
  531. * @param array|string $value
  532. * @return $this
  533. */
  534. abstract public function restoreValue($value);
  535. /**
  536. * Return formatted attribute value from entity model
  537. *
  538. * @param string $format
  539. * @return string|array
  540. */
  541. abstract public function outputValue($format = \Magento\Eav\Model\AttributeDataFactory::OUTPUT_FORMAT_TEXT);
  542. }