123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Catalog\Model\ResourceModel;
- use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
- use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend;
- use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend;
- use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
- use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface;
- /**
- * Catalog entity abstract model
- *
- * @api
- *
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @since 100.0.2
- */
- abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity
- {
- /**
- * Store manager
- *
- * @var \Magento\Store\Model\StoreManagerInterface
- */
- protected $_storeManager;
- /**
- * Model factory
- *
- * @var \Magento\Catalog\Model\Factory
- */
- protected $_modelFactory;
- /**
- * @param \Magento\Eav\Model\Entity\Context $context
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
- * @param \Magento\Catalog\Model\Factory $modelFactory
- * @param array $data
- * @param UniqueValidationInterface|null $uniqueValidator
- */
- public function __construct(
- \Magento\Eav\Model\Entity\Context $context,
- \Magento\Store\Model\StoreManagerInterface $storeManager,
- \Magento\Catalog\Model\Factory $modelFactory,
- $data = [],
- UniqueValidationInterface $uniqueValidator = null
- ) {
- $this->_storeManager = $storeManager;
- $this->_modelFactory = $modelFactory;
- parent::__construct($context, $data, $uniqueValidator);
- }
- /**
- * Re-declare attribute model
- *
- * @return string
- */
- protected function _getDefaultAttributeModel()
- {
- return \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class;
- }
- /**
- * Returns default Store ID
- *
- * @return int
- */
- public function getDefaultStoreId()
- {
- return \Magento\Store\Model\Store::DEFAULT_STORE_ID;
- }
- /**
- * Check whether the attribute is Applicable to the object
- *
- * @param \Magento\Framework\DataObject $object
- * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
- * @return boolean
- */
- protected function _isApplicableAttribute($object, $attribute)
- {
- $applyTo = $attribute->getApplyTo() ?: [];
- return (count($applyTo) == 0 || in_array($object->getTypeId(), $applyTo))
- && $attribute->isInSet($object->getAttributeSetId());
- }
- /**
- * Check whether attribute instance (attribute, backend, frontend or source) has method and applicable
- *
- * @param AbstractAttribute|AbstractBackend|AbstractFrontend|AbstractSource $instance
- * @param string $method
- * @param array $args array of arguments
- * @return boolean
- */
- protected function _isCallableAttributeInstance($instance, $method, $args)
- {
- if ($instance instanceof \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
- && ($method == 'beforeSave' || $method == 'afterSave')
- ) {
- $attributeCode = $instance->getAttribute()->getAttributeCode();
- if (isset($args[0])
- && $args[0] instanceof \Magento\Framework\DataObject
- && $args[0]->getData($attributeCode) === false
- ) {
- return false;
- }
- }
- return parent::_isCallableAttributeInstance($instance, $method, $args);
- }
- /**
- * Retrieve select object for loading entity attributes values
- *
- * Join attribute store value
- *
- * @param \Magento\Framework\DataObject $object
- * @param string $table
- * @return \Magento\Framework\DB\Select
- */
- protected function _getLoadAttributesSelect($object, $table)
- {
- /**
- * This condition is applicable for all cases when we was work in not single
- * store mode, customize some value per specific store view and than back
- * to single store mode. We should load correct values
- */
- if ($this->_storeManager->hasSingleStore()) {
- $storeId = (int) $this->_storeManager->getStore(true)->getId();
- } else {
- $storeId = (int) $object->getStoreId();
- }
- $setId = $object->getAttributeSetId();
- $storeIds = [$this->getDefaultStoreId()];
- if ($storeId != $this->getDefaultStoreId()) {
- $storeIds[] = $storeId;
- }
- $select = $this->getConnection()
- ->select()
- ->from(['attr_table' => $table], [])
- ->where("attr_table.{$this->getLinkField()} = ?", $object->getData($this->getLinkField()))
- ->where('attr_table.store_id IN (?)', $storeIds);
- if ($setId) {
- $select->join(
- ['set_table' => $this->getTable('eav_entity_attribute')],
- $this->getConnection()->quoteInto(
- 'attr_table.attribute_id = set_table.attribute_id' . ' AND set_table.attribute_set_id = ?',
- $setId
- ),
- []
- );
- }
- return $select;
- }
- /**
- * Prepare select object for loading entity attributes values
- *
- * @param array $selects
- * @return \Magento\Framework\DB\Select
- */
- protected function _prepareLoadSelect(array $selects)
- {
- $select = parent::_prepareLoadSelect($selects);
- $select->order('store_id');
- return $select;
- }
- /**
- * Insert or Update attribute data
- *
- * @param \Magento\Catalog\Model\AbstractModel $object
- * @param AbstractAttribute $attribute
- * @param mixed $value
- * @return $this
- */
- protected function _saveAttributeValue($object, $attribute, $value)
- {
- $connection = $this->getConnection();
- $hasSingleStore = $this->_storeManager->hasSingleStore();
- $storeId = $hasSingleStore
- ? $this->getDefaultStoreId()
- : (int) $this->_storeManager->getStore($object->getStoreId())->getId();
- $table = $attribute->getBackend()->getTable();
- /**
- * If we work in single store mode all values should be saved just
- * for default store id
- * In this case we clear all not default values
- */
- $entityIdField = $this->getLinkField();
- $conditions = [
- 'attribute_id = ?' => $attribute->getAttributeId(),
- "{$entityIdField} = ?" => $object->getData($entityIdField),
- 'store_id <> ?' => $storeId
- ];
- if ($hasSingleStore
- && !$object->isObjectNew()
- && $this->isAttributePresentForNonDefaultStore($attribute, $conditions)
- ) {
- $connection->delete(
- $table,
- $conditions
- );
- }
- $data = new \Magento\Framework\DataObject(
- [
- 'attribute_id' => $attribute->getAttributeId(),
- 'store_id' => $storeId,
- $entityIdField => $object->getData($entityIdField),
- 'value' => $this->_prepareValueForSave($value, $attribute),
- ]
- );
- $bind = $this->_prepareDataForTable($data, $table);
- if ($attribute->isScopeStore()) {
- /**
- * Update attribute value for store
- */
- $this->_attributeValuesToSave[$table][] = $bind;
- } elseif ($attribute->isScopeWebsite() && $storeId != $this->getDefaultStoreId()) {
- /**
- * Update attribute value for website
- */
- $storeIds = $this->_storeManager->getStore($storeId)->getWebsite()->getStoreIds(true);
- foreach ($storeIds as $storeId) {
- $bind['store_id'] = (int) $storeId;
- $this->_attributeValuesToSave[$table][] = $bind;
- }
- } else {
- /**
- * Update global attribute value
- */
- $bind['store_id'] = $this->getDefaultStoreId();
- $this->_attributeValuesToSave[$table][] = $bind;
- }
- return $this;
- }
- /**
- * Check if attribute present for non default Store View.
- *
- * Prevent "delete" query locking in a case when nothing to delete
- *
- * @param AbstractAttribute $attribute
- * @param array $conditions
- *
- * @return boolean
- */
- private function isAttributePresentForNonDefaultStore($attribute, $conditions)
- {
- $connection = $this->getConnection();
- $select = $connection->select()->from($attribute->getBackend()->getTable());
- foreach ($conditions as $condition => $conditionValue) {
- $select->where($condition, $conditionValue);
- }
- $select->limit(1);
- return !empty($connection->fetchRow($select));
- }
- /**
- * Insert entity attribute value
- *
- * @param \Magento\Framework\DataObject $object
- * @param AbstractAttribute $attribute
- * @param mixed $value
- * @return $this
- */
- protected function _insertAttribute($object, $attribute, $value)
- {
- /**
- * save required attributes in global scope every time if store id different from default
- */
- $storeId = (int) $this->_storeManager->getStore($object->getStoreId())->getId();
- if ($this->getDefaultStoreId() != $storeId) {
- if ($attribute->getIsRequired() || $attribute->getIsRequiredInAdminStore()) {
- $table = $attribute->getBackend()->getTable();
- $select = $this->getConnection()->select()
- ->from($table)
- ->where('attribute_id = ?', $attribute->getAttributeId())
- ->where('store_id = ?', $this->getDefaultStoreId())
- ->where($this->getLinkField() . ' = ?', $object->getData($this->getLinkField()));
- $row = $this->getConnection()->fetchOne($select);
- if (!$row) {
- $data = new \Magento\Framework\DataObject(
- [
- 'attribute_id' => $attribute->getAttributeId(),
- 'store_id' => $this->getDefaultStoreId(),
- $this->getLinkField() => $object->getData($this->getLinkField()),
- 'value' => $this->_prepareValueForSave($value, $attribute),
- ]
- );
- $bind = $this->_prepareDataForTable($data, $table);
- $this->getConnection()->insertOnDuplicate($table, $bind, ['value']);
- }
- }
- }
- return $this->_saveAttributeValue($object, $attribute, $value);
- }
- /**
- * Update entity attribute value
- *
- * @param \Magento\Framework\DataObject $object
- * @param AbstractAttribute $attribute
- * @param mixed $valueId
- * @param mixed $value
- * @return $this
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- protected function _updateAttribute($object, $attribute, $valueId, $value)
- {
- return $this->_saveAttributeValue($object, $attribute, $value);
- }
- /**
- * Update attribute value for specific store
- *
- * @param \Magento\Catalog\Model\AbstractModel $object
- * @param object $attribute
- * @param mixed $value
- * @param int $storeId
- * @return $this
- */
- protected function _updateAttributeForStore($object, $attribute, $value, $storeId)
- {
- $connection = $this->getConnection();
- $table = $attribute->getBackend()->getTable();
- $entityIdField = $this->getLinkField();
- $select = $connection->select()
- ->from($table, 'value_id')
- ->where("$entityIdField = :entity_field_id")
- ->where('store_id = :store_id')
- ->where('attribute_id = :attribute_id');
- $bind = [
- 'entity_field_id' => $object->getId(),
- 'store_id' => $storeId,
- 'attribute_id' => $attribute->getId(),
- ];
- $valueId = $connection->fetchOne($select, $bind);
- /**
- * When value for store exist
- */
- if ($valueId) {
- $bind = ['value' => $this->_prepareValueForSave($value, $attribute)];
- $where = ['value_id = ?' => (int) $valueId];
- $connection->update($table, $bind, $where);
- } else {
- $bind = [
- $entityIdField => (int) $object->getId(),
- 'attribute_id' => (int) $attribute->getId(),
- 'value' => $this->_prepareValueForSave($value, $attribute),
- 'store_id' => (int) $storeId,
- ];
- $connection->insert($table, $bind);
- }
- return $this;
- }
- /**
- * Delete entity attribute values
- *
- * @param \Magento\Framework\DataObject $object
- * @param string $table
- * @param array $info
- * @return $this
- */
- protected function _deleteAttributes($object, $table, $info)
- {
- $connection = $this->getConnection();
- $entityIdField = $this->getLinkField();
- $globalValues = [];
- $websiteAttributes = [];
- $storeAttributes = [];
- /**
- * Separate attributes by scope
- */
- foreach ($info as $itemData) {
- $attribute = $this->getAttribute($itemData['attribute_id']);
- if ($attribute->isScopeStore()) {
- $storeAttributes[] = (int) $itemData['attribute_id'];
- } elseif ($attribute->isScopeWebsite()) {
- $websiteAttributes[] = (int) $itemData['attribute_id'];
- } elseif ($itemData['value_id'] !== null) {
- $globalValues[] = (int) $itemData['value_id'];
- }
- }
- /**
- * Delete global scope attributes
- */
- if (!empty($globalValues)) {
- $connection->delete($table, ['value_id IN (?)' => $globalValues]);
- }
- $condition = [
- $entityIdField . ' = ?' => $object->getId(),
- ];
- /**
- * Delete website scope attributes
- */
- if (!empty($websiteAttributes)) {
- $storeIds = $object->getWebsiteStoreIds();
- if (!empty($storeIds)) {
- $delCondition = $condition;
- $delCondition['attribute_id IN(?)'] = $websiteAttributes;
- $delCondition['store_id IN(?)'] = $storeIds;
- $connection->delete($table, $delCondition);
- }
- }
- /**
- * Delete store scope attributes
- */
- if (!empty($storeAttributes)) {
- $delCondition = $condition;
- $delCondition['attribute_id IN(?)'] = $storeAttributes;
- $delCondition['store_id = ?'] = (int) $object->getStoreId();
- $connection->delete($table, $delCondition);
- }
- return $this;
- }
- /**
- * Retrieve Object instance with original data
- *
- * @param \Magento\Framework\DataObject $object
- * @return \Magento\Framework\DataObject
- */
- protected function _getOrigObject($object)
- {
- //TODO:
- $className = get_class($object);
- $origObject = $this->_modelFactory->create($className);
- $origObject->setData([]);
- $origObject->setStoreId($object->getStoreId());
- $this->load($origObject, $object->getData($this->getEntityIdField()));
- return $origObject;
- }
- /**
- * Return if attribute exists in original data array.
- * Checks also attribute's store scope:
- * We should insert on duplicate key update values if we unchecked 'STORE VIEW' checkbox in store view.
- *
- * @param AbstractAttribute $attribute
- * @param mixed $value New value of the attribute.
- * @param array &$origData
- * @return bool
- */
- protected function _canUpdateAttribute(AbstractAttribute $attribute, $value, array &$origData)
- {
- $result = parent::_canUpdateAttribute($attribute, $value, $origData);
- if ($result
- && ($attribute->isScopeStore() || $attribute->isScopeWebsite())
- && !$this->_isAttributeValueEmpty($attribute, $value)
- && $value == $origData[$attribute->getAttributeCode()]
- && isset($origData['store_id'])
- && $origData['store_id'] != $this->getDefaultStoreId()
- ) {
- return false;
- }
- return $result;
- }
- /**
- * Retrieve attribute's raw value from DB.
- *
- * @param int $entityId
- * @param int|string|array $attribute atrribute's ids or codes
- * @param int|\Magento\Store\Model\Store $store
- * @return bool|string|array
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- public function getAttributeRawValue($entityId, $attribute, $store)
- {
- if (!$entityId || empty($attribute)) {
- return false;
- }
- if (!is_array($attribute)) {
- $attribute = [$attribute];
- }
- $attributesData = [];
- $staticAttributes = [];
- $typedAttributes = [];
- $staticTable = null;
- $connection = $this->getConnection();
- foreach ($attribute as $item) {
- /* @var $attribute \Magento\Catalog\Model\Entity\Attribute */
- $item = $this->getAttribute($item);
- if (!$item) {
- continue;
- }
- $attributeCode = $item->getAttributeCode();
- $attrTable = $item->getBackend()->getTable();
- $isStatic = $item->getBackend()->isStatic();
- if ($isStatic) {
- $staticAttributes[] = $attributeCode;
- $staticTable = $attrTable;
- } else {
- /**
- * That structure needed to avoid farther sql joins for getting attribute's code by id
- */
- $typedAttributes[$attrTable][$item->getId()] = $attributeCode;
- }
- }
- /**
- * Collecting static attributes
- */
- if ($staticAttributes) {
- $select = $connection->select()->from(
- $staticTable,
- $staticAttributes
- )->join(
- ['e' => $this->getTable($this->getEntityTable())],
- 'e.' . $this->getLinkField() . ' = ' . $staticTable . '.' . $this->getLinkField()
- )->where(
- 'e.entity_id = :entity_id'
- );
- $attributesData = $connection->fetchRow($select, ['entity_id' => $entityId]);
- }
- /**
- * Collecting typed attributes, performing separate SQL query for each attribute type table
- */
- if ($store instanceof \Magento\Store\Model\Store) {
- $store = $store->getId();
- }
- $store = (int) $store;
- if ($typedAttributes) {
- foreach ($typedAttributes as $table => $_attributes) {
- $select = $connection->select()
- ->from(['default_value' => $table], ['attribute_id'])
- ->join(
- ['e' => $this->getTable($this->getEntityTable())],
- 'e.' . $this->getLinkField() . ' = ' . 'default_value.' . $this->getLinkField(),
- ''
- )->where('default_value.attribute_id IN (?)', array_keys($_attributes))
- ->where("e.entity_id = :entity_id")
- ->where('default_value.store_id = ?', 0);
- $bind = ['entity_id' => $entityId];
- if ($store != $this->getDefaultStoreId()) {
- $valueExpr = $connection->getCheckSql(
- 'store_value.value IS NULL',
- 'default_value.value',
- 'store_value.value'
- );
- $joinCondition = [
- $connection->quoteInto('store_value.attribute_id IN (?)', array_keys($_attributes)),
- "store_value.{$this->getLinkField()} = e.{$this->getLinkField()}",
- 'store_value.store_id = :store_id',
- ];
- $select->joinLeft(
- ['store_value' => $table],
- implode(' AND ', $joinCondition),
- ['attr_value' => $valueExpr]
- );
- $bind['store_id'] = $store;
- } else {
- $select->columns(['attr_value' => 'value'], 'default_value');
- }
- $result = $connection->fetchPairs($select, $bind);
- foreach ($result as $attrId => $value) {
- $attrCode = $typedAttributes[$table][$attrId];
- $attributesData[$attrCode] = $value;
- }
- }
- }
- if (is_array($attributesData) && sizeof($attributesData) == 1) {
- $attributesData = array_shift($attributesData);
- }
- return $attributesData === false ? false : $attributesData;
- }
- }
|