AbstractAttribute.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. declare(strict_types=1);
  7. namespace Magento\Eav\Model\Entity\Attribute;
  8. use Magento\Framework\Api\AttributeValueFactory;
  9. use Magento\Framework\Exception\LocalizedException;
  10. use Magento\Framework\Serialize\Serializer\Json;
  11. /**
  12. * Entity/Attribute/Model - attribute abstract
  13. * @api
  14. * @SuppressWarnings(PHPMD.ExcessivePublicCount)
  15. * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  16. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  17. * @SuppressWarnings(PHPMD.TooManyFields)
  18. * @since 100.0.2
  19. */
  20. abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtensibleModel implements
  21. AttributeInterface,
  22. \Magento\Eav\Api\Data\AttributeInterface
  23. {
  24. const TYPE_STATIC = 'static';
  25. /**
  26. * Const for empty string value.
  27. */
  28. const EMPTY_STRING = '';
  29. /**
  30. * Attribute name
  31. *
  32. * @var string
  33. */
  34. protected $_name;
  35. /**
  36. * Entity instance
  37. *
  38. * @var \Magento\Eav\Model\Entity\AbstractEntity
  39. */
  40. protected $_entity;
  41. /**
  42. * Backend instance
  43. *
  44. * @var \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
  45. */
  46. protected $_backend;
  47. /**
  48. * Frontend instance
  49. *
  50. * @var \Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend
  51. */
  52. protected $_frontend;
  53. /**
  54. * Source instance
  55. *
  56. * @var \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource
  57. */
  58. protected $_source;
  59. /**
  60. * Attribute id cache
  61. *
  62. * @var array
  63. */
  64. protected $_attributeIdCache = [];
  65. /**
  66. * Attribute data table name
  67. *
  68. * @var string
  69. */
  70. protected $_dataTable = null;
  71. /**
  72. * @var \Magento\Eav\Model\Config
  73. */
  74. protected $_eavConfig;
  75. /**
  76. * @var \Magento\Eav\Model\Entity\TypeFactory
  77. */
  78. protected $_eavTypeFactory;
  79. /**
  80. * @var \Magento\Store\Model\StoreManagerInterface
  81. */
  82. protected $_storeManager;
  83. /**
  84. * @var \Magento\Eav\Model\ResourceModel\Helper
  85. */
  86. protected $_resourceHelper;
  87. /**
  88. * @var \Magento\Framework\Validator\UniversalFactory
  89. */
  90. protected $_universalFactory;
  91. /**
  92. * @var \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory
  93. */
  94. protected $optionDataFactory;
  95. /**
  96. * @var \Magento\Framework\Reflection\DataObjectProcessor
  97. */
  98. protected $dataObjectProcessor;
  99. /**
  100. * @var \Magento\Framework\Api\DataObjectHelper
  101. */
  102. protected $dataObjectHelper;
  103. /**
  104. * @var FrontendLabelFactory
  105. */
  106. private $frontendLabelFactory;
  107. /**
  108. * Serializer Instance.
  109. *
  110. * @var Json
  111. * @since 101.0.0
  112. */
  113. protected $serializer;
  114. /**
  115. * Array of attribute types that have empty string as a possible value.
  116. *
  117. * @var array
  118. */
  119. private $emptyStringTypes = [
  120. 'int',
  121. 'decimal',
  122. 'datetime',
  123. 'varchar',
  124. 'text',
  125. 'static',
  126. ];
  127. /**
  128. * @var \Magento\Eav\Api\Data\AttributeExtensionFactory
  129. */
  130. private $eavExtensionFactory;
  131. /**
  132. * @param \Magento\Framework\Model\Context $context
  133. * @param \Magento\Framework\Registry $registry
  134. * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
  135. * @param AttributeValueFactory $customAttributeFactory
  136. * @param \Magento\Eav\Model\Config $eavConfig
  137. * @param \Magento\Eav\Model\Entity\TypeFactory $eavTypeFactory
  138. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  139. * @param \Magento\Eav\Model\ResourceModel\Helper $resourceHelper
  140. * @param \Magento\Framework\Validator\UniversalFactory $universalFactory
  141. * @param \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionDataFactory
  142. * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor
  143. * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
  144. * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
  145. * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
  146. * @param array $data
  147. * @param \Magento\Eav\Api\Data\AttributeExtensionFactory|null $eavExtensionFactory
  148. * @param FrontendLabelFactory|null $frontendLabelFactory
  149. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  150. * @codeCoverageIgnore
  151. */
  152. public function __construct(
  153. \Magento\Framework\Model\Context $context,
  154. \Magento\Framework\Registry $registry,
  155. \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
  156. AttributeValueFactory $customAttributeFactory,
  157. \Magento\Eav\Model\Config $eavConfig,
  158. \Magento\Eav\Model\Entity\TypeFactory $eavTypeFactory,
  159. \Magento\Store\Model\StoreManagerInterface $storeManager,
  160. \Magento\Eav\Model\ResourceModel\Helper $resourceHelper,
  161. \Magento\Framework\Validator\UniversalFactory $universalFactory,
  162. \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionDataFactory,
  163. \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor,
  164. \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
  165. \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
  166. \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
  167. array $data = [],
  168. \Magento\Eav\Api\Data\AttributeExtensionFactory $eavExtensionFactory = null,
  169. FrontendLabelFactory $frontendLabelFactory = null
  170. ) {
  171. parent::__construct(
  172. $context,
  173. $registry,
  174. $extensionFactory,
  175. $customAttributeFactory,
  176. $resource,
  177. $resourceCollection,
  178. $data
  179. );
  180. $this->_eavConfig = $eavConfig;
  181. $this->_eavTypeFactory = $eavTypeFactory;
  182. $this->_storeManager = $storeManager;
  183. $this->_resourceHelper = $resourceHelper;
  184. $this->_universalFactory = $universalFactory;
  185. $this->optionDataFactory = $optionDataFactory;
  186. $this->dataObjectProcessor = $dataObjectProcessor;
  187. $this->dataObjectHelper = $dataObjectHelper;
  188. $this->eavExtensionFactory = $eavExtensionFactory ?: \Magento\Framework\App\ObjectManager::getInstance()
  189. ->get(\Magento\Eav\Api\Data\AttributeExtensionFactory::class);
  190. $this->frontendLabelFactory = $frontendLabelFactory
  191. ?: \Magento\Framework\App\ObjectManager::getInstance()->get(FrontendLabelFactory::class);
  192. }
  193. /**
  194. * Get Serializer instance.
  195. *
  196. * @deprecated 101.0.0
  197. *
  198. * @return Json
  199. * @since 101.0.0
  200. */
  201. protected function getSerializer()
  202. {
  203. if ($this->serializer === null) {
  204. $this->serializer = \Magento\Framework\App\ObjectManager::getInstance()->create(Json::class);
  205. }
  206. return $this->serializer;
  207. }
  208. /**
  209. * Initialize resource model
  210. *
  211. * @return void
  212. * @codeCoverageIgnore
  213. */
  214. protected function _construct()
  215. {
  216. $this->_init(\Magento\Eav\Model\ResourceModel\Entity\Attribute::class);
  217. }
  218. /**
  219. * Load attribute data by code
  220. *
  221. * @param string|int|\Magento\Eav\Model\Entity\Type $entityType
  222. * @param string $code
  223. * @return $this
  224. * @throws LocalizedException
  225. */
  226. public function loadByCode($entityType, $code)
  227. {
  228. \Magento\Framework\Profiler::start('load_by_code');
  229. if (is_numeric($entityType)) {
  230. $entityTypeId = $entityType;
  231. } elseif (is_string($entityType)) {
  232. $entityType = $this->_eavTypeFactory->create()->loadByCode($entityType);
  233. }
  234. if ($entityType instanceof \Magento\Eav\Model\Entity\Type) {
  235. $entityTypeId = $entityType->getId();
  236. }
  237. if (empty($entityTypeId)) {
  238. throw new LocalizedException(__('The entity supplied is invalid. Verify the entity and try again.'));
  239. }
  240. $this->_getResource()->loadByCode($this, $entityTypeId, $code);
  241. $this->_afterLoad();
  242. \Magento\Framework\Profiler::stop('load_by_code');
  243. return $this;
  244. }
  245. /**
  246. * Get attribute name
  247. *
  248. * @return string
  249. * @codeCoverageIgnore
  250. */
  251. public function getName()
  252. {
  253. return $this->_getData('attribute_code');
  254. }
  255. /**
  256. * Specify attribute identifier
  257. *
  258. * @param int $data
  259. * @return $this
  260. * @codeCoverageIgnore
  261. */
  262. public function setAttributeId($data)
  263. {
  264. $this->_data['attribute_id'] = $data;
  265. return $this;
  266. }
  267. /**
  268. * @inheritdoc
  269. * @codeCoverageIgnore
  270. */
  271. public function getAttributeId()
  272. {
  273. return $this->_getData('attribute_id');
  274. }
  275. /**
  276. * Set attribute code
  277. *
  278. * @param string $data
  279. * @return $this
  280. * @codeCoverageIgnore
  281. */
  282. public function setAttributeCode($data)
  283. {
  284. return $this->setData('attribute_code', $data);
  285. }
  286. /**
  287. * @inheritdoc
  288. * @codeCoverageIgnore
  289. */
  290. public function getAttributeCode()
  291. {
  292. return $this->_getData('attribute_code');
  293. }
  294. /**
  295. * Set attribute model
  296. *
  297. * @param array $data
  298. * @return $this
  299. * @codeCoverageIgnore
  300. */
  301. public function setAttributeModel($data)
  302. {
  303. return $this->setData('attribute_model', $data);
  304. }
  305. /**
  306. * Returns attribute model
  307. *
  308. * @return array
  309. * @codeCoverageIgnore
  310. */
  311. public function getAttributeModel()
  312. {
  313. return $this->_getData('attribute_model');
  314. }
  315. /**
  316. * Set backend type
  317. *
  318. * @param string $data
  319. * @return $this
  320. * @codeCoverageIgnore
  321. */
  322. public function setBackendType($data)
  323. {
  324. return $this->setData('backend_type', $data);
  325. }
  326. /**
  327. * @inheritdoc
  328. * @codeCoverageIgnore
  329. */
  330. public function getBackendType()
  331. {
  332. return $this->_getData('backend_type');
  333. }
  334. /**
  335. * Set backend model
  336. *
  337. * @param string $data
  338. * @return $this
  339. * @codeCoverageIgnore
  340. */
  341. public function setBackendModel($data)
  342. {
  343. return $this->setData('backend_model', $data);
  344. }
  345. /**
  346. * @inheritdoc
  347. * @codeCoverageIgnore
  348. */
  349. public function getBackendModel()
  350. {
  351. return $this->_getData('backend_model');
  352. }
  353. /**
  354. * Set backend table
  355. *
  356. * @param string $data
  357. * @return $this
  358. * @codeCoverageIgnore
  359. */
  360. public function setBackendTable($data)
  361. {
  362. return $this->setData('backend_table', $data);
  363. }
  364. /**
  365. * Returns is visible on front
  366. *
  367. * @return bool
  368. * @SuppressWarnings(PHPMD.BooleanGetMethodName)
  369. * @codeCoverageIgnore
  370. */
  371. public function getIsVisibleOnFront()
  372. {
  373. return $this->_getData('is_visible_on_front');
  374. }
  375. /**
  376. * Returns default value
  377. *
  378. * @return string|null
  379. * @codeCoverageIgnore
  380. */
  381. public function getDefaultValue()
  382. {
  383. return $this->_getData('default_value');
  384. }
  385. /**
  386. * Set default value for the element.
  387. *
  388. * @param string $defaultValue
  389. * @return $this
  390. * @codeCoverageIgnore
  391. */
  392. public function setDefaultValue($defaultValue)
  393. {
  394. return $this->setData('default_value', $defaultValue);
  395. }
  396. /**
  397. * Returns attribute set id
  398. *
  399. * @return int
  400. * @codeCoverageIgnore
  401. */
  402. public function getAttributeSetId()
  403. {
  404. return $this->_getData('attribute_set_id');
  405. }
  406. /**
  407. * Set attribute set id
  408. *
  409. * @param int $id
  410. * @return $this
  411. * @codeCoverageIgnore
  412. */
  413. public function setAttributeSetId($id)
  414. {
  415. $this->_data['attribute_set_id'] = $id;
  416. return $this;
  417. }
  418. /**
  419. * @inheritdoc
  420. * @codeCoverageIgnore
  421. */
  422. public function getEntityTypeId()
  423. {
  424. return $this->_getData('entity_type_id');
  425. }
  426. /**
  427. * Set entity type id
  428. *
  429. * @param int|string $id
  430. * @return $this
  431. * @codeCoverageIgnore
  432. */
  433. public function setEntityTypeId($id)
  434. {
  435. $this->_data['entity_type_id'] = $id;
  436. return $this;
  437. }
  438. /**
  439. * Set entity type
  440. *
  441. * @param string $type
  442. * @return $this
  443. * @codeCoverageIgnore
  444. */
  445. public function setEntityType($type)
  446. {
  447. $this->setData('entity_type', $type);
  448. return $this;
  449. }
  450. /**
  451. * Get attribute alias as "entity_type/attribute_code"
  452. *
  453. * @param \Magento\Eav\Model\Entity\AbstractEntity $entity exclude this entity
  454. * @return string
  455. */
  456. public function getAlias($entity = null)
  457. {
  458. $alias = '';
  459. if ($entity === null || $entity->getType() !== $this->getEntity()->getType()) {
  460. $alias .= $this->getEntity()->getType() . '/';
  461. }
  462. $alias .= $this->getAttributeCode();
  463. return $alias;
  464. }
  465. /**
  466. * Set attribute name
  467. *
  468. * @param string $name
  469. * @return $this
  470. * @codeCoverageIgnore
  471. */
  472. public function setName($name)
  473. {
  474. return $this->setData('attribute_code', $name);
  475. }
  476. /**
  477. * Retrieve entity type
  478. *
  479. * @return \Magento\Eav\Model\Entity\Type
  480. * @codeCoverageIgnore
  481. */
  482. public function getEntityType()
  483. {
  484. return $this->_eavConfig->getEntityType($this->getEntityTypeId());
  485. }
  486. /**
  487. * Set attribute entity instance
  488. *
  489. * @param \Magento\Eav\Model\Entity\AbstractEntity $entity
  490. * @return $this
  491. * @codeCoverageIgnore
  492. */
  493. public function setEntity($entity)
  494. {
  495. $this->_entity = $entity;
  496. return $this;
  497. }
  498. /**
  499. * Retrieve entity instance
  500. *
  501. * @return \Magento\Eav\Model\Entity\AbstractEntity
  502. */
  503. public function getEntity()
  504. {
  505. if (!$this->_entity) {
  506. $this->_entity = $this->getEntityType()->getEntity();
  507. }
  508. return $this->_entity;
  509. }
  510. /**
  511. * Retrieve entity type
  512. *
  513. * @return string
  514. * @codeCoverageIgnore
  515. */
  516. public function getEntityIdField()
  517. {
  518. return $this->getEntity()->getValueEntityIdField();
  519. }
  520. /**
  521. * Retrieve backend instance
  522. *
  523. * @return \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
  524. * @throws LocalizedException
  525. */
  526. public function getBackend()
  527. {
  528. if (empty($this->_backend)) {
  529. if (!$this->getBackendModel()) {
  530. $this->setBackendModel($this->_getDefaultBackendModel());
  531. }
  532. $backend = $this->_universalFactory->create($this->getBackendModel());
  533. if (!$backend) {
  534. throw new LocalizedException(
  535. __(
  536. 'The "%1" backend model is invalid. Verify the backend model and try again.',
  537. $this->getBackendModel()
  538. )
  539. );
  540. }
  541. $this->_backend = $backend->setAttribute($this);
  542. }
  543. return $this->_backend;
  544. }
  545. /**
  546. * Retrieve frontend instance
  547. *
  548. * @return \Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend
  549. */
  550. public function getFrontend()
  551. {
  552. if (empty($this->_frontend)) {
  553. if (!$this->getFrontendModel()) {
  554. $this->setFrontendModel($this->_getDefaultFrontendModel());
  555. }
  556. $this->_frontend = $this->_universalFactory->create($this->getFrontendModel())->setAttribute($this);
  557. }
  558. return $this->_frontend;
  559. }
  560. /**
  561. * Retrieve source instance
  562. *
  563. * @return \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource
  564. * @throws LocalizedException
  565. */
  566. public function getSource()
  567. {
  568. if (empty($this->_source)) {
  569. if (!$this->getSourceModel()) {
  570. $this->_source = $this->_getDefaultSourceModel();
  571. } else {
  572. $this->_source = $this->getSourceModel();
  573. }
  574. $source = $this->_universalFactory->create($this->_source);
  575. if (!$source) {
  576. throw new LocalizedException(
  577. __(
  578. 'Source model "%1" not found for attribute "%2"',
  579. $this->getSourceModel(),
  580. $this->getAttributeCode()
  581. )
  582. );
  583. }
  584. $this->_source = $source->setAttribute($this);
  585. }
  586. return $this->_source;
  587. }
  588. /**
  589. * Whether possible attribute values are retrieved from finite source
  590. *
  591. * @return bool
  592. */
  593. public function usesSource()
  594. {
  595. $input = $this->getFrontendInput();
  596. return $input === 'select' || $input === 'multiselect' || $this->_getData('source_model') != '';
  597. }
  598. /**
  599. * Returns default backend model
  600. *
  601. * @return string
  602. * @codeCoverageIgnore
  603. */
  604. protected function _getDefaultBackendModel()
  605. {
  606. return \Magento\Eav\Model\Entity::DEFAULT_BACKEND_MODEL;
  607. }
  608. /**
  609. * Returns default frontend model
  610. *
  611. * @return string
  612. * @codeCoverageIgnore
  613. */
  614. protected function _getDefaultFrontendModel()
  615. {
  616. return \Magento\Eav\Model\Entity::DEFAULT_FRONTEND_MODEL;
  617. }
  618. /**
  619. * Returns default source model
  620. *
  621. * @return string
  622. * @codeCoverageIgnore
  623. */
  624. protected function _getDefaultSourceModel()
  625. {
  626. return $this->getEntityType()->getEntity()->getDefaultAttributeSourceModel();
  627. }
  628. /**
  629. * Check if Value is empty.
  630. *
  631. * @param array|null|bool|int|float|string $value
  632. * @return bool
  633. */
  634. public function isValueEmpty($value)
  635. {
  636. return (is_array($value) && count($value) == 0)
  637. || $value === null
  638. || ($value === false && $this->getBackend()->getType() != 'int')
  639. || ($value === self::EMPTY_STRING && $this->isInEmptyStringTypes());
  640. }
  641. /**
  642. * Check if attribute empty value is valid.
  643. *
  644. * @param array|null|bool|int|float|string $value
  645. * @return bool
  646. * @since 100.1.6
  647. */
  648. public function isAllowedEmptyTextValue($value)
  649. {
  650. return $this->isInEmptyStringTypes() && $value === self::EMPTY_STRING;
  651. }
  652. /**
  653. * Check is attribute type in allowed empty string types.
  654. *
  655. * @return bool
  656. */
  657. private function isInEmptyStringTypes()
  658. {
  659. return in_array($this->getBackend()->getType(), $this->emptyStringTypes);
  660. }
  661. /**
  662. * Check if attribute in specified set
  663. *
  664. * @param int|int[] $setId
  665. * @return bool
  666. */
  667. public function isInSet($setId)
  668. {
  669. if (!$this->hasAttributeSetInfo()) {
  670. return true;
  671. }
  672. if (is_array($setId) && count(array_intersect($setId, array_keys($this->getAttributeSetInfo())))) {
  673. return true;
  674. }
  675. if (!is_array($setId) && array_key_exists($setId, $this->getAttributeSetInfo())) {
  676. return true;
  677. }
  678. return false;
  679. }
  680. /**
  681. * Check if attribute in specified group
  682. *
  683. * @param int $setId
  684. * @param int $groupId
  685. * @return bool
  686. */
  687. public function isInGroup($setId, $groupId)
  688. {
  689. $dataPath = sprintf('attribute_set_info/%s/group_id', $setId);
  690. if ($this->isInSet($setId) && $this->getData($dataPath) == $groupId) {
  691. return true;
  692. }
  693. return false;
  694. }
  695. /**
  696. * Return attribute id
  697. *
  698. * @param string $entityType
  699. * @param string $code
  700. * @return int
  701. */
  702. public function getIdByCode($entityType, $code)
  703. {
  704. $cacheKey = "{$entityType}|{$code}";
  705. if (!isset($this->_attributeIdCache[$cacheKey])) {
  706. $this->_attributeIdCache[$cacheKey] = $this->getResource()->getIdByCode($entityType, $code);
  707. }
  708. return $this->_attributeIdCache[$cacheKey];
  709. }
  710. /**
  711. * Check if attribute is static
  712. *
  713. * @return bool
  714. */
  715. public function isStatic()
  716. {
  717. return $this->getBackendType() == self::TYPE_STATIC || $this->getBackendType() == '';
  718. }
  719. /**
  720. * Get attribute backend table name
  721. *
  722. * @return string
  723. */
  724. public function getBackendTable()
  725. {
  726. if ($this->_dataTable === null) {
  727. if ($this->isStatic()) {
  728. $this->_dataTable = $this->getEntityType()->getValueTablePrefix();
  729. } else {
  730. $backendTable = trim((string)$this->_getData('backend_table'));
  731. if (empty($backendTable)) {
  732. $entityTable = [$this->getEntityType()->getEntityTablePrefix(), $this->getBackendType()];
  733. $backendTable = $this->getResource()->getTable($entityTable);
  734. }
  735. $this->_dataTable = $backendTable;
  736. }
  737. }
  738. return $this->_dataTable;
  739. }
  740. /**
  741. * Retrieve flat columns definition
  742. *
  743. * @return array
  744. */
  745. public function getFlatColumns()
  746. {
  747. // If source model exists - get definition from it
  748. if ($this->usesSource() && $this->getBackendType() != self::TYPE_STATIC) {
  749. return $this->getSource()->getFlatColumns();
  750. }
  751. return $this->_getFlatColumnsDdlDefinition();
  752. }
  753. /**
  754. * Retrieve flat columns DDL definition
  755. *
  756. * @return array
  757. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  758. */
  759. public function _getFlatColumnsDdlDefinition()
  760. {
  761. $columns = [];
  762. switch ($this->getBackendType()) {
  763. case 'static':
  764. $describe = $this->_getResource()->describeTable($this->getBackend()->getTable());
  765. if (!isset($describe[$this->getAttributeCode()])) {
  766. break;
  767. }
  768. $prop = $describe[$this->getAttributeCode()];
  769. $type = $prop['DATA_TYPE'];
  770. $size = $prop['LENGTH'] ? $prop['LENGTH'] : null;
  771. $columns[$this->getAttributeCode()] = [
  772. 'type' => $this->_resourceHelper->getDdlTypeByColumnType($type),
  773. 'length' => $size,
  774. 'unsigned' => $prop['UNSIGNED'] ? true : false,
  775. 'nullable' => $prop['NULLABLE'],
  776. 'default' => $prop['DEFAULT'],
  777. 'extra' => null,
  778. ];
  779. break;
  780. case 'datetime':
  781. $columns[$this->getAttributeCode()] = [
  782. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DATETIME,
  783. 'unsigned' => false,
  784. 'nullable' => true,
  785. 'default' => null,
  786. 'extra' => null,
  787. ];
  788. break;
  789. case 'decimal':
  790. $columns[$this->getAttributeCode()] = [
  791. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL,
  792. 'length' => '12,4',
  793. 'unsigned' => false,
  794. 'nullable' => true,
  795. 'default' => null,
  796. 'extra' => null,
  797. ];
  798. break;
  799. case 'int':
  800. $columns[$this->getAttributeCode()] = [
  801. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
  802. 'unsigned' => false,
  803. 'nullable' => true,
  804. 'default' => null,
  805. 'extra' => null,
  806. ];
  807. break;
  808. case 'text':
  809. $columns[$this->getAttributeCode()] = [
  810. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
  811. 'unsigned' => false,
  812. 'nullable' => true,
  813. 'default' => null,
  814. 'extra' => null,
  815. 'length' => \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE,
  816. ];
  817. break;
  818. case 'varchar':
  819. $columns[$this->getAttributeCode()] = [
  820. 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
  821. 'length' => '255',
  822. 'unsigned' => false,
  823. 'nullable' => true,
  824. 'default' => null,
  825. 'extra' => null,
  826. ];
  827. break;
  828. default:
  829. break;
  830. }
  831. return $columns;
  832. }
  833. /**
  834. * Retrieve flat columns definition in old format (before MMDB support)
  835. *
  836. * Used in database compatible mode
  837. *
  838. * @deprecated 101.0.0
  839. * @return array
  840. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  841. */
  842. protected function _getFlatColumnsOldDefinition()
  843. {
  844. $columns = [];
  845. switch ($this->getBackendType()) {
  846. case 'static':
  847. $describe = $this->_getResource()->describeTable($this->getBackend()->getTable());
  848. if (!isset($describe[$this->getAttributeCode()])) {
  849. break;
  850. }
  851. $prop = $describe[$this->getAttributeCode()];
  852. $columns[$this->getAttributeCode()] = [
  853. 'type' => $prop['DATA_TYPE'] . ($prop['LENGTH'] ? "({$prop['LENGTH']})" : ""),
  854. 'unsigned' => $prop['UNSIGNED'] ? true : false,
  855. 'is_null' => $prop['NULLABLE'],
  856. 'default' => $prop['DEFAULT'],
  857. 'extra' => null,
  858. ];
  859. break;
  860. case 'datetime':
  861. $columns[$this->getAttributeCode()] = [
  862. 'type' => 'datetime',
  863. 'unsigned' => false,
  864. 'is_null' => true,
  865. 'default' => null,
  866. 'extra' => null,
  867. ];
  868. break;
  869. case 'decimal':
  870. $columns[$this->getAttributeCode()] = [
  871. 'type' => 'decimal(12,4)',
  872. 'unsigned' => false,
  873. 'is_null' => true,
  874. 'default' => null,
  875. 'extra' => null,
  876. ];
  877. break;
  878. case 'int':
  879. $columns[$this->getAttributeCode()] = [
  880. 'type' => 'int',
  881. 'unsigned' => false,
  882. 'is_null' => true,
  883. 'default' => null,
  884. 'extra' => null,
  885. ];
  886. break;
  887. case 'text':
  888. $columns[$this->getAttributeCode()] = [
  889. 'type' => 'text',
  890. 'unsigned' => false,
  891. 'is_null' => true,
  892. 'default' => null,
  893. 'extra' => null,
  894. ];
  895. break;
  896. case 'varchar':
  897. $columns[$this->getAttributeCode()] = [
  898. 'type' => 'varchar(255)',
  899. 'unsigned' => false,
  900. 'is_null' => true,
  901. 'default' => null,
  902. 'extra' => null,
  903. ];
  904. break;
  905. default:
  906. break;
  907. }
  908. return $columns;
  909. }
  910. /**
  911. * Retrieve index data for flat table
  912. *
  913. * @return array
  914. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  915. */
  916. public function getFlatIndexes()
  917. {
  918. $condition = $this->getUsedForSortBy();
  919. if ($this->getFlatAddFilterableAttributes()) {
  920. $condition = $condition || $this->getIsFilterable();
  921. }
  922. if ($condition) {
  923. if ($this->usesSource() && $this->getBackendType() != self::TYPE_STATIC) {
  924. return $this->getSource()->getFlatIndexes();
  925. }
  926. $indexes = [];
  927. switch ($this->getBackendType()) {
  928. case 'static':
  929. $describe = $this->_getResource()->describeTable($this->getBackend()->getTable());
  930. if (!isset($describe[$this->getAttributeCode()])) {
  931. break;
  932. }
  933. $indexDataTypes = [
  934. 'varchar',
  935. 'varbinary',
  936. 'char',
  937. 'date',
  938. 'datetime',
  939. 'timestamp',
  940. 'time',
  941. 'year',
  942. 'enum',
  943. 'set',
  944. 'bit',
  945. 'bool',
  946. 'tinyint',
  947. 'smallint',
  948. 'mediumint',
  949. 'int',
  950. 'bigint',
  951. 'float',
  952. 'double',
  953. 'decimal',
  954. ];
  955. $prop = $describe[$this->getAttributeCode()];
  956. if (in_array($prop['DATA_TYPE'], $indexDataTypes)) {
  957. $indexName = 'IDX_' . strtoupper($this->getAttributeCode());
  958. $indexes[$indexName] = ['type' => 'index', 'fields' => [$this->getAttributeCode()]];
  959. }
  960. break;
  961. case 'datetime':
  962. case 'decimal':
  963. case 'int':
  964. case 'varchar':
  965. $indexName = 'IDX_' . strtoupper($this->getAttributeCode());
  966. $indexes[$indexName] = ['type' => 'index', 'fields' => [$this->getAttributeCode()]];
  967. break;
  968. default:
  969. break;
  970. }
  971. return $indexes;
  972. }
  973. return [];
  974. }
  975. /**
  976. * Retrieve Select For Flat Attribute update
  977. *
  978. * @param int $store
  979. * @return \Magento\Framework\DB\Select
  980. */
  981. public function getFlatUpdateSelect($store = null)
  982. {
  983. if ($store === null) {
  984. foreach ($this->_storeManager->getStores() as $store) {
  985. $this->getFlatUpdateSelect($store->getId());
  986. }
  987. return $this;
  988. }
  989. if ($this->getBackendType() == self::TYPE_STATIC) {
  990. return null;
  991. }
  992. if ($this->usesSource()) {
  993. return $this->getSource()->getFlatUpdateSelect($store);
  994. }
  995. return $this->_getResource()->getFlatUpdateSelect($this, $store);
  996. }
  997. /**
  998. * @inheritdoc
  999. * @codeCoverageIgnoreStart
  1000. */
  1001. public function getIsUnique()
  1002. {
  1003. return $this->_getData(self::IS_UNIQUE);
  1004. }
  1005. /**
  1006. * Set whether this is a unique attribute
  1007. *
  1008. * @param string $isUnique
  1009. * @return $this
  1010. */
  1011. public function setIsUnique($isUnique)
  1012. {
  1013. return $this->setData(self::IS_UNIQUE, $isUnique);
  1014. }
  1015. /**
  1016. * @inheritdoc
  1017. */
  1018. public function getFrontendClass()
  1019. {
  1020. return $this->_getData(self::FRONTEND_CLASS);
  1021. }
  1022. /**
  1023. * Set frontend class of attribute
  1024. *
  1025. * @param string $frontendClass
  1026. * @return $this
  1027. */
  1028. public function setFrontendClass($frontendClass)
  1029. {
  1030. return $this->setData(self::FRONTEND_CLASS, $frontendClass);
  1031. }
  1032. /**
  1033. * @inheritdoc
  1034. */
  1035. public function getFrontendInput()
  1036. {
  1037. return $this->_getData(self::FRONTEND_INPUT);
  1038. }
  1039. /**
  1040. * @inheritdoc
  1041. */
  1042. public function setFrontendInput($frontendInput)
  1043. {
  1044. return $this->setData(self::FRONTEND_INPUT, $frontendInput);
  1045. }
  1046. /**
  1047. * @inheritdoc
  1048. */
  1049. public function getIsRequired()
  1050. {
  1051. return $this->_getData(self::IS_REQUIRED);
  1052. }
  1053. /**
  1054. * @inheritdoc
  1055. */
  1056. public function setIsRequired($isRequired)
  1057. {
  1058. return $this->setData(self::IS_REQUIRED, $isRequired);
  1059. }
  1060. //@codeCoverageIgnoreEnd
  1061. /**
  1062. * @inheritdoc
  1063. */
  1064. public function getOptions()
  1065. {
  1066. $options = $this->_getData(self::OPTIONS);
  1067. if (!$options) {
  1068. $options = $this->usesSource() ? $this->getSource()->getAllOptions() : [];
  1069. }
  1070. return $this->convertToObjects($options);
  1071. }
  1072. /**
  1073. * Set options of the attribute (key => value pairs for select)
  1074. *
  1075. * @param \Magento\Eav\Api\Data\AttributeOptionInterface[] $options
  1076. * @return $this
  1077. */
  1078. public function setOptions(array $options = null)
  1079. {
  1080. if ($options !== null) {
  1081. $optionDataArray = [];
  1082. foreach ($options as $option) {
  1083. $optionData = $this->dataObjectProcessor->buildOutputDataArray(
  1084. $option,
  1085. \Magento\Eav\Api\Data\AttributeOptionInterface::class
  1086. );
  1087. $optionDataArray[] = $optionData;
  1088. }
  1089. $this->setData(self::OPTIONS, $optionDataArray);
  1090. } else {
  1091. $this->setData(self::OPTIONS, $options);
  1092. }
  1093. return $this;
  1094. }
  1095. /**
  1096. * Convert option values from arrays to data objects
  1097. *
  1098. * @param array $options
  1099. * @return \Magento\Eav\Api\Data\AttributeOptionInterface[]
  1100. */
  1101. protected function convertToObjects(array $options)
  1102. {
  1103. $dataObjects = [];
  1104. foreach ($options as $option) {
  1105. /** @var \Magento\Eav\Api\Data\AttributeOptionInterface $optionDataObject */
  1106. $optionDataObject = $this->optionDataFactory->create();
  1107. $this->dataObjectHelper->populateWithArray(
  1108. $optionDataObject,
  1109. $option,
  1110. \Magento\Eav\Api\Data\AttributeOptionInterface::class
  1111. );
  1112. $dataObjects[] = $optionDataObject;
  1113. }
  1114. return $dataObjects;
  1115. }
  1116. /**
  1117. * @inheritdoc
  1118. * @codeCoverageIgnoreStart
  1119. */
  1120. public function getIsUserDefined()
  1121. {
  1122. return $this->getData(self::IS_USER_DEFINED);
  1123. }
  1124. /**
  1125. * Set whether current attribute has been defined by a user.
  1126. *
  1127. * @param bool $isUserDefined
  1128. * @return $this
  1129. */
  1130. public function setIsUserDefined($isUserDefined)
  1131. {
  1132. return $this->setData(self::IS_USER_DEFINED, $isUserDefined);
  1133. }
  1134. /**
  1135. * @inheritdoc
  1136. */
  1137. public function getDefaultFrontendLabel()
  1138. {
  1139. return $this->_getData(self::FRONTEND_LABEL);
  1140. }
  1141. /**
  1142. * Set frontend label for default store
  1143. *
  1144. * @param string $defaultFrontendLabel
  1145. * @return $this
  1146. */
  1147. public function setDefaultFrontendLabel($defaultFrontendLabel)
  1148. {
  1149. return $this->setData(self::FRONTEND_LABEL, $defaultFrontendLabel);
  1150. }
  1151. /**
  1152. * @inheritdoc
  1153. */
  1154. public function getFrontendLabels()
  1155. {
  1156. if ($this->getData(self::FRONTEND_LABELS) == null) {
  1157. $attributeId = $this->getAttributeId();
  1158. $storeLabels = $this->_getResource()->getStoreLabelsByAttributeId($attributeId);
  1159. $resultFrontedLabels = [];
  1160. foreach ($storeLabels as $i => $label) {
  1161. $frontendLabel = $this->frontendLabelFactory->create();
  1162. $frontendLabel->setStoreId($i);
  1163. $frontendLabel->setLabel($label);
  1164. $resultFrontedLabels[] = $frontendLabel;
  1165. }
  1166. $this->setData(self::FRONTEND_LABELS, $resultFrontedLabels);
  1167. }
  1168. return $this->_getData(self::FRONTEND_LABELS);
  1169. }
  1170. /**
  1171. * Set frontend label for each store
  1172. *
  1173. * @param \Magento\Eav\Api\Data\AttributeFrontendLabelInterface[] $frontendLabels
  1174. * @return $this
  1175. */
  1176. public function setFrontendLabels(array $frontendLabels = null)
  1177. {
  1178. return $this->setData(self::FRONTEND_LABELS, $frontendLabels);
  1179. }
  1180. /**
  1181. * @inheritdoc
  1182. */
  1183. public function getNote()
  1184. {
  1185. return $this->_getData(self::NOTE);
  1186. }
  1187. /**
  1188. * Set the note attribute for the element.
  1189. *
  1190. * @param string $note
  1191. * @return $this
  1192. */
  1193. public function setNote($note)
  1194. {
  1195. return $this->setData(self::NOTE, $note);
  1196. }
  1197. /**
  1198. * @inheritdoc
  1199. */
  1200. public function getSourceModel()
  1201. {
  1202. return $this->_getData(self::SOURCE_MODEL);
  1203. }
  1204. /**
  1205. * Set source model
  1206. *
  1207. * @param string $sourceModel
  1208. * @return $this
  1209. */
  1210. public function setSourceModel($sourceModel)
  1211. {
  1212. return $this->setData(self::SOURCE_MODEL, $sourceModel);
  1213. }
  1214. //@codeCoverageIgnoreEnd
  1215. /**
  1216. * @inheritdoc
  1217. */
  1218. public function getValidationRules()
  1219. {
  1220. $rules = $this->_getData(self::VALIDATE_RULES);
  1221. if (is_array($rules)) {
  1222. return $rules;
  1223. } elseif (!empty($rules)) {
  1224. return $this->getSerializer()->unserialize($rules);
  1225. }
  1226. return [];
  1227. }
  1228. /**
  1229. * Set validation rules.
  1230. *
  1231. * @param \Magento\Eav\Api\Data\AttributeValidationRuleInterface[] $validationRules
  1232. * @return $this
  1233. * @codeCoverageIgnore
  1234. */
  1235. public function setValidationRules(array $validationRules = null)
  1236. {
  1237. return $this->setData(self::VALIDATE_RULES, $validationRules);
  1238. }
  1239. /**
  1240. * @inheritdoc
  1241. *
  1242. * @return \Magento\Eav\Api\Data\AttributeExtensionInterface|null
  1243. * @codeCoverageIgnore
  1244. */
  1245. public function getExtensionAttributes()
  1246. {
  1247. $extensionAttributes = $this->_getExtensionAttributes();
  1248. if (!($extensionAttributes instanceof \Magento\Eav\Api\Data\AttributeExtensionInterface)) {
  1249. /** @var \Magento\Eav\Api\Data\AttributeExtensionInterface $extensionAttributes */
  1250. $extensionAttributes = $this->eavExtensionFactory->create();
  1251. $this->setExtensionAttributes($extensionAttributes);
  1252. }
  1253. return $extensionAttributes;
  1254. }
  1255. /**
  1256. * @inheritdoc
  1257. *
  1258. * @param \Magento\Eav\Api\Data\AttributeExtensionInterface $extensionAttributes
  1259. * @return $this
  1260. * @codeCoverageIgnore
  1261. */
  1262. public function setExtensionAttributes(\Magento\Eav\Api\Data\AttributeExtensionInterface $extensionAttributes)
  1263. {
  1264. return $this->_setExtensionAttributes($extensionAttributes);
  1265. }
  1266. /**
  1267. * @inheritdoc
  1268. * @since 100.0.7
  1269. */
  1270. public function __sleep()
  1271. {
  1272. return array_diff(
  1273. parent::__sleep(),
  1274. [
  1275. '_eavConfig',
  1276. '_eavTypeFactory',
  1277. '_storeManager',
  1278. '_resourceHelper',
  1279. '_universalFactory',
  1280. 'optionDataFactory',
  1281. 'dataObjectProcessor',
  1282. 'dataObjectHelper',
  1283. '_entity',
  1284. '_backend',
  1285. '_source',
  1286. '_frontend',
  1287. ]
  1288. );
  1289. }
  1290. /**
  1291. * @inheritdoc
  1292. * @since 100.0.7
  1293. */
  1294. public function __wakeup()
  1295. {
  1296. parent::__wakeup();
  1297. $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
  1298. $this->_eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class);
  1299. $this->_eavTypeFactory = $objectManager->get(\Magento\Eav\Model\Entity\TypeFactory::class);
  1300. $this->_storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class);
  1301. $this->_resourceHelper = $objectManager->get(\Magento\Eav\Model\ResourceModel\Helper::class);
  1302. $this->_universalFactory = $objectManager->get(\Magento\Framework\Validator\UniversalFactory ::class);
  1303. $this->optionDataFactory = $objectManager->get(\Magento\Eav\Api\Data\AttributeOptionInterfaceFactory::class);
  1304. $this->dataObjectProcessor = $objectManager->get(\Magento\Framework\Reflection\DataObjectProcessor::class);
  1305. $this->dataObjectHelper = $objectManager->get(\Magento\Framework\Api\DataObjectHelper::class);
  1306. }
  1307. }