Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Attribute.php
Go to the documentation of this file.
1 <?php
8 
15 
24 {
30  protected static $_entityAttributes = [];
31 
35  protected $_storeManager;
36 
40  protected $_eavEntityType;
41 
45  private $config;
46 
56  public function __construct(
57  \Magento\Framework\Model\ResourceModel\Db\Context $context,
59  Type $eavEntityType,
60  $connectionName = null
61  ) {
62  $this->_storeManager = $storeManager;
63  $this->_eavEntityType = $eavEntityType;
64  parent::__construct($context, $connectionName);
65  }
66 
73  protected function _construct()
74  {
75  $this->_init('eav_attribute', 'attribute_id');
76  }
77 
84  protected function _initUniqueFields()
85  {
86  $this->_uniqueFields = [
87  ['field' => ['attribute_code', 'entity_type_id'], 'title' => __('Attribute with the same code')],
88  ];
89  return $this;
90  }
91 
100  public function loadByCode(AbstractModel $object, $entityTypeId, $code)
101  {
102  $bind = [':entity_type_id' => $entityTypeId];
103  $select = $this->_getLoadSelect('attribute_code', $code, $object)->where('entity_type_id = :entity_type_id');
104  $data = $this->getConnection()->fetchRow($select, $bind);
105 
106  if ($data) {
107  $object->setData($data);
108  $this->_afterLoad($object);
109  return true;
110  }
111  return false;
112  }
113 
120  private function _getMaxSortOrder(AbstractModel $object)
121  {
122  if (intval($object->getAttributeGroupId()) > 0) {
123  $connection = $this->getConnection();
124  $bind = [
125  ':attribute_set_id' => $object->getAttributeSetId(),
126  ':attribute_group_id' => $object->getAttributeGroupId(),
127  ];
128  $select = $connection->select()->from(
129  $this->getTable('eav_entity_attribute'),
130  new \Zend_Db_Expr("MAX(sort_order)")
131  )->where(
132  'attribute_set_id = :attribute_set_id'
133  )->where(
134  'attribute_group_id = :attribute_group_id'
135  );
136 
137  return $connection->fetchOne($select, $bind);
138  }
139 
140  return 0;
141  }
142 
149  public function deleteEntity(\Magento\Framework\Model\AbstractModel $object)
150  {
151  if (!$object->getEntityAttributeId()) {
152  return $this;
153  }
154 
155  $this->getConnection()->delete(
156  $this->getTable('eav_entity_attribute'),
157  ['entity_attribute_id = ?' => $object->getEntityAttributeId()]
158  );
159 
160  return $this;
161  }
162 
170  protected function _beforeSave(AbstractModel $object)
171  {
172  $frontendLabel = $object->getFrontendLabel();
173  if (is_array($frontendLabel)) {
174  $this->checkDefaultFrontendLabelExists($frontendLabel, $frontendLabel);
175  $object->setFrontendLabel($frontendLabel[0])->setStoreLabels($frontendLabel);
176  } else {
177  $this->setStoreLabels($object, $frontendLabel);
178  }
179 
183  if (!$object->getId()) {
184  if ($object->getFrontendInput() == 'select') {
185  $object->setSourceModel(\Magento\Eav\Model\Entity\Attribute\Source\Table::class);
186  }
187  }
188 
189  return parent::_beforeSave($object);
190  }
191 
198  protected function _afterSave(AbstractModel $object)
199  {
200  $this->_saveStoreLabels(
201  $object
202  )->_saveAdditionalAttributeData(
203  $object
204  )->saveInSetIncluding(
205  $object
206  )->_saveOption(
207  $object
208  );
209  $this->getConfig()->clear();
210  return parent::_afterSave($object);
211  }
212 
221  protected function _afterDelete(\Magento\Framework\Model\AbstractModel $object)
222  {
223  $this->getConfig()->clear();
224  return $this;
225  }
226 
231  private function getConfig()
232  {
233  if (!$this->config) {
234  $this->config = ObjectManager::getInstance()->get(Config::class);
235  }
236  return $this->config;
237  }
238 
245  protected function _saveStoreLabels(AbstractModel $object)
246  {
247  $storeLabels = $object->getStoreLabels();
248  if (is_array($storeLabels)) {
249  $connection = $this->getConnection();
250  if ($object->getId()) {
251  $condition = ['attribute_id =?' => $object->getId()];
252  $connection->delete($this->getTable('eav_attribute_label'), $condition);
253  }
254  foreach ($storeLabels as $storeId => $label) {
255  if ($storeId == 0 || !strlen($label)) {
256  continue;
257  }
258  $bind = ['attribute_id' => $object->getId(), 'store_id' => $storeId, 'value' => $label];
259  $connection->insert($this->getTable('eav_attribute_label'), $bind);
260  }
261  }
262 
263  return $this;
264  }
265 
272  protected function _saveAdditionalAttributeData(AbstractModel $object)
273  {
274  $additionalTable = $this->getAdditionalAttributeTable($object->getEntityTypeId());
275  if ($additionalTable) {
276  $connection = $this->getConnection();
277  $data = $this->_prepareDataForTable($object, $this->getTable($additionalTable));
278  $bind = [':attribute_id' => $object->getId()];
279  $select = $connection->select()->from(
280  $this->getTable($additionalTable),
281  ['attribute_id']
282  )->where(
283  'attribute_id = :attribute_id'
284  );
285  $result = $connection->fetchOne($select, $bind);
286  if ($result) {
287  $where = ['attribute_id = ?' => $object->getId()];
288  $connection->update($this->getTable($additionalTable), $data, $where);
289  } else {
290  $connection->insert($this->getTable($additionalTable), $data);
291  }
292  }
293  return $this;
294  }
295 
307  public function saveInSetIncluding(
308  AbstractModel $object,
309  $attributeEntityId = null,
310  $attributeSetId = null,
311  $attributeGroupId = null,
312  $attributeSortOrder = null
313  ) {
314  $attributeId = $attributeEntityId === null ? (int)$object->getId() : (int)$attributeEntityId;
315  $setId = $attributeSetId === null ? (int)$object->getAttributeSetId() : (int)$attributeSetId;
316  $groupId = $attributeGroupId === null ? (int)$object->getAttributeGroupId() : (int)$attributeGroupId;
317  $attributeSortOrder = $attributeSortOrder === null ? (int)$object->getSortOrder() : (int)$attributeSortOrder;
318 
319  if ($setId && $groupId && $object->getEntityTypeId()) {
320  $connection = $this->getConnection();
321  $table = $this->getTable('eav_entity_attribute');
322 
323  $sortOrder = $attributeSortOrder ?: $this->_getMaxSortOrder($object) + 1;
324  $data = [
325  'entity_type_id' => $object->getEntityTypeId(),
326  'attribute_set_id' => $setId,
327  'attribute_group_id' => $groupId,
328  'attribute_id' => $attributeId,
329  'sort_order' => $sortOrder,
330  ];
331 
332  $where = ['attribute_id =?' => $attributeId, 'attribute_set_id =?' => $setId];
333 
334  $connection->delete($table, $where);
335  $connection->insert($table, $data);
336  }
337  return $this;
338  }
339 
346  protected function _saveOption(AbstractModel $object)
347  {
348  $option = $object->getOption();
349  if (!is_array($option)) {
350  return $this;
351  }
352 
353  $defaultValue = $object->getDefault() ?: [];
354  if (isset($option['value'])) {
355  if (!is_array($object->getDefault())) {
356  $object->setDefault([]);
357  }
358  $defaultValue = $this->_processAttributeOptions($object, $option);
359  }
360 
361  $this->_saveDefaultValue($object, $defaultValue);
362  return $this;
363  }
364 
372  protected function _processAttributeOptions($object, $option)
373  {
374  $defaultValue = [];
375  foreach ($option['value'] as $optionId => $values) {
376  $intOptionId = $this->_updateAttributeOption($object, $optionId, $option);
377  if ($intOptionId === false) {
378  continue;
379  }
380  $this->_updateDefaultValue($object, $optionId, $intOptionId, $defaultValue);
382  $this->_updateAttributeOptionValues($intOptionId, $values);
383  }
384  return $defaultValue;
385  }
386 
394  protected function _checkDefaultOptionValue($values)
395  {
396  if (!isset($values[0])) {
397  throw new \Magento\Framework\Exception\LocalizedException(
398  __("The default option isn't defined. Set the option and try again.")
399  );
400  }
401  }
402 
412  protected function _updateDefaultValue($object, $optionId, $intOptionId, &$defaultValue)
413  {
414  if (in_array($optionId, $object->getDefault())) {
415  $frontendInput = $object->getFrontendInput();
416  if ($frontendInput === 'multiselect') {
417  $defaultValue[] = $intOptionId;
418  } elseif ($frontendInput === 'select') {
419  $defaultValue = [$intOptionId];
420  }
421  }
422  }
423 
431  protected function _saveDefaultValue($object, $defaultValue)
432  {
433  if ($defaultValue !== null) {
434  $bind = ['default_value' => implode(',', $defaultValue)];
435  $where = ['attribute_id = ?' => $object->getId()];
436  $this->getConnection()->update($this->getMainTable(), $bind, $where);
437  }
438  }
439 
448  protected function _updateAttributeOption($object, $optionId, $option)
449  {
450  $connection = $this->getConnection();
451  $table = $this->getTable('eav_attribute_option');
452  // ignore strings that start with a number
453  $intOptionId = is_numeric($optionId) ? (int)$optionId : 0;
454 
455  if (!empty($option['delete'][$optionId])) {
456  if ($intOptionId) {
457  $connection->delete($table, ['option_id = ?' => $intOptionId]);
458  }
459  return false;
460  }
461 
462  $sortOrder = empty($option['order'][$optionId]) ? 0 : $option['order'][$optionId];
463  if (!$intOptionId) {
464  $data = ['attribute_id' => $object->getId(), 'sort_order' => $sortOrder];
465  $connection->insert($table, $data);
466  $intOptionId = $connection->lastInsertId($table);
467  } else {
468  $data = ['sort_order' => $sortOrder];
469  $where = ['option_id = ?' => $intOptionId];
470  $connection->update($table, $data, $where);
471  }
472 
473  return $intOptionId;
474  }
475 
484  {
485  $connection = $this->getConnection();
486  $table = $this->getTable('eav_attribute_option_value');
487 
488  $connection->delete($table, ['option_id = ?' => $optionId]);
489 
490  $stores = $this->_storeManager->getStores(true);
491  foreach ($stores as $store) {
492  $storeId = $store->getId();
493  if (!empty($values[$storeId]) || isset($values[$storeId]) && $values[$storeId] == '0') {
494  $data = ['option_id' => $optionId, 'store_id' => $storeId, 'value' => $values[$storeId]];
495  $connection->insert($table, $data);
496  }
497  }
498  }
499 
507  public function getIdByCode($entityType, $code)
508  {
509  $connection = $this->getConnection();
510  $bind = [':entity_type_code' => $entityType, ':attribute_code' => $code];
511  $select = $connection->select()->from(
512  ['a' => $this->getTable('eav_attribute')],
513  ['a.attribute_id']
514  )->join(
515  ['t' => $this->getTable('eav_entity_type')],
516  'a.entity_type_id = t.entity_type_id',
517  []
518  )->where(
519  't.entity_type_code = :entity_type_code'
520  )->where(
521  'a.attribute_code = :attribute_code'
522  );
523 
524  return $connection->fetchOne($select, $bind);
525  }
526 
534  public function getEntityAttribute($entityAttributeId)
535  {
536  $select = $this->getConnection()->select()->from(
537  $this->getTable('eav_entity_attribute')
538  )->where(
539  'entity_attribute_id = ?',
540  (int)$entityAttributeId
541  );
542  return $this->getConnection()->fetchRow($select);
543  }
544 
551  public function getAttributeCodesByFrontendType($frontendType)
552  {
553  $connection = $this->getConnection();
554  $bind = [':frontend_input' => $frontendType];
555  $select = $connection->select()->from(
556  $this->getTable('eav_attribute'),
557  'attribute_code'
558  )->where(
559  'frontend_input = :frontend_input'
560  );
561 
562  return $connection->fetchCol($select, $bind);
563  }
564 
572  public function getFlatUpdateSelect(AbstractAttribute $attribute, $storeId)
573  {
574  $connection = $this->getConnection();
575  $joinConditionTemplate = "%s.entity_id=%s.entity_id" .
576  " AND %s.entity_type_id = " .
577  $attribute->getEntityTypeId() .
578  " AND %s.attribute_id = " .
579  $attribute->getId() .
580  " AND %s.store_id = %d";
581  $joinCondition = sprintf(
582  $joinConditionTemplate,
583  'e',
584  't1',
585  't1',
586  't1',
587  't1',
589  );
590  if ($attribute->getFlatAddChildData()) {
591  $joinCondition .= ' AND e.child_id = t1.entity_id';
592  }
593 
594  $valueExpr = $connection->getCheckSql('t2.value_id > 0', 't2.value', 't1.value');
595 
597  $select = $connection->select()->joinLeft(
598  ['t1' => $attribute->getBackend()->getTable()],
599  $joinCondition,
600  []
601  )->joinLeft(
602  ['t2' => $attribute->getBackend()->getTable()],
603  sprintf($joinConditionTemplate, 't1', 't2', 't2', 't2', 't2', $storeId),
604  [$attribute->getAttributeCode() => $valueExpr]
605  );
606  if ($attribute->getFlatAddChildData()) {
607  $select->where("e.is_child = ?", 0);
608  }
609 
610  return $select;
611  }
612 
620  public function describeTable($table)
621  {
622  return $this->getConnection()->describeTable($table);
623  }
624 
633  {
634  return $this->_eavEntityType->getAdditionalAttributeTable($entityTypeId);
635  }
636 
644  protected function _afterLoad(AbstractModel $object)
645  {
647  $entityType = $object->getData('entity_type');
648  if ($entityType) {
649  $additionalTable = $entityType->getAdditionalAttributeTable();
650  } else {
651  $additionalTable = $this->getAdditionalAttributeTable($object->getEntityTypeId());
652  }
653 
654  if ($additionalTable) {
655  $connection = $this->getConnection();
656  $bind = [':attribute_id' => $object->getId()];
657  $select = $connection->select()->from(
658  $this->getTable($additionalTable)
659  )->where(
660  'attribute_id = :attribute_id'
661  );
662 
663  $result = $connection->fetchRow($select, $bind);
664  if ($result) {
665  $object->addData($result);
666  }
667  }
668 
669  return $this;
670  }
671 
675  private $storeLabelsCache = [];
676 
683  public function getStoreLabelsByAttributeId($attributeId)
684  {
685  if (!isset($this->storeLabelsCache[$attributeId])) {
686  $connection = $this->getConnection();
687  $bind = [':attribute_id' => $attributeId];
688  $select = $connection->select()->from(
689  $this->getTable('eav_attribute_label'),
690  ['store_id', 'value']
691  )->where(
692  'attribute_id = :attribute_id'
693  );
694  $this->storeLabelsCache[$attributeId] = $connection->fetchPairs($select, $bind);
695  }
696 
697  return $this->storeLabelsCache[$attributeId];
698  }
699 
706  public function getValidAttributeIds($attributeIds)
707  {
708  $connection = $this->getConnection();
709  $select = $connection->select()->from(
710  $this->getMainTable(),
711  ['attribute_id']
712  )->where(
713  'attribute_id IN (?)',
714  $attributeIds
715  );
716 
717  return $connection->fetchCol($select);
718  }
719 
726  public function __sleep()
727  {
729  $properties = array_diff($properties, ['_storeManager']);
730  return $properties;
731  }
732 
739  public function __wakeup()
740  {
743  ->get(\Magento\Store\Model\StoreManagerInterface::class);
744  }
745 
754  private function setStoreLabels(AbstractModel $object, $frontendLabel)
755  {
756  $resultLabel = [];
757  $frontendLabels = $object->getFrontendLabels();
758  if (isset($frontendLabels[0])
759  && $frontendLabels[0] instanceof \Magento\Eav\Model\Entity\Attribute\FrontendLabel
760  ) {
761  foreach ($frontendLabels as $label) {
762  $resultLabel[$label->getStoreId()] = $label->getLabel();
763  }
764  $this->checkDefaultFrontendLabelExists($frontendLabel, $resultLabel);
765  $object->setStoreLabels($resultLabel);
766  }
767  }
768 
777  private function checkDefaultFrontendLabelExists($frontendLabel, $resultLabels)
778  {
779  $isAdminStoreLabel = (isset($resultLabels[0]) && !empty($resultLabels[0]));
780  if (empty($frontendLabel) && !$isAdminStoreLabel) {
781  throw new \Magento\Framework\Exception\LocalizedException(__('The storefront label is not defined.'));
782  }
783  }
784 }
getData($key='', $index=null)
Definition: DataObject.php:119
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$attributeGroupId
_saveAdditionalAttributeData(AbstractModel $object)
Definition: Attribute.php:272
$storeManager
_afterDelete(\Magento\Framework\Model\AbstractModel $object)
Definition: Attribute.php:221
$values
Definition: options.phtml:88
__()
Definition: __.php:13
_updateAttributeOption($object, $optionId, $option)
Definition: Attribute.php:448
__construct(\Magento\Framework\Model\ResourceModel\Db\Context $context, \Magento\Store\Model\StoreManagerInterface $storeManager, Type $eavEntityType, $connectionName=null)
Definition: Attribute.php:56
$label
Definition: details.phtml:21
saveInSetIncluding(AbstractModel $object, $attributeEntityId=null, $attributeSetId=null, $attributeGroupId=null, $attributeSortOrder=null)
Definition: Attribute.php:307
$properties
Definition: categories.php:26
$connection
Definition: bulk.php:13
_updateDefaultValue($object, $optionId, $intOptionId, &$defaultValue)
Definition: Attribute.php:412
$table
Definition: trigger.php:14
loadByCode(AbstractModel $object, $entityTypeId, $code)
Definition: Attribute.php:100
_afterLoad(\Magento\Framework\Model\AbstractModel $object)
Definition: AbstractDb.php:641
deleteEntity(\Magento\Framework\Model\AbstractModel $object)
Definition: Attribute.php:149
$code
Definition: info.phtml:12