From 74f912689f1a96f72875bd163fffd08218609903 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Tue, 24 Jan 2023 03:50:25 +0100 Subject: [PATCH 01/25] use eav/config singleton for caching entity types and attributes, replacing several DB queries per request with a single cache entry --- .../Catalog/Product/Attribute/Set/Main.php | 4 +- .../Product/Attribute/Set/Main/Formgroup.php | 3 +- app/code/core/Mage/Api2/Model/Resource.php | 2 +- app/code/core/Mage/Catalog/Helper/Data.php | 4 +- .../core/Mage/Catalog/Model/Api2/Product.php | 2 +- .../Model/Api2/Product/Validator/Product.php | 2 +- app/code/core/Mage/Catalog/Model/Config.php | 31 +- app/code/core/Mage/Catalog/Model/Layer.php | 31 +- .../Mage/Catalog/Model/Resource/Category.php | 14 +- .../Model/Resource/Product/Flat/Indexer.php | 4 +- .../Model/Indexer/Minimalprice.php | 4 +- .../Product/Edit/Tab/Downloadable/Links.php | 4 +- app/code/core/Mage/Eav/Model/Config.php | 774 ++++++++---------- .../Eav/Model/Entity/Attribute/Abstract.php | 27 +- .../Eav/Model/Entity/Collection/Abstract.php | 7 +- .../Model/Resource/Entity/Attribute/Set.php | 62 +- .../Sales/Model/Resource/Order/Abstract.php | 2 +- app/code/core/Mage/Tax/Helper/Data.php | 4 +- 18 files changed, 424 insertions(+), 557 deletions(-) diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php index ad875a675e2..acd3eb4950d 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php @@ -390,8 +390,8 @@ public function getIsCurrentSetDefault() { $isDefault = $this->getData('is_current_set_default'); if (is_null($isDefault)) { - $defaultSetId = Mage::getModel('eav/entity_type') - ->load(Mage::registry('entityType')) + $defaultSetId = Mage::getSingleton('eav/config') + ->getEntityType(Mage::registry('entityType')) ->getDefaultAttributeSetId(); $isDefault = $this->_getSetId() == $defaultSetId; $this->setData('is_current_set_default', $isDefault); diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main/Formgroup.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main/Formgroup.php index 44b952212af..868e1b5be03 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main/Formgroup.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main/Formgroup.php @@ -76,8 +76,7 @@ protected function _getSetId() { return ((int) $this->getRequest()->getParam('id') > 0) ? (int) $this->getRequest()->getParam('id') - : Mage::getModel('eav/entity_type') - ->load(Mage::registry('entityType')) + : Mage::getSingleton('eav/config')->getEntityType(Mage::registry('entityType')) ->getDefaultAttributeSetId(); } } diff --git a/app/code/core/Mage/Api2/Model/Resource.php b/app/code/core/Mage/Api2/Model/Resource.php index 200dbbf1a22..c811c9f001a 100644 --- a/app/code/core/Mage/Api2/Model/Resource.php +++ b/app/code/core/Mage/Api2/Model/Resource.php @@ -1030,7 +1030,7 @@ public function getEavAttributes($onlyVisible = false, $excludeSystem = false) $model = $this->getConfig()->getResourceWorkingModel($this->getResourceType()); /** @var Mage_Eav_Model_Entity_Type $entityType */ - $entityType = Mage::getModel('eav/entity_type')->load($model, 'entity_model'); + $entityType = Mage::getSingleton('eav/config')->getEntityType($model, 'entity_model'); /** @var Mage_Eav_Model_Entity_Attribute $attribute */ foreach ($entityType->getAttributeCollection() as $attribute) { diff --git a/app/code/core/Mage/Catalog/Helper/Data.php b/app/code/core/Mage/Catalog/Helper/Data.php index 45d0f8de5d3..8c6d078c1f7 100644 --- a/app/code/core/Mage/Catalog/Helper/Data.php +++ b/app/code/core/Mage/Catalog/Helper/Data.php @@ -416,8 +416,8 @@ public function canApplyMsrpToProductType($product) { if ($this->_mapApplyToProductType === null) { /** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */ - $attribute = Mage::getModel('catalog/resource_eav_attribute') - ->loadByCode(Mage_Catalog_Model_Product::ENTITY, 'msrp_enabled'); + $attribute = Mage::getSingleton('eav/config') + ->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'msrp_enabled'); $this->_mapApplyToProductType = $attribute->getApplyTo(); } return empty($this->_mapApplyToProductType) || in_array($product->getTypeId(), $this->_mapApplyToProductType); diff --git a/app/code/core/Mage/Catalog/Model/Api2/Product.php b/app/code/core/Mage/Catalog/Model/Api2/Product.php index 347af285458..2029654ff0b 100644 --- a/app/code/core/Mage/Catalog/Model/Api2/Product.php +++ b/app/code/core/Mage/Catalog/Model/Api2/Product.php @@ -39,7 +39,7 @@ public function getAvailableAttributes($userType, $operation) { $attributes = $this->getAvailableAttributesFromConfig(); /** @var Mage_Eav_Model_Entity_Type $entityType */ - $entityType = Mage::getModel('eav/entity_type')->loadByCode('catalog_product'); + $entityType = Mage::getSingleton('eav/config')->getEntityType('catalog_product'); $entityOnlyAttrs = $this->getEntityOnlyAttributes($userType, $operation); /** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */ foreach ($entityType->getAttributeCollection() as $attribute) { diff --git a/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php b/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php index 14ea4e60014..900e891f77a 100644 --- a/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php +++ b/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php @@ -107,7 +107,7 @@ public function isValidData(array $data) try { $this->_validateProductType($data); /** @var Mage_Eav_Model_Entity_Type $productEntity */ - $productEntity = Mage::getModel('eav/entity_type')->loadByCode(Mage_Catalog_Model_Product::ENTITY); + $productEntity = Mage::getSingleton('eav/config')->getEntityType(Mage_Catalog_Model_Product::ENTITY); $this->_validateAttributeSet($data, $productEntity); $this->_validateSku($data); $this->_validateGiftOptions($data); diff --git a/app/code/core/Mage/Catalog/Model/Config.php b/app/code/core/Mage/Catalog/Model/Config.php index 3241fab17df..6f21d8b03f6 100644 --- a/app/code/core/Mage/Catalog/Model/Config.php +++ b/app/code/core/Mage/Catalog/Model/Config.php @@ -330,17 +330,13 @@ protected function _getResource() public function getAttributesUsedInProductListing() { if (is_null($this->_usedInProductListing)) { + $allAttributes = Mage::getSingleton('eav/config') + ->getAttributes(Mage_Catalog_Model_Product::ENTITY); $this->_usedInProductListing = []; - $entityType = Mage_Catalog_Model_Product::ENTITY; - $attributesData = $this->_getResource() - ->setStoreId($this->getStoreId()) - ->getAttributesUsedInListing(); - Mage::getSingleton('eav/config') - ->importAttributesData($entityType, $attributesData); - foreach ($attributesData as $attributeData) { - $attributeCode = $attributeData['attribute_code']; - $this->_usedInProductListing[$attributeCode] = Mage::getSingleton('eav/config') - ->getAttribute($entityType, $attributeCode); + foreach ($allAttributes as $attribute) { + if ($attribute->getData('used_in_product_listing')) { + $this->_usedInProductListing[$attribute->getAttributeCode()] = $attribute; + } } } return $this->_usedInProductListing; @@ -354,16 +350,13 @@ public function getAttributesUsedInProductListing() public function getAttributesUsedForSortBy() { if (is_null($this->_usedForSortBy)) { + $allAttributes = Mage::getSingleton('eav/config') + ->getAttributes(Mage_Catalog_Model_Product::ENTITY); $this->_usedForSortBy = []; - $entityType = Mage_Catalog_Model_Product::ENTITY; - $attributesData = $this->_getResource() - ->getAttributesUsedForSortBy(); - Mage::getSingleton('eav/config') - ->importAttributesData($entityType, $attributesData); - foreach ($attributesData as $attributeData) { - $attributeCode = $attributeData['attribute_code']; - $this->_usedForSortBy[$attributeCode] = Mage::getSingleton('eav/config') - ->getAttribute($entityType, $attributeCode); + foreach ($allAttributes as $attribute) { + if ($attribute->getData('used_for_sort_by')) { + $this->_usedForSortBy[$attribute->getAttributeCode()] = $attribute; + } } } return $this->_usedForSortBy; diff --git a/app/code/core/Mage/Catalog/Model/Layer.php b/app/code/core/Mage/Catalog/Model/Layer.php index bb267c0215d..92dcfee1f13 100644 --- a/app/code/core/Mage/Catalog/Model/Layer.php +++ b/app/code/core/Mage/Catalog/Model/Layer.php @@ -202,7 +202,7 @@ public function getCurrentStore() /** * Get collection of all filterable attributes for layer products set * - * @return Mage_Catalog_Model_Resource_Product_Attribute_Collection|array + * @return array */ public function getFilterableAttributes() { @@ -210,17 +210,26 @@ public function getFilterableAttributes() if (!$setIds) { return []; } - /** @var Mage_Catalog_Model_Resource_Product_Attribute_Collection $collection */ - $collection = Mage::getResourceModel('catalog/product_attribute_collection'); - $collection - ->setItemObjectClass('catalog/resource_eav_attribute') - ->setAttributeSetFilter($setIds) - ->addStoreLabel(Mage::app()->getStore()->getId()) - ->setOrder('position', 'ASC'); - $collection = $this->_prepareAttributeCollection($collection); - $collection->load(); - return $collection; + $eavConfig=Mage::getSingleton('eav/config'); + /** @var Mage_Catalog_Model_Resource_Eav_Attribute[] $attributes */ + $attributes = []; + foreach($setIds as $setId) { + $setAttributeIds = $eavConfig->getAttributeSetAttributeIds($setId); + foreach($setAttributeIds as $attributeId) { + if(!isset($attributes[$attributeId])) { + $attribute = $eavConfig->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeId); + if($attribute->getIsFilterable()) { + $attributes[$attributeId] = $attribute; + } + } + } + } + usort($attributes, function ($a,$b) { + return $a->getPosition() - $b->getPosition(); + }); + + return $attributes; } /** diff --git a/app/code/core/Mage/Catalog/Model/Resource/Category.php b/app/code/core/Mage/Catalog/Model/Resource/Category.php index 0d032fe9b11..c86cefda5a0 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Category.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Category.php @@ -483,17 +483,9 @@ public function getChildrenAmount($category, $isActiveFlag = true) protected function _getIsActiveAttributeId() { if ($this->_isActiveAttributeId === null) { - $bind = [ - 'catalog_category' => Mage_Catalog_Model_Category::ENTITY, - 'is_active' => 'is_active', - ]; - $select = $this->_getReadAdapter()->select() - ->from(['a' => $this->getTable('eav/attribute')], ['attribute_id']) - ->join(['t' => $this->getTable('eav/entity_type')], 'a.entity_type_id = t.entity_type_id') - ->where('entity_type_code = :catalog_category') - ->where('attribute_code = :is_active'); - - $this->_isActiveAttributeId = $this->_getReadAdapter()->fetchOne($select, $bind); + $this->_isActiveAttributeId = Mage::getSingleton('eav/config') + ->getAttribute(Mage_Catalog_Model_Category::ENTITY, 'is_active') + ->getId(); } return $this->_isActiveAttributeId; diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php index 7784e548bed..9e728440ecb 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php @@ -264,8 +264,8 @@ public function getAttribute($attributeCode) { $attributes = $this->getAttributes(); if (!isset($attributes[$attributeCode])) { - $attribute = Mage::getModel('catalog/resource_eav_attribute') - ->loadByCode($this->getEntityTypeId(), $attributeCode); + $attribute = Mage::getSingleton('eav/config') + ->getAttribute($this->getEntityTypeId(), $attributeCode); if (!$attribute->getId()) { Mage::throwException(Mage::helper('catalog')->__('Invalid attribute %s', $attributeCode)); } diff --git a/app/code/core/Mage/CatalogIndex/Model/Indexer/Minimalprice.php b/app/code/core/Mage/CatalogIndex/Model/Indexer/Minimalprice.php index 309db7e9f23..0091235d6bb 100644 --- a/app/code/core/Mage/CatalogIndex/Model/Indexer/Minimalprice.php +++ b/app/code/core/Mage/CatalogIndex/Model/Indexer/Minimalprice.php @@ -65,7 +65,7 @@ public function getTierPriceAttribute() { $data = $this->getData('tier_price_attribute'); if (is_null($data)) { - $data = Mage::getModel('eav/entity_attribute')->loadByCode(Mage_Catalog_Model_Product::ENTITY, 'tier_price'); + $data = Mage::getSingleton('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'tier_price'); $this->setData('tier_price_attribute', $data); } return $data; @@ -79,7 +79,7 @@ public function getPriceAttribute() { $data = $this->getData('price_attribute'); if (is_null($data)) { - $data = Mage::getModel('eav/entity_attribute')->loadByCode(Mage_Catalog_Model_Product::ENTITY, 'price'); + $data = Mage::getSingleton('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'price'); $this->setData('price_attribute', $data); } return $data; diff --git a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php index e1edf6a32ed..70e55bbabdc 100644 --- a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php +++ b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php @@ -70,8 +70,8 @@ public function getPurchasedSeparatelyAttribute() if (is_null($this->_purchasedSeparatelyAttribute)) { $_attributeCode = 'links_purchased_separately'; - $this->_purchasedSeparatelyAttribute = Mage::getModel('eav/entity_attribute') - ->loadByCode(Mage_Catalog_Model_Product::ENTITY, $_attributeCode); + $this->_purchasedSeparatelyAttribute = Mage::getSingleton('eav/config') + ->getAttribute(Mage_Catalog_Model_Product::ENTITY, $_attributeCode); } return $this->_purchasedSeparatelyAttribute; diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 4704b6dec7b..b888de83eb2 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -28,191 +28,255 @@ */ class Mage_Eav_Model_Config { - public const ENTITIES_CACHE_ID = 'EAV_ENTITY_TYPES'; - public const ATTRIBUTES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES'; - /** - * Entity types data - * - * @var array|null + * Cache key for storing: + * - EAV entities + * - EAV attributes + * - EAV attribute extension table data */ - protected $_entityData; + public const EAV_DATA_CACHE = 'EAV_DATA_CACHE'; - /** - * Attributes data - * - * @var array|null - */ - protected $_attributeData; + public const ENTITIES_CACHE_ID = 'EAV_ENTITY_TYPES'; + + protected $_storeInitialized = []; /** - * Information about preloaded attributes - * - * @var array + * @var Mage_Eav_Model_Entity_Type[]|null */ - protected $_preloadedAttributes = []; + protected $_entityTypes; /** - * Information about entity types with initialized attributes - * - * @var array + * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][]|null */ - protected $_initializedAttributes = []; + protected $_entityTypeAttributes; /** - * Attribute codes cache array - * - * @var array + * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][]|null */ - protected $_attributeCodes = []; + protected $_entityTypeAttributeIdByCode; /** - * Initialized objects - * - * array ($objectId => $object) - * - * @var array|null + * Attribute set relation information. Structure: + *
+ * [ + * int attribute_id => [ + * int set_id => [ + * int group_id, + * int group_sort, + * int sort, + * ] + * ] + * @var [][][][]mixed */ - protected $_objects; + protected $_attributeSetInfo; /** - * References between codes and identifiers - * - * array ( - * 'attributes'=> array ($attributeId => $attributeCode), - * 'entities' => array ($entityId => $entityCode) - * ) - * - * @var array|null + * Special local cache for default attributes to avoid re-hydrating them + * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][] */ - protected $_references; + protected $_defaultAttributes = []; /** * Cache flag * * @var bool */ - protected $_isCacheEnabled = null; - - /** - * Array of attributes objects used in collections - * - * @var array - */ - protected $_collectionAttributes = []; + protected $_isCacheEnabled = null; /** * Reset object state * - * @deprecated * @return $this + * @deprecated */ public function clear() { - $this->_entityData = null; - $this->_attributeData = null; - $this->_objects = null; - $this->_references = null; - $this->_preloadedAttributes = []; - $this->_initializedAttributes = []; + $this->_entityTypes = null; + $this->_entityTypeAttributes = null; + $this->_entityTypeAttributeIdByCode = null; + $this->_storeInitialized = []; + + Mage::app()->cleanCache([self::ENTITIES_CACHE_ID]); + // Mage::app()->removeCache(self::ENTITIES_CACHE_ID . "_" . Mage::app()->getStore()->getId()); + return $this; } - /** - * Get object by identifier - * - * @param mixed $id - * @return mixed - */ - protected function _load($id) + public function __construct() { - return $this->_objects[$id] ?? null; } - /** - * Associate object with identifier - * - * @param mixed $obj - * @param mixed $id - * @return Mage_Eav_Model_Config - */ - protected function _save($obj, $id) + protected function _initializeStore($storeId = null) { - $this->_objects[$id] = $obj; - return $this; - } - /** - * Specify reference for entity type id - * - * @param int $id - * @param string $code - * @return Mage_Eav_Model_Config - */ - protected function _addEntityTypeReference($id, $code) - { - $this->_references['entity'][$id] = $code; - return $this; + if ($storeId === null) { + $storeId = Mage::app()->getStore()->getId(); + } else { + // ensure store id is consistent + $storeId = (int)$storeId; + } + if (isset($this->_storeInitialized[$storeId]) && $this->_storeInitialized[$storeId]) { + return; + } + + Varien_Profiler::start('EAV: ' . __METHOD__); + + if ($this->_isCacheEnabled()) { + $this->_loadFromCache($storeId); + } + + if (empty($this->_entityTypes)) { + $this->_loadEntityTypes(); + $this->_loadAttributeSetInfo(); + } + + // load each entity attributes for given storeId + if (empty($this->_entityTypeAttributes[$storeId])) { + $this->_entityTypeAttributes[$storeId] = []; + foreach ($this->_entityTypes as $entityType) { + $this->_loadEntityAttributes($entityType, $storeId); + // $this->_loadEntityDefaultAttributes($entityType, $storeId); + } + } + + if ($this->_isCacheEnabled()) { + $this->_saveToCache($storeId); + } + + $this->_storeInitialized[$storeId] = true; + + Varien_Profiler::stop('EAV: ' . __METHOD__); } /** - * Get entity type code by id - * - * @param int $id - * @return string + * @param $storeId + * @return void + * @throws Exception */ - protected function _getEntityTypeReference($id) + protected function _loadEntityTypes() { - return $this->_references['entity'][$id] ?? null; + Varien_Profiler::start('EAV: ' . __METHOD__); + + // load entity types + $this->_entityTypes = []; + $entityTypeCollection = Mage::getResourceModel('eav/entity_type_collection'); + /** @var $entityType Mage_Eav_Model_Entity_Type */ + foreach ($entityTypeCollection as $entityType) { + // if (empty($entityType->getAttributeModel())) { + // $entityType->setAttributeModel('eav/entity_attribute'); + // } + $this->_entityTypes[$entityType->getId()] = $entityType; + } + + Varien_Profiler::stop('EAV: ' . __METHOD__); } /** - * Specify reference between entity attribute id and attribute code - * - * @param int $id - * @param string $code - * @param string $entityTypeCode - * @return Mage_Eav_Model_Config + * @param Mage_Eav_Model_Entity_Type $entityType + * @param int $storeId + * @return void + * @throws Exception */ - protected function _addAttributeReference($id, $code, $entityTypeCode) + protected function _loadEntityAttributes($entityType, $storeId) { - $this->_references['attribute'][$entityTypeCode][$id] = $code; - return $this; + // preload attributes in array form to avoid instantiating models for every attribute even if it is never accessed + $entityAttributes = $entityType->getAttributeCollection() + ->addStoreLabel($storeId) + ->getData(); + + $this->_entityTypeAttributes[$storeId][$entityType->getId()] = []; + $attributeCodes = []; + + /** @var Mage_Eav_Model_Entity_Attribute_Abstract $entityAttributeData */ + foreach ($entityAttributes as $entityAttributeData) { + $attributeId = $entityAttributeData['attribute_id']; + $attributeCode = $entityAttributeData['attribute_code']; + $this->_entityTypeAttributes[$storeId][$entityType->getId()][$attributeId] = $entityAttributeData; + $this->_entityTypeAttributeIdByCode[$storeId][$entityType->getId()][$attributeCode] = $attributeId; + $attributeCodes[] = $attributeCode; + } + + $entityType->setAttributeCodes($attributeCodes); } /** - * Get attribute code by attribute id - * - * @param int $id - * @param string $entityTypeCode - * @return string + * @param $storeId + * @return void + * @throws Exception */ - protected function _getAttributeReference($id, $entityTypeCode) + protected function _loadAttributeSetInfo() { - return $this->_references['attribute'][$entityTypeCode][$id] ?? null; + Varien_Profiler::start('EAV: ' . __METHOD__); + + $this->_attributeSetInfo = Mage::getResourceModel('eav/entity_attribute_set')->getSetInfo(); + + Varien_Profiler::stop('EAV: ' . __METHOD__); } - /** - * Get internal cache key for entity type code - * - * @param string $code - * @return string - */ - protected function _getEntityKey($code) + protected function _loadFromCache($storeId) { - return 'ENTITY/' . $code; + Varien_Profiler::start('EAV: ' . __METHOD__); + + $cacheData = Mage::app()->loadCache(self::ENTITIES_CACHE_ID . "_" . $storeId); + if ($cacheData === false) { + return; + } + $cacheData = unserialize($cacheData); + + $this->_entityTypes = []; + /** @var $entityType Mage_Eav_Model_Entity_Type */ + foreach ($cacheData['_entityTypes'] as $entityTypeData) { + $entityType = Mage::getModel('eav/entity_type') + ->setData($entityTypeData); + $this->_entityTypes[$entityType->getId()] = $entityType; + } + + $this->_entityTypeAttributes[$storeId] = []; + /** @var $entityType Mage_Eav_Model_Entity_Type */ + foreach ($cacheData['_entityTypeAttributes'] as $entityTypeId => $entityTypeAttributes) { + foreach ($entityTypeAttributes as $attributeData) { + $attributeId = $attributeData['attribute_id']; + $attributeCode = $attributeData['attribute_code']; + $this->_entityTypeAttributes[$storeId][$entityTypeId][$attributeId] = $attributeData; + $this->_entityTypeAttributeIdByCode[$storeId][$entityTypeId][$attributeCode] = $attributeId; + } + } + + $this->_attributeSetInfo = $cacheData['_attributeSetInfo']; + + Varien_Profiler::stop('EAV: ' . __METHOD__); } - /** - * Get internal cache key for attribute object cache - * - * @param string $entityTypeCode - * @param string $attributeCode - * @return string - */ - protected function _getAttributeKey($entityTypeCode, $attributeCode) + protected function _saveToCache($storeId) { - return 'ATTRIBUTE/' . $entityTypeCode . '/' . $attributeCode; + Varien_Profiler::start('EAV: ' . __METHOD__); + + $cacheData = [ + '_entityTypes' => [], + '_entityTypeAttributes' => [], + '_attributeSetInfo' => $this->_attributeSetInfo, + ]; + + foreach ($this->_entityTypes as $entityType) { + $cacheData['_entityTypes'][$entityType->getId()] = $entityType->getData(); + } + + foreach ($this->_entityTypeAttributes[$storeId] as $entityTypeId => $attributes) { + $cacheData['_entityTypeAttributes'][$entityTypeId] = []; + foreach ($attributes as $attribute) { + $attributeId = is_array($attribute) ? $attribute['attribute_id'] : $attribute->getId(); + $attributeData = is_array($attribute) ? $attribute : $attribute->getData(); + $cacheData['_entityTypeAttributes'][$entityTypeId][$attributeId] = $attributeData; + } + } + + Mage::app()->saveCache( + serialize($cacheData), + self::ENTITIES_CACHE_ID . "_" . $storeId, + ['eav', self::ENTITIES_CACHE_ID, Mage_Eav_Model_Entity_Attribute::CACHE_TAG] + ); + + Varien_Profiler::stop('EAV: ' . __METHOD__); } /** @@ -229,153 +293,114 @@ protected function _isCacheEnabled() } /** - * Initialize all entity types data + * Create model instance from array * - * @return $this + * @param array $attributeData + * @return Mage_Eav_Model_Entity_Attribute_Abstract */ - protected function _initEntityTypes() + protected function _hydrateAttribute($attributeData) { - if (is_array($this->_entityData)) { - return $this; - } - Varien_Profiler::start('EAV: ' . __METHOD__); - - /** - * try load information about entity types from cache - */ - if ($this->_isCacheEnabled() - && ($cache = Mage::app()->loadCache(self::ENTITIES_CACHE_ID)) - ) { - $this->_entityData = unserialize($cache, ['allowed_classes' => false]); - foreach ($this->_entityData as $typeCode => $data) { - $typeId = $data['entity_type_id']; - $this->_addEntityTypeReference($typeId, $typeCode); - } - Varien_Profiler::stop('EAV: ' . __METHOD__); - return $this; + $entityType = $this->getEntityType($attributeData['entity_type_id']); + if (!empty($attributeData['attribute_model'])) { + $model = $attributeData['attribute_model']; + } else { + $model = $entityType->getAttributeModel(); } + $attribute = Mage::getModel($model)->setData($attributeData); + if ($attribute) { + $attribute->setData($attributeData); - $entityTypesData = Mage::getModel('eav/entity_type')->getCollection()->getData(); - $types = []; - - /** - * prepare entity type data - */ - foreach ($entityTypesData as $typeData) { - if (!isset($typeData['attribute_model'])) { - $typeData['attribute_model'] = 'eav/entity_attribute'; + $entity = $entityType->getEntity(); + if ($entity && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes())) { + $attribute + ->setBackendType(Mage_Eav_Model_Entity_Attribute_Abstract::TYPE_STATIC) + ->setIsGlobal(1); } - - $typeCode = $typeData['entity_type_code']; - $typeId = $typeData['entity_type_id']; - - $this->_addEntityTypeReference($typeId, $typeCode); - $types[$typeCode] = $typeData; + $attribute + ->setEntityType($entityType) + ->setEntityTypeId($entityType->getId()); } - $this->_entityData = $types; - - if ($this->_isCacheEnabled()) { - Mage::app()->saveCache( - serialize($this->_entityData), - self::ENTITIES_CACHE_ID, - ['eav', Mage_Eav_Model_Entity_Attribute::CACHE_TAG] - ); - } - Varien_Profiler::stop('EAV: ' . __METHOD__); - return $this; + /** @noinspection PhpIncompatibleReturnTypeInspection */ + return $attribute; } /** * Get entity type object by entity type code/identifier * - * @param mixed $code + * @param mixed $code + * @param string|null $code * @return Mage_Eav_Model_Entity_Type */ - public function getEntityType($code) + public function getEntityType($code, $field = null) { if ($code instanceof Mage_Eav_Model_Entity_Type) { return $code; } - Varien_Profiler::start('EAV: ' . __METHOD__); - if (is_numeric($code)) { - $entityCode = $this->_getEntityTypeReference($code); - if ($entityCode !== null) { - $code = $entityCode; + $storeId = Mage::app()->getStore()->getId(); + $this->_initializeStore($storeId); + + if (empty($field) && is_numeric($code)) { + $entity = $this->_entityTypes[$code]; + if ($entity !== null) { + return $entity; } } - $entityKey = $this->_getEntityKey($code); - $entityType = $this->_load($entityKey); - if ($entityType) { - Varien_Profiler::stop('EAV: ' . __METHOD__); - return $entityType; - } + if (empty($field)) $field = 'entity_type_code'; - $entityType = Mage::getModel('eav/entity_type'); - if (isset($this->_entityData[$code])) { - $entityType->setData($this->_entityData[$code]); - } else { - if (is_numeric($code)) { - $entityType->load($code); - } else { - $entityType->loadByCode($code); - } - - if (!$entityType->getId()) { - Mage::throwException(Mage::helper('eav')->__('Invalid entity_type specified: %s', $code)); + foreach ($this->_entityTypes as $entityType) { + if ($entityType->getData($field) == $code) { + return $entityType; } } - $this->_addEntityTypeReference($entityType->getId(), $entityType->getEntityTypeCode()); - $this->_save($entityType, $entityKey); - Varien_Profiler::stop('EAV: ' . __METHOD__); - return $entityType; + return null; } /** - * Initialize all attributes for entity type - * - * @param string $entityType - * @return Mage_Eav_Model_Config + * Default attributes are loaded only on getAttribute(...) call to avoid infinite loading loop between + * Entity_Type->getEntity() which itself requires this class and re-triggers loading. + * + * @param Mage_Eav_Model_Entity_Type $entityType + * @param int $storeId + * @param string $attributeCode + * @return Mage_Eav_Model_Entity_Attribute_Abstract|false + * @throws Exception */ - protected function _initAttributes($entityType) + protected function _getDefaultAttributeIfExists($entityType, $attributeCode, $storeId) { - $entityType = $this->getEntityType($entityType); - $entityTypeCode = $entityType->getEntityTypeCode(); - - if (isset($this->_initializedAttributes[$entityTypeCode])) { - return $this; - } - Varien_Profiler::start('EAV: ' . __METHOD__); - - /** @var Mage_Eav_Model_Resource_Entity_Attribute_Collection $attributesInfo */ - $attributesInfo = Mage::getResourceModel($entityType->getEntityAttributeCollection()); - $attributesInfo->setEntityTypeFilter($entityType); - $attributesInfoData = $attributesInfo->getData(); - - $codes = []; - foreach ($attributesInfoData as $attribute) { - $this->_createAttribute($entityType, $attribute); - $codes[] = $attribute['attribute_code']; + if (isset($this->_defaultAttributes[$storeId][$entityType->getId()][$attributeCode])) { + return $this->_defaultAttributes[$storeId][$entityType->getId()][$attributeCode]; + } + + $entity = $entityType->getEntity(); + if ($entity && method_exists($entity, 'getDefaultAttributes')) { + if (in_array($attributeCode, $entity->getDefaultAttributes())) { + $attributeData = [ + 'entity_type_id' => $entityType->getId(), + 'attribute_code' => $attributeCode, + ]; + $attribute = $this->_hydrateAttribute($attributeData); + $this->_defaultAttributes[$storeId][$entityType->getId()][$attributeCode] = $attribute; + return $attribute; + } } - $entityType->setAttributeCodes($codes); - $this->_initializedAttributes[$entityTypeCode] = true; - - Varien_Profiler::stop('EAV: ' . __METHOD__); - return $this; + $this->_defaultAttributes[$storeId][$entityType->getId()][$attributeCode] = false; + return false; } /** * Get attribute by code for entity type * - * @param mixed $entityType - * @param mixed $code + * @param mixed $entityType + * @param mixed $code + * @param int|null $storeId * @return Mage_Eav_Model_Entity_Attribute_Abstract|false */ - public function getAttribute($entityType, $code) + public function getAttribute($entityType, $code, $storeId = null) { if ($code instanceof Mage_Eav_Model_Entity_Attribute_Interface) { return $code; @@ -383,289 +408,131 @@ public function getAttribute($entityType, $code) Varien_Profiler::start('EAV: ' . __METHOD__); - $entityTypeCode = $this->getEntityType($entityType)->getEntityTypeCode(); - $entityType = $this->getEntityType($entityType); + $storeId = $storeId !== null ? $storeId : Mage::app()->getStore()->getId(); + $this->_initializeStore($storeId); - /** - * Validate attribute code - */ - if (is_numeric($code)) { - $attributeCode = $this->_getAttributeReference($code, $entityTypeCode); - if ($attributeCode) { - $code = $attributeCode; - } - } - $attributeKey = $this->_getAttributeKey($entityTypeCode, $code); + $entityType = $this->getEntityType($entityType); - /** - * Try use loaded attribute - */ - $attribute = $this->_load($attributeKey); - if ($attribute) { - Varien_Profiler::stop('EAV: ' . __METHOD__); - return $attribute; + // lookup id by code + if (!is_numeric($code) && isset($this->_entityTypeAttributeIdByCode[$storeId][$entityType->getId()][$code])) { + $code = $this->_entityTypeAttributeIdByCode[$storeId][$entityType->getId()][$code]; } - if (isset($this->_attributeData[$entityTypeCode][$code])) { - $data = $this->_attributeData[$entityTypeCode][$code]; - unset($this->_attributeData[$entityTypeCode][$code]); - $attribute = Mage::getModel($data['attribute_model'], $data); - } else { - if (is_numeric($code)) { - /** @var Mage_Eav_Model_Entity_Attribute_Abstract $attribute */ - $attribute = Mage::getModel($entityType->getAttributeModel())->load($code); - if ($attribute->getEntityTypeId() != $entityType->getId()) { - return false; - } - $attributeKey = $this->_getAttributeKey($entityTypeCode, $attribute->getAttributeCode()); + // get model + $attribute = false; + if (isset($this->_entityTypeAttributes[$storeId][$entityType->getId()][$code])) { + $attributeData = $this->_entityTypeAttributes[$storeId][$entityType->getId()][$code]; + if (is_array($attributeData)) { + $attribute = $this->_hydrateAttribute($attributeData); + $this->_entityTypeAttributes[$storeId][$entityType->getId()][$attribute->getId()] = $attribute; } else { - $attribute = Mage::getModel($entityType->getAttributeModel()) - ->loadByCode($entityType, $code) - ->setAttributeCode($code); + $attribute = $attributeData; } + } else { + $attribute = $this->_getDefaultAttributeIfExists($entityType, $code, $storeId); } - if ($attribute) { - $entity = $entityType->getEntity(); - if ($entity && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes())) { - $attribute->setBackendType(Mage_Eav_Model_Entity_Attribute_Abstract::TYPE_STATIC) - ->setIsGlobal(1); - } - $attribute->setEntityType($entityType) - ->setEntityTypeId($entityType->getId()); - $this->_addAttributeReference($attribute->getId(), $attribute->getAttributeCode(), $entityTypeCode); - $this->_save($attribute, $attributeKey); - } Varien_Profiler::stop('EAV: ' . __METHOD__); return $attribute; } + /** + * @param mixed $entityType + * @param mixed $code + * @return Mage_Eav_Model_Entity_Attribute_Abstract[] + */ + public function getAttributes($entityType) + { + Varien_Profiler::start('EAV: ' . __METHOD__); + + $entityType = $this->getEntityType($entityType); + $attributes = []; + $storeId = Mage::app()->getStore()->getId(); + // need to access attributes to ensure they are hydrated and initialized + foreach (array_keys($this->_entityTypeAttributes[$storeId][$entityType->getId()]) as $attributeId) { + $attributes[] = $this->getAttribute($entityType, $attributeId); + } + return $attributes; + } + /** * Get codes of all entity type attributes * - * @param Mage_Eav_Model_Entity_Type $entityType - * @param Varien_Object $object + * @param Mage_Eav_Model_Entity_Type $entityType + * @param Varien_Object $object * @return array */ public function getEntityAttributeCodes($entityType, $object = null) { - $entityType = $this->getEntityType($entityType); + $entityType = $this->getEntityType($entityType); $attributeSetId = 0; if (($object instanceof Varien_Object) && $object->getAttributeSetId()) { $attributeSetId = $object->getAttributeSetId(); } + + // technically store id is irrelevant for attribute sets, they are the same in all store scopes $storeId = 0; if (($object instanceof Varien_Object) && $object->getStoreId()) { $storeId = $object->getStoreId(); } - $cacheKey = sprintf('%d-%d', $entityType->getId(), $attributeSetId); - if (isset($this->_attributeCodes[$cacheKey])) { - return $this->_attributeCodes[$cacheKey]; - } + $this->_initializeStore($storeId); if ($attributeSetId) { - /** @var Mage_Eav_Model_Resource_Entity_Attribute_Collection $attributesInfo */ - $attributesInfo = Mage::getResourceModel($entityType->getEntityAttributeCollection()); - $attributesInfo - ->setEntityTypeFilter($entityType) - ->setAttributeSetFilter($attributeSetId) - ->addStoreLabel($storeId); - $attributesInfoData = $attributesInfo->getData(); - $attributes = []; - foreach ($attributesInfoData as $attributeData) { - $attributes[] = $attributeData['attribute_code']; - $this->_createAttribute($entityType, $attributeData); + $attributeIds = $this->getAttributeSetAttributeIds($attributeSetId); + $attributeCodes = []; + foreach ($attributeIds as $attributeId) { + $attributeCodes[] = $this->getAttribute($entityType, $attributeId, $storeId)->getAttributeCode(); } + return $attributeCodes; } else { - $this->_initAttributes($entityType); - $attributes = $this->getEntityType($entityType)->getAttributeCodes(); + return array_keys($this->_entityTypeAttributeIdByCode[$entityType->getId()]); } - - $this->_attributeCodes[$cacheKey] = $attributes; - - return $attributes; } /** - * Preload entity type attributes for performance optimization - * - * @param mixed $entityType - * @param mixed $attributes - * @return Mage_Eav_Model_Config + * @param int $attributeSetId + * @return int[] + * @throws Mage_Core_Model_Store_Exception */ - public function preloadAttributes($entityType, $attributes) + public function getAttributeSetAttributeIds($attributeSetId) { - if (is_string($attributes)) { - $attributes = [$attributes]; - } - - $entityType = $this->getEntityType($entityType); - $entityTypeCode = $entityType->getEntityTypeCode(); - - if (!isset($this->_preloadedAttributes[$entityTypeCode])) { - $this->_preloadedAttributes[$entityTypeCode] = $attributes; - } else { - $attributes = array_diff($attributes, $this->_preloadedAttributes[$entityTypeCode]); - $this->_preloadedAttributes[$entityTypeCode] = array_merge( - $this->_preloadedAttributes[$entityTypeCode], - $attributes - ); - } - - if (empty($attributes)) { - return $this; - } - Varien_Profiler::start('EAV: ' . __METHOD__ . ':' . $entityTypeCode); - - /** @var Mage_Eav_Model_Resource_Entity_Attribute_Collection $attributesInfo */ - $attributesInfo = Mage::getResourceModel($entityType->getEntityAttributeCollection()); - $attributesInfo - ->setEntityTypeFilter($entityType) - ->setCodeFilter($attributes); - $attributesInfoData = $attributesInfo->getData(); - - if (!$attributesInfoData) { - Varien_Profiler::stop('EAV: ' . __METHOD__ . ':' . $entityTypeCode); - return $this; - } - - $attributesData = $codes = []; - - foreach ($attributesInfoData as $attribute) { - if (empty($attribute['attribute_model'])) { - $attribute['attribute_model'] = $entityType->getAttributeModel(); + $attributes = []; + foreach ($this->_attributeSetInfo as $attributeId => $sets) { + if (isset($sets[$attributeSetId])) { + $attributes[] = $attributeId; } - - $attributeCode = $attribute['attribute_code']; - $attributeId = $attribute['attribute_id']; - - $this->_addAttributeReference($attributeId, $attributeCode, $entityTypeCode); - $attributesData[$attributeCode] = $attribute; - $codes[] = $attributeCode; } - $this->_attributeData[$entityTypeCode] = $attributesData; - Varien_Profiler::stop('EAV: ' . __METHOD__ . ':' . $entityTypeCode); - return $this; + return $attributes; } /** - * Get attribute object for colection usage - * - * @param mixed $entityType - * @param string $attribute + * @param mixed $entityType + * @param string $attribute * @return Mage_Eav_Model_Entity_Attribute_Abstract|null + * @deprecated Equivalent to getAttribute(...), use getAttribute(...) instead + * Get attribute object for collection usage + * */ public function getCollectionAttribute($entityType, $attribute) { - $entityType = $this->getEntityType($entityType); - $entityTypeCode = $entityType->getEntityTypeCode(); - - if (is_numeric($attribute)) { - $attribute = $this->_getAttributeReference($attribute, $entityTypeCode); - if (!$attribute) { - return null; - } - } - - $attributeKey = $this->_getAttributeKey($entityTypeCode, $attribute); - $attributeObject = $this->_load($attributeKey); - if ($attributeObject) { - return $attributeObject; - } - return $this->getAttribute($entityType, $attribute); } /** + * @param mixed $entityType + * @param array $attributes + * @return Mage_Eav_Model_Config + * @deprecated No longer required to preload only collection attributes explicitly * Prepare attributes for usage in EAV collection * - * @param mixed $entityType - * @param array $attributes - * @return Mage_Eav_Model_Config */ public function loadCollectionAttributes($entityType, $attributes) { - $entityType = $this->getEntityType($entityType); - $entityTypeCode = $entityType->getEntityTypeCode(); - - if (!isset($this->_collectionAttributes[$entityTypeCode])) { - $this->_collectionAttributes[$entityTypeCode] = []; - } - $loadedAttributes = array_keys($this->_collectionAttributes[$entityTypeCode]); - $attributes = array_diff($attributes, $loadedAttributes); - - foreach ($attributes as $k => $attribute) { - if (is_numeric($attribute)) { - $attribute = $this->_getAttributeReference($attribute, $entityTypeCode); - } - $attributeKey = $this->_getAttributeKey($entityTypeCode, $attribute); - if ($this->_load($attributeKey)) { - unset($attributes[$k]); - } - } - - if (empty($attributes)) { - return $this; - } - $attributeCollection = $entityType->getEntityAttributeCollection(); - /** @var Mage_Eav_Model_Resource_Entity_Attribute_Collection $attributesInfo */ - $attributesInfo = Mage::getResourceModel($attributeCollection); - $attributesInfo - ->useLoadDataFields() - ->setEntityTypeFilter($entityType) - ->setCodeFilter($attributes); - $attributesInfoData = $attributesInfo->getData(); - - foreach ($attributesInfoData as $attributeData) { - $attribute = $this->_createAttribute($entityType, $attributeData); - $this->_collectionAttributes[$entityTypeCode][$attribute->getAttributeCode()] = $attribute; - } - return $this; } - /** - * Create attribute from attribute data array - * - * @param string $entityType - * @param array $attributeData - * @return false|Mage_Core_Model_Abstract - */ - protected function _createAttribute($entityType, $attributeData) - { - $entityType = $this->getEntityType($entityType); - $entityTypeCode = $entityType->getEntityTypeCode(); - - $attributeKey = $this->_getAttributeKey($entityTypeCode, $attributeData['attribute_code']); - $attribute = $this->_load($attributeKey); - if ($attribute) { - $existsFullAttribute = $attribute->hasIsRequired(); - $fullAttributeData = array_key_exists('is_required', $attributeData); - - if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) { - return $attribute; - } - } - - if (!empty($attributeData['attribute_model'])) { - $model = $attributeData['attribute_model']; - } else { - $model = $entityType->getAttributeModel(); - } - $attribute = Mage::getModel($model)->setData($attributeData); - $this->_addAttributeReference( - $attributeData['attribute_id'], - $attributeData['attribute_code'], - $entityTypeCode - ); - $attributeKey = $this->_getAttributeKey($entityTypeCode, $attributeData['attribute_code']); - $this->_save($attribute, $attributeKey); - - return $attribute; - } - /** * Validate attribute data from import * @@ -706,7 +573,6 @@ public function importAttributesData($entityType, array $attributes) if (!$this->_validateAttributeData($attributeData)) { continue; } - $this->_createAttribute($entityType, $attributeData); } return $this; diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php index 3604bf150e9..f5633280937 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php @@ -133,18 +133,23 @@ protected function _construct() public function loadByCode($entityType, $code) { Varien_Profiler::start('_LOAD_ATTRIBUTE_BY_CODE__'); - if (is_numeric($entityType)) { - $entityTypeId = $entityType; - } elseif (is_string($entityType)) { - $entityType = Mage::getModel('eav/entity_type')->loadByCode($entityType); - } - if ($entityType instanceof Mage_Eav_Model_Entity_Type) { - $entityTypeId = $entityType->getId(); - } - if (empty($entityTypeId)) { - throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid entity supplied.')); + $model = Mage::getSingleton('eav/config')->getAttribute($entityType, $code); + if($model) { + $this->setData($model->getData()); + } else { + if (is_numeric($entityType)) { + $entityTypeId = $entityType; + } elseif (is_string($entityType)) { + $entityType = Mage::getSingleton('eav/config')->getEntityType($entityType); + } + if ($entityType instanceof Mage_Eav_Model_Entity_Type) { + $entityTypeId = $entityType->getId(); + } + if (empty($entityTypeId)) { + throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid entity supplied.')); + } + $this->_getResource()->loadByCode($this, $entityTypeId, $code); } - $this->_getResource()->loadByCode($this, $entityTypeId, $code); $this->_afterLoad(); Varien_Profiler::stop('_LOAD_ATTRIBUTE_BY_CODE__'); return $this; diff --git a/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php index 94bee878a77..c6ff2e91992 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php @@ -417,7 +417,6 @@ protected function _prepareOrderExpression($field) public function addAttributeToSelect($attribute, $joinType = false) { if (is_array($attribute)) { - Mage::getSingleton('eav/config')->loadCollectionAttributes($this->getEntity()->getType(), $attribute); foreach ($attribute as $a) { $this->addAttributeToSelect($a, $joinType); } @@ -438,7 +437,7 @@ public function addAttributeToSelect($attribute, $joinType = false) $attrInstance = $this->_joinAttributes[$attribute]['attribute']; } else { $attrInstance = Mage::getSingleton('eav/config') - ->getCollectionAttribute($this->getEntity()->getType(), $attribute); + ->getAttribute($this->getEntity()->getType(), $attribute); } if (empty($attrInstance)) { throw Mage::exception( @@ -1098,7 +1097,7 @@ public function _loadAttributes($printQuery = false, $logQuery = false) if (!$attributeId) { continue; } - $attribute = Mage::getSingleton('eav/config')->getCollectionAttribute($entity->getType(), $attributeCode); + $attribute = Mage::getSingleton('eav/config')->getAttribute($entity->getType(), $attributeCode); if ($attribute && !$attribute->isStatic()) { $tableAttributes[$attribute->getBackendTable()][] = $attributeId; if (!isset($attributeTypes[$attribute->getBackendTable()])) { @@ -1205,7 +1204,7 @@ protected function _setItemAttributeValue($valueInfo) } $attributeCode = array_search($valueInfo['attribute_id'], $this->_selectAttributes); if (!$attributeCode) { - $attribute = Mage::getSingleton('eav/config')->getCollectionAttribute( + $attribute = Mage::getSingleton('eav/config')->getAttribute( $this->getEntity()->getType(), $valueInfo['attribute_id'] ); diff --git a/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php b/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php index 4dce913fdd8..56e5cac9b7e 100644 --- a/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php +++ b/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php @@ -96,46 +96,50 @@ public function validate($object, $attributeSetName) * Retrieve Set info by attributes * * @param array $attributeIds - * @param int $setId + * @param int|null $setId * @return array */ - public function getSetInfo(array $attributeIds, $setId = null) + public function getSetInfo(array $attributeIds = [], $setId = null) { $adapter = $this->_getReadAdapter(); $setInfo = []; $attributeToSetInfo = []; + $select = $adapter->select() + ->from( + ['entity' => $this->getTable('eav/entity_attribute')], + ['attribute_id', 'attribute_set_id', 'attribute_group_id', 'sort_order'] + ) + ->joinLeft( + ['attribute_group' => $this->getTable('eav/attribute_group')], + 'entity.attribute_group_id = attribute_group.attribute_group_id', + ['group_sort_order' => 'sort_order'] + ); if (count($attributeIds) > 0) { - $select = $adapter->select() - ->from( - ['entity' => $this->getTable('eav/entity_attribute')], - ['attribute_id', 'attribute_set_id', 'attribute_group_id', 'sort_order'] - ) - ->joinLeft( - ['attribute_group' => $this->getTable('eav/attribute_group')], - 'entity.attribute_group_id = attribute_group.attribute_group_id', - ['group_sort_order' => 'sort_order'] - ) - ->where('entity.attribute_id IN (?)', $attributeIds); - $bind = []; - if (is_numeric($setId)) { - $bind[':attribute_set_id'] = $setId; - $select->where('entity.attribute_set_id = :attribute_set_id'); - } - $result = $adapter->fetchAll($select, $bind); + $select->where('entity.attribute_id IN (?)', $attributeIds); + } + $bind = []; + if (is_numeric($setId)) { + $bind[':attribute_set_id'] = $setId; + $select->where('entity.attribute_set_id = :attribute_set_id'); + } + $result = $adapter->fetchAll($select, $bind); - foreach ($result as $row) { - $data = [ - 'group_id' => $row['attribute_group_id'], - 'group_sort' => $row['group_sort_order'], - 'sort' => $row['sort_order'] - ]; - $attributeToSetInfo[$row['attribute_id']][$row['attribute_set_id']] = $data; - } + foreach ($result as $row) { + $data = [ + 'group_id' => $row['attribute_group_id'], + 'group_sort' => $row['group_sort_order'], + 'sort' => $row['sort_order'] + ]; + $attributeToSetInfo[$row['attribute_id']][$row['attribute_set_id']] = $data; } - foreach ($attributeIds as $atttibuteId) { - $setInfo[$atttibuteId] = $attributeToSetInfo[$atttibuteId] ?? []; + if(count($attributeIds)) { + foreach ($attributeIds as $atttibuteId) { + $setInfo[$atttibuteId] = $attributeToSetInfo[$atttibuteId] ?? []; + } + } else { + return $attributeToSetInfo; } return $setInfo; diff --git a/app/code/core/Mage/Sales/Model/Resource/Order/Abstract.php b/app/code/core/Mage/Sales/Model/Resource/Order/Abstract.php index 6836603f48c..e4ced9b47ab 100644 --- a/app/code/core/Mage/Sales/Model/Resource/Order/Abstract.php +++ b/app/code/core/Mage/Sales/Model/Resource/Order/Abstract.php @@ -363,7 +363,7 @@ protected function _beforeSave(Mage_Core_Model_Abstract $object) { if ($this->_useIncrementId && !$object->getIncrementId()) { /** @var Mage_Eav_Model_Entity_Type $entityType */ - $entityType = Mage::getModel('eav/entity_type')->loadByCode($this->_entityTypeForIncrementId); + $entityType = Mage::getSingleton('eav/config')->getEntityType($this->_entityTypeForIncrementId); $object->setIncrementId($entityType->fetchNewIncrementId($object->getStoreId())); } parent::_beforeSave($object); diff --git a/app/code/core/Mage/Tax/Helper/Data.php b/app/code/core/Mage/Tax/Helper/Data.php index 308dced7f9e..b686152e4b4 100644 --- a/app/code/core/Mage/Tax/Helper/Data.php +++ b/app/code/core/Mage/Tax/Helper/Data.php @@ -877,8 +877,8 @@ public function getPriceTaxSql($priceField, $taxClassField) */ public function joinTaxClass($select, $storeId, $priceTable = 'main_table') { - $taxClassAttribute = Mage::getModel('eav/entity_attribute') - ->loadByCode(Mage_Catalog_Model_Product::ENTITY, 'tax_class_id'); + $taxClassAttribute = Mage::getSingleton('eav/config') + ->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'tax_class_id'); $joinConditionD = implode(' AND ', [ "tax_class_d.entity_id = {$priceTable}.entity_id", $select->getAdapter()->quoteInto('tax_class_d.attribute_id = ?', (int)$taxClassAttribute->getId()), From 11383b7caccef3295bbf4212cd7c7273e0809034 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Tue, 24 Jan 2023 04:02:01 +0100 Subject: [PATCH 02/25] Add @DavidHiendl as a contributor --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 66437446208..ffbfe161086 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1373,6 +1373,15 @@ "contributions": [ "code" ] + }, + { + "login": "davidhiendl", + "name": "David Hiendl", + "avatar_url": "https://avatars.githubusercontent.com/u/11006964?v=4", + "profile": "https://gitlab.com/davidhiendl", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7 diff --git a/README.md b/README.md index b01c34621aa..3083556d2ec 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

-All Contributors +All Contributors Total Downloads License @@ -505,6 +505,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Scott Moore

Roger Feese

Alexander Gelzer
+
David Hiendl
From 8cafff3ee8ef981dff5b06738110b6ed79680317 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Tue, 24 Jan 2023 05:00:39 +0100 Subject: [PATCH 03/25] fix format inconsistencies, fixed eav/config clear fields --- app/code/core/Mage/Catalog/Model/Layer.php | 14 +++++++------- app/code/core/Mage/Eav/Model/Config.php | 13 ++++++------- .../Mage/Eav/Model/Entity/Attribute/Abstract.php | 2 +- .../Eav/Model/Resource/Entity/Attribute/Set.php | 2 +- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/code/core/Mage/Catalog/Model/Layer.php b/app/code/core/Mage/Catalog/Model/Layer.php index 92dcfee1f13..acba5abd5a1 100644 --- a/app/code/core/Mage/Catalog/Model/Layer.php +++ b/app/code/core/Mage/Catalog/Model/Layer.php @@ -211,22 +211,22 @@ public function getFilterableAttributes() return []; } - $eavConfig=Mage::getSingleton('eav/config'); + $eavConfig = Mage::getSingleton('eav/config'); /** @var Mage_Catalog_Model_Resource_Eav_Attribute[] $attributes */ $attributes = []; - foreach($setIds as $setId) { + foreach ($setIds as $setId) { $setAttributeIds = $eavConfig->getAttributeSetAttributeIds($setId); - foreach($setAttributeIds as $attributeId) { - if(!isset($attributes[$attributeId])) { + foreach ($setAttributeIds as $attributeId) { + if (!isset($attributes[$attributeId])) { $attribute = $eavConfig->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeId); - if($attribute->getIsFilterable()) { + if ($attribute->getIsFilterable()) { $attributes[$attributeId] = $attribute; } } } } - usort($attributes, function ($a,$b) { - return $a->getPosition() - $b->getPosition(); + usort($attributes, function ($a, $b) { + return $a->getPosition() - $b->getPosition(); }); return $attributes; diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index b888de83eb2..9a1cfaf3e4c 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -91,10 +91,12 @@ class Mage_Eav_Model_Config */ public function clear() { + $this->_storeInitialized = []; $this->_entityTypes = null; $this->_entityTypeAttributes = null; $this->_entityTypeAttributeIdByCode = null; - $this->_storeInitialized = []; + $this->_attributeSetInfo = null; + $this->_defaultAttributes = []; Mage::app()->cleanCache([self::ENTITIES_CACHE_ID]); // Mage::app()->removeCache(self::ENTITIES_CACHE_ID . "_" . Mage::app()->getStore()->getId()); @@ -102,13 +104,8 @@ public function clear() return $this; } - public function __construct() - { - } - protected function _initializeStore($storeId = null) { - if ($storeId === null) { $storeId = Mage::app()->getStore()->getId(); } else { @@ -348,7 +345,9 @@ public function getEntityType($code, $field = null) } } - if (empty($field)) $field = 'entity_type_code'; + if (empty($field)) { + $field = 'entity_type_code'; + } foreach ($this->_entityTypes as $entityType) { if ($entityType->getData($field) == $code) { diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php index f5633280937..82ded4a67c1 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php @@ -134,7 +134,7 @@ public function loadByCode($entityType, $code) { Varien_Profiler::start('_LOAD_ATTRIBUTE_BY_CODE__'); $model = Mage::getSingleton('eav/config')->getAttribute($entityType, $code); - if($model) { + if ($model) { $this->setData($model->getData()); } else { if (is_numeric($entityType)) { diff --git a/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php b/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php index 56e5cac9b7e..e22a4768312 100644 --- a/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php +++ b/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Set.php @@ -134,7 +134,7 @@ public function getSetInfo(array $attributeIds = [], $setId = null) $attributeToSetInfo[$row['attribute_id']][$row['attribute_set_id']] = $data; } - if(count($attributeIds)) { + if (count($attributeIds)) { foreach ($attributeIds as $atttibuteId) { $setInfo[$atttibuteId] = $attributeToSetInfo[$atttibuteId] ?? []; } From c22d33345ac23ea13a938bcf3f008a5d164f6689 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Tue, 24 Jan 2023 16:31:16 +0100 Subject: [PATCH 04/25] fix fallback for getEntityAttributeCodes missing store_id in lookup --- app/code/core/Mage/Eav/Model/Config.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 9a1cfaf3e4c..6b68009e394 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -470,8 +470,9 @@ public function getEntityAttributeCodes($entityType, $object = null) $attributeSetId = $object->getAttributeSetId(); } - // technically store id is irrelevant for attribute sets, they are the same in all store scopes - $storeId = 0; + // Technically store id is irrelevant for attribute sets, they are the same in all store scopes. + // Use current store id when not specified to avoid loading two store-scope attribute data sets from cache + $storeId = Mage::app()->getStore()->getStoreId(); if (($object instanceof Varien_Object) && $object->getStoreId()) { $storeId = $object->getStoreId(); } @@ -485,7 +486,7 @@ public function getEntityAttributeCodes($entityType, $object = null) } return $attributeCodes; } else { - return array_keys($this->_entityTypeAttributeIdByCode[$entityType->getId()]); + return array_keys($this->_entityTypeAttributeIdByCode[$storeId][$entityType->getId()]); } } From 46a7c62a443826ab60650a3ac229662b7f7b9370 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Wed, 25 Jan 2023 16:58:26 +0100 Subject: [PATCH 05/25] patch potential error with leftover option data for deleted attributes --- app/code/core/Mage/Eav/Model/Config.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 6b68009e394..10d4805ed2b 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -482,7 +482,11 @@ public function getEntityAttributeCodes($entityType, $object = null) $attributeIds = $this->getAttributeSetAttributeIds($attributeSetId); $attributeCodes = []; foreach ($attributeIds as $attributeId) { - $attributeCodes[] = $this->getAttribute($entityType, $attributeId, $storeId)->getAttributeCode(); + $attribute = $this->getAttribute($entityType, $attributeId, $storeId); + // need to verify attribute actually exists to avoid problems with deleted attributes that left behind some remnants + if ($attribute) { + $attributeCodes[] = $attribute->getAttributeCode(); + } } return $attributeCodes; } else { From 185fbf99cc1f30e0119d1434374b7e471a9138aa Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Wed, 25 Jan 2023 16:59:24 +0100 Subject: [PATCH 06/25] optimzied/fixed calls to Varien_Profiler --- app/code/core/Mage/Eav/Model/Config.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 10d4805ed2b..4a0cf810824 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -152,8 +152,6 @@ protected function _initializeStore($storeId = null) */ protected function _loadEntityTypes() { - Varien_Profiler::start('EAV: ' . __METHOD__); - // load entity types $this->_entityTypes = []; $entityTypeCollection = Mage::getResourceModel('eav/entity_type_collection'); @@ -164,8 +162,6 @@ protected function _loadEntityTypes() // } $this->_entityTypes[$entityType->getId()] = $entityType; } - - Varien_Profiler::stop('EAV: ' . __METHOD__); } /** @@ -203,11 +199,7 @@ protected function _loadEntityAttributes($entityType, $storeId) */ protected function _loadAttributeSetInfo() { - Varien_Profiler::start('EAV: ' . __METHOD__); - $this->_attributeSetInfo = Mage::getResourceModel('eav/entity_attribute_set')->getSetInfo(); - - Varien_Profiler::stop('EAV: ' . __METHOD__); } protected function _loadFromCache($storeId) @@ -216,6 +208,7 @@ protected function _loadFromCache($storeId) $cacheData = Mage::app()->loadCache(self::ENTITIES_CACHE_ID . "_" . $storeId); if ($cacheData === false) { + Varien_Profiler::stop('EAV: ' . __METHOD__); return; } $cacheData = unserialize($cacheData); @@ -246,8 +239,6 @@ protected function _loadFromCache($storeId) protected function _saveToCache($storeId) { - Varien_Profiler::start('EAV: ' . __METHOD__); - $cacheData = [ '_entityTypes' => [], '_entityTypeAttributes' => [], @@ -272,8 +263,6 @@ protected function _saveToCache($storeId) self::ENTITIES_CACHE_ID . "_" . $storeId, ['eav', self::ENTITIES_CACHE_ID, Mage_Eav_Model_Entity_Attribute::CACHE_TAG] ); - - Varien_Profiler::stop('EAV: ' . __METHOD__); } /** @@ -452,6 +441,9 @@ public function getAttributes($entityType) foreach (array_keys($this->_entityTypeAttributes[$storeId][$entityType->getId()]) as $attributeId) { $attributes[] = $this->getAttribute($entityType, $attributeId); } + + Varien_Profiler::stop('EAV: ' . __METHOD__); + return $attributes; } From 50ee31da8723360d457a394e46f4eae02e978fdd Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Wed, 25 Jan 2023 17:02:25 +0100 Subject: [PATCH 07/25] improved phpdoc, removed redundant checks --- app/code/core/Mage/Eav/Model/Config.php | 29 ++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 4a0cf810824..3d25a3cdbcb 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -46,7 +46,7 @@ class Mage_Eav_Model_Config protected $_entityTypes; /** - * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][]|null + * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][]|mixed[][][]|null */ protected $_entityTypeAttributes; @@ -66,13 +66,13 @@ class Mage_Eav_Model_Config * int sort, * ] * ] - * @var [][][][]mixed + * @var mixed[][][][]|null */ protected $_attributeSetInfo; /** * Special local cache for default attributes to avoid re-hydrating them - * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][] + * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][]|false[][][] */ protected $_defaultAttributes = []; @@ -155,7 +155,7 @@ protected function _loadEntityTypes() // load entity types $this->_entityTypes = []; $entityTypeCollection = Mage::getResourceModel('eav/entity_type_collection'); - /** @var $entityType Mage_Eav_Model_Entity_Type */ + /** @var Mage_Eav_Model_Entity_Type $entityType */ foreach ($entityTypeCollection as $entityType) { // if (empty($entityType->getAttributeModel())) { // $entityType->setAttributeModel('eav/entity_attribute'); @@ -282,7 +282,7 @@ protected function _isCacheEnabled() * Create model instance from array * * @param array $attributeData - * @return Mage_Eav_Model_Entity_Attribute_Abstract + * @return Mage_Eav_Model_Entity_Attribute_Abstract|false */ protected function _hydrateAttribute($attributeData) { @@ -292,12 +292,13 @@ protected function _hydrateAttribute($attributeData) } else { $model = $entityType->getAttributeModel(); } - $attribute = Mage::getModel($model)->setData($attributeData); + /** @var Mage_Eav_Model_Entity_Attribute_Abstract|false $attribute */ + $attribute = Mage::getModel($model); if ($attribute) { $attribute->setData($attributeData); $entity = $entityType->getEntity(); - if ($entity && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes())) { + if (in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes())) { $attribute ->setBackendType(Mage_Eav_Model_Entity_Attribute_Abstract::TYPE_STATIC) ->setIsGlobal(1); @@ -307,7 +308,6 @@ protected function _hydrateAttribute($attributeData) ->setEntityTypeId($entityType->getId()); } - /** @noinspection PhpIncompatibleReturnTypeInspection */ return $attribute; } @@ -316,7 +316,8 @@ protected function _hydrateAttribute($attributeData) * * @param mixed $code * @param string|null $code - * @return Mage_Eav_Model_Entity_Type + * @return Mage_Eav_Model_Entity_Type + * @throws Mage_Core_Exception */ public function getEntityType($code, $field = null) { @@ -344,7 +345,7 @@ public function getEntityType($code, $field = null) } } - return null; + Mage::throwException("Failed to find entity eav/entity_type for $field=$code"); } /** @@ -355,7 +356,6 @@ public function getEntityType($code, $field = null) * @param int $storeId * @param string $attributeCode * @return Mage_Eav_Model_Entity_Attribute_Abstract|false - * @throws Exception */ protected function _getDefaultAttributeIfExists($entityType, $attributeCode, $storeId) { @@ -364,7 +364,7 @@ protected function _getDefaultAttributeIfExists($entityType, $attributeCode, $st } $entity = $entityType->getEntity(); - if ($entity && method_exists($entity, 'getDefaultAttributes')) { + if (method_exists($entity, 'getDefaultAttributes')) { if (in_array($attributeCode, $entity->getDefaultAttributes())) { $attributeData = [ 'entity_type_id' => $entityType->getId(), @@ -386,7 +386,7 @@ protected function _getDefaultAttributeIfExists($entityType, $attributeCode, $st * @param mixed $entityType * @param mixed $code * @param int|null $storeId - * @return Mage_Eav_Model_Entity_Attribute_Abstract|false + * @return Mage_Eav_Model_Entity_Attribute_Abstract|false */ public function getAttribute($entityType, $code, $storeId = null) { @@ -427,8 +427,7 @@ public function getAttribute($entityType, $code, $storeId = null) /** * @param mixed $entityType - * @param mixed $code - * @return Mage_Eav_Model_Entity_Attribute_Abstract[] + * @return Mage_Eav_Model_Entity_Attribute_Abstract[] */ public function getAttributes($entityType) { From 68527f616feab739561b1ed9bb4fcf8f7fb0a632 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Wed, 25 Jan 2023 17:06:37 +0100 Subject: [PATCH 08/25] fixed contributor profile link --- .all-contributorsrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index ffbfe161086..e2ce8b9ae93 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1378,7 +1378,7 @@ "login": "davidhiendl", "name": "David Hiendl", "avatar_url": "https://avatars.githubusercontent.com/u/11006964?v=4", - "profile": "https://gitlab.com/davidhiendl", + "profile": "https://github.com/davidhiendl", "contributions": [ "code" ] From 0cd0aa2bd16adfaa426d0c08c9d4874152a7b6f1 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 26 Jan 2023 12:50:30 +0100 Subject: [PATCH 09/25] fix for eav/config->getAttribute no longer returning an empty model for non-existent attributes --- app/code/core/Mage/Eav/Model/Entity/Abstract.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/core/Mage/Eav/Model/Entity/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Abstract.php index 463fca82918..84dda689dd1 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Abstract.php @@ -372,7 +372,7 @@ public function getAttribute($attribute) } $attributeInstance = Mage::getSingleton('eav/config') ->getAttribute($this->getEntityType(), $attributeCode); - if (!$attributeInstance->getAttributeCode() && in_array($attribute, $this->getDefaultAttributes())) { + if ($attributeInstance && !$attributeInstance->getAttributeCode() && in_array($attribute, $this->getDefaultAttributes())) { $attributeInstance ->setAttributeCode($attribute) ->setBackendType(Mage_Eav_Model_Entity_Attribute_Abstract::TYPE_STATIC) From 973b44721c0451b9d90003dfae5401879bf6f742 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 26 Jan 2023 12:55:18 +0100 Subject: [PATCH 10/25] deprecated importAttributesData, as it no longer has any purpose (all attributes loaded at all times), rewrite product compare to use eav/config --- .../Catalog/Block/Product/Compare/List.php | 2 +- app/code/core/Mage/Catalog/Model/Layer.php | 2 +- .../Product/Compare/Item/Collection.php | 41 +++++---------- .../Model/Resource/Product/Flat/Indexer.php | 6 ++- app/code/core/Mage/Eav/Model/Config.php | 52 +++++-------------- 5 files changed, 31 insertions(+), 72 deletions(-) diff --git a/app/code/core/Mage/Catalog/Block/Product/Compare/List.php b/app/code/core/Mage/Catalog/Block/Product/Compare/List.php index e8ba5497160..ea9d75576b3 100644 --- a/app/code/core/Mage/Catalog/Block/Product/Compare/List.php +++ b/app/code/core/Mage/Catalog/Block/Product/Compare/List.php @@ -126,7 +126,7 @@ public function getItems() /** * Retrieve Product Compare Attributes * - * @return array + * @return Mage_Eav_Model_Entity_Attribute_Abstract[] */ public function getAttributes() { diff --git a/app/code/core/Mage/Catalog/Model/Layer.php b/app/code/core/Mage/Catalog/Model/Layer.php index acba5abd5a1..a553cb6e987 100644 --- a/app/code/core/Mage/Catalog/Model/Layer.php +++ b/app/code/core/Mage/Catalog/Model/Layer.php @@ -202,7 +202,7 @@ public function getCurrentStore() /** * Get collection of all filterable attributes for layer products set * - * @return array + * @return Mage_Catalog_Model_Resource_Eav_Attribute[] */ public function getFilterableAttributes() { diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Compare/Item/Collection.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Compare/Item/Collection.php index 7f6986918d7..212c2aa00d2 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Compare/Item/Collection.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Compare/Item/Collection.php @@ -45,7 +45,7 @@ class Mage_Catalog_Model_Resource_Product_Compare_Item_Collection extends Mage_C /** * Comparable attributes cache * - * @var array|null + * @var Mage_Eav_Model_Entity_Attribute_Abstract[]|null */ protected $_comparableAttributes; @@ -211,7 +211,7 @@ protected function _getAttributeIdsBySetIds(array $setIds) /** * Retrieve Merged comparable attributes for compared product items * - * @return array + * @return Mage_Eav_Model_Entity_Attribute_Abstract[] */ public function getComparableAttributes() { @@ -219,36 +219,19 @@ public function getComparableAttributes() $this->_comparableAttributes = []; $setIds = $this->_getAttributeSetIds(); if ($setIds) { - $select = $this->getConnection()->select() - ->from(['main_table' => $this->getTable('eav/attribute')]) - ->join( - ['additional_table' => $this->getTable('catalog/eav_attribute')], - 'additional_table.attribute_id=main_table.attribute_id' - ) - ->joinLeft( - ['al' => $this->getTable('eav/attribute_label')], - 'al.attribute_id = main_table.attribute_id AND al.store_id = ' . (int) $this->getStoreId(), - ['store_label' => new Zend_Db_Expr('IFNULL(al.value, main_table.frontend_label)')] - ) - ->joinLeft( - ['ai' => $this->getTable('eav/entity_attribute')], - 'ai.attribute_id = main_table.attribute_id' - ) - ->where('additional_table.is_comparable=?', 1) - ->where('ai.attribute_set_id IN(?)', $setIds) - ->order(['ai.attribute_group_id ASC', 'ai.sort_order ASC']); - $attributesData = $this->getConnection()->fetchAll($select); - if ($attributesData) { - $entityType = Mage_Catalog_Model_Product::ENTITY; - Mage::getSingleton('eav/config') - ->importAttributesData($entityType, $attributesData); - foreach ($attributesData as $data) { - $attribute = Mage::getSingleton('eav/config') - ->getAttribute($entityType, $data['attribute_code']); + $eavConfig = Mage::getSingleton('eav/config'); + $attributeIds = $eavConfig->getAttributeSetAttributeIds($setIds); + $this->_comparableAttributes = []; + foreach ($attributeIds as $attributeId) { + $attribute = $eavConfig->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeId); + if ($attribute->getData('is_comparable')) { $this->_comparableAttributes[$attribute->getAttributeCode()] = $attribute; } - unset($attributesData); } + + usort($this->_comparableAttributes, function ($a, $b) { + return $a->getPosition() - $b->getPosition(); + }); } } return $this->_comparableAttributes; diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php index 9e728440ecb..b33cdcf428e 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php @@ -184,8 +184,6 @@ public function getAttributeCodes() $select->where(implode(' OR ', $whereCondition)); $attributesData = $adapter->fetchAll($select, $bind); - Mage::getSingleton('eav/config') - ->importAttributesData($this->getEntityType(), $attributesData); foreach ($attributesData as $data) { $this->_attributeCodes[$data['attribute_id']] = $data['attribute_code']; @@ -274,6 +272,10 @@ public function getAttribute($attributeCode) ->getEntity(); $attribute->setEntity($entity); + if (!($attribute instanceof Mage_Eav_Model_Entity_Attribute)) { + Mage::throwException('Product attribute(code=". $attributeCode . ") is expected to be of type Mage_Eav_Model_Entity_Attribute'); + } + return $attribute; } diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 3d25a3cdbcb..2f43c70f53c 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -486,20 +486,27 @@ public function getEntityAttributeCodes($entityType, $object = null) } /** - * @param int $attributeSetId + * @param int|int[] $attributeSetId * @return int[] * @throws Mage_Core_Model_Store_Exception */ public function getAttributeSetAttributeIds($attributeSetId) { + if (!is_array($attributeSetId)) { + $attributeSetId = [$attributeSetId]; + } + $attributes = []; - foreach ($this->_attributeSetInfo as $attributeId => $sets) { - if (isset($sets[$attributeSetId])) { - $attributes[] = $attributeId; + + foreach ($attributeSetId as $setId) { + foreach ($this->_attributeSetInfo as $attributeId => $sets) { + if (isset($sets[$setId])) { + $attributes[$attributeId] = true; + } } } - return $attributes; + return array_keys($attributes); } /** @@ -529,33 +536,7 @@ public function loadCollectionAttributes($entityType, $attributes) } /** - * Validate attribute data from import - * - * @param array $attributeData - * @return bool - */ - protected function _validateAttributeData($attributeData = null) - { - if (!is_array($attributeData)) { - return false; - } - $requiredKeys = [ - 'attribute_id', - 'attribute_code', - 'entity_type_id', - 'attribute_model' - ]; - foreach ($requiredKeys as $key) { - if (!array_key_exists($key, $attributeData)) { - return false; - } - } - - return true; - } - - /** - * Import attributes data from external source + * @deprecated No longer required. All attribute data is cached on-access. * * @param string|Mage_Eav_Model_Entity_Type $entityType * @param array $attributes @@ -563,13 +544,6 @@ protected function _validateAttributeData($attributeData = null) */ public function importAttributesData($entityType, array $attributes) { - $entityType = $this->getEntityType($entityType); - foreach ($attributes as $attributeData) { - if (!$this->_validateAttributeData($attributeData)) { - continue; - } - } - return $this; } } From b0883ed899278dc6b4b011262fa0f651212d2a48 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 26 Jan 2023 17:02:13 +0100 Subject: [PATCH 11/25] fixed one more bug related to deleted attributes when there is leftover eav data --- app/code/core/Mage/Catalog/Model/Layer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/core/Mage/Catalog/Model/Layer.php b/app/code/core/Mage/Catalog/Model/Layer.php index a553cb6e987..26a10e26fd2 100644 --- a/app/code/core/Mage/Catalog/Model/Layer.php +++ b/app/code/core/Mage/Catalog/Model/Layer.php @@ -219,7 +219,7 @@ public function getFilterableAttributes() foreach ($setAttributeIds as $attributeId) { if (!isset($attributes[$attributeId])) { $attribute = $eavConfig->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeId); - if ($attribute->getIsFilterable()) { + if ($attribute && $attribute->getIsFilterable()) { $attributes[$attributeId] = $attribute; } } From fa31e6067d41ec80a725650d8f2fcad2b732eacf Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Fri, 27 Jan 2023 15:15:25 +0100 Subject: [PATCH 12/25] added map lookup for entity type by code, reduce calls to app()->getStore(), implemented possible cache workaround for getStore() calls for frontend, simplified some conditions --- app/code/core/Mage/Eav/Model/Config.php | 86 +++++++++++++++++------ app/code/core/Mage/Eav/Model/Observer.php | 48 +++++++++++++ app/code/core/Mage/Eav/etc/config.xml | 10 +++ 3 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 app/code/core/Mage/Eav/Model/Observer.php diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 2f43c70f53c..3d54e2a6f24 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -45,6 +45,11 @@ class Mage_Eav_Model_Config */ protected $_entityTypes; + /** + * @var Mage_Eav_Model_Entity_Type[]|null + */ + protected $_entityTypeByCode; + /** * @var Mage_Eav_Model_Entity_Attribute_Abstract[][][]|mixed[][][]|null */ @@ -83,6 +88,12 @@ class Mage_Eav_Model_Config */ protected $_isCacheEnabled = null; + /** + * @var int|false + */ + protected $_currentStoreId; + + /** * Reset object state * @@ -93,21 +104,39 @@ public function clear() { $this->_storeInitialized = []; $this->_entityTypes = null; + $this->_entityTypeByCode = null; $this->_entityTypeAttributes = null; $this->_entityTypeAttributeIdByCode = null; $this->_attributeSetInfo = null; $this->_defaultAttributes = []; Mage::app()->cleanCache([self::ENTITIES_CACHE_ID]); - // Mage::app()->removeCache(self::ENTITIES_CACHE_ID . "_" . Mage::app()->getStore()->getId()); + // Mage::app()->removeCache(self::ENTITIES_CACHE_ID . '_' . Mage::app()->getStore()->getId()); return $this; } + /** + * @param int|false|null $storeId + * @return void + */ + public function setCurrentStoreId($storeId) + { + $this->_currentStoreId = $storeId; + } + + protected function _storeId() + { + if (isset($this->_currentStoreId) && $this->_currentStoreId !== false) { + return $this->_currentStoreId; + } + return Mage::app()->getStore()->getId(); + } + protected function _initializeStore($storeId = null) { if ($storeId === null) { - $storeId = Mage::app()->getStore()->getId(); + $storeId = $this->_storeId(); } else { // ensure store id is consistent $storeId = (int)$storeId; @@ -154,6 +183,7 @@ protected function _loadEntityTypes() { // load entity types $this->_entityTypes = []; + $this->_entityTypeByCode = []; $entityTypeCollection = Mage::getResourceModel('eav/entity_type_collection'); /** @var Mage_Eav_Model_Entity_Type $entityType */ foreach ($entityTypeCollection as $entityType) { @@ -161,6 +191,7 @@ protected function _loadEntityTypes() // $entityType->setAttributeModel('eav/entity_attribute'); // } $this->_entityTypes[$entityType->getId()] = $entityType; + $this->_entityTypeByCode[$entityType->getEntityTypeCode()] = $entityType; } } @@ -206,7 +237,7 @@ protected function _loadFromCache($storeId) { Varien_Profiler::start('EAV: ' . __METHOD__); - $cacheData = Mage::app()->loadCache(self::ENTITIES_CACHE_ID . "_" . $storeId); + $cacheData = Mage::app()->loadCache(self::ENTITIES_CACHE_ID . '_' . $storeId); if ($cacheData === false) { Varien_Profiler::stop('EAV: ' . __METHOD__); return; @@ -214,15 +245,17 @@ protected function _loadFromCache($storeId) $cacheData = unserialize($cacheData); $this->_entityTypes = []; - /** @var $entityType Mage_Eav_Model_Entity_Type */ + $this->_entityTypeByCode = []; + /** @var Mage_Eav_Model_Entity_Type $entityType */ foreach ($cacheData['_entityTypes'] as $entityTypeData) { $entityType = Mage::getModel('eav/entity_type') ->setData($entityTypeData); $this->_entityTypes[$entityType->getId()] = $entityType; + $this->_entityTypeByCode[$entityType->getEntityTypeCode()] = $entityType; } $this->_entityTypeAttributes[$storeId] = []; - /** @var $entityType Mage_Eav_Model_Entity_Type */ + /** @var Mage_Eav_Model_Entity_Type $entityType */ foreach ($cacheData['_entityTypeAttributes'] as $entityTypeId => $entityTypeAttributes) { foreach ($entityTypeAttributes as $attributeData) { $attributeId = $attributeData['attribute_id']; @@ -260,7 +293,7 @@ protected function _saveToCache($storeId) Mage::app()->saveCache( serialize($cacheData), - self::ENTITIES_CACHE_ID . "_" . $storeId, + self::ENTITIES_CACHE_ID . '_' . $storeId, ['eav', self::ENTITIES_CACHE_ID, Mage_Eav_Model_Entity_Attribute::CACHE_TAG] ); } @@ -325,27 +358,39 @@ public function getEntityType($code, $field = null) return $code; } - $storeId = Mage::app()->getStore()->getId(); - $this->_initializeStore($storeId); + // initialize entity type cache + if (!isset($this->_entityTypes)) { + $this->_initializeStore(); + } + // lookup by id* if (empty($field) && is_numeric($code)) { $entity = $this->_entityTypes[$code]; if ($entity !== null) { return $entity; + } else { + Mage::throwException('Invalid entity type: ' . $code); } } - if (empty($field)) { - $field = 'entity_type_code'; + // lookup by code + if (empty($field) || $field == 'entity_type_code') { + $entity = $this->_entityTypeByCode[$code]; + if ($entity !== null) { + return $entity; + } else { + Mage::throwException('Invalid entity type: ' . $code); + } } + // lookup by other field foreach ($this->_entityTypes as $entityType) { if ($entityType->getData($field) == $code) { return $entityType; } } - Mage::throwException("Failed to find entity eav/entity_type for $field=$code"); + Mage::throwException('Failed to find entity eav/entity_type for ' . $field . '=' . $code); } /** @@ -376,6 +421,7 @@ protected function _getDefaultAttributeIfExists($entityType, $attributeCode, $st } } + // cache a miss as well $this->_defaultAttributes[$storeId][$entityType->getId()][$attributeCode] = false; return false; } @@ -394,11 +440,8 @@ public function getAttribute($entityType, $code, $storeId = null) return $code; } - Varien_Profiler::start('EAV: ' . __METHOD__); - - $storeId = $storeId !== null ? $storeId : Mage::app()->getStore()->getId(); + $storeId = $storeId !== null ? $storeId : $this->_storeId(); $this->_initializeStore($storeId); - $entityType = $this->getEntityType($entityType); // lookup id by code @@ -407,7 +450,7 @@ public function getAttribute($entityType, $code, $storeId = null) } // get model - $attribute = false; + $attribute = null; if (isset($this->_entityTypeAttributes[$storeId][$entityType->getId()][$code])) { $attributeData = $this->_entityTypeAttributes[$storeId][$entityType->getId()][$code]; if (is_array($attributeData)) { @@ -420,8 +463,6 @@ public function getAttribute($entityType, $code, $storeId = null) $attribute = $this->_getDefaultAttributeIfExists($entityType, $code, $storeId); } - Varien_Profiler::stop('EAV: ' . __METHOD__); - return $attribute; } @@ -435,10 +476,10 @@ public function getAttributes($entityType) $entityType = $this->getEntityType($entityType); $attributes = []; - $storeId = Mage::app()->getStore()->getId(); + $storeId = $this->_storeId(); // need to access attributes to ensure they are hydrated and initialized foreach (array_keys($this->_entityTypeAttributes[$storeId][$entityType->getId()]) as $attributeId) { - $attributes[] = $this->getAttribute($entityType, $attributeId); + $attributes[] = $this->getAttribute($entityType, $attributeId, $storeId); } Varien_Profiler::stop('EAV: ' . __METHOD__); @@ -463,7 +504,7 @@ public function getEntityAttributeCodes($entityType, $object = null) // Technically store id is irrelevant for attribute sets, they are the same in all store scopes. // Use current store id when not specified to avoid loading two store-scope attribute data sets from cache - $storeId = Mage::app()->getStore()->getStoreId(); + $storeId = $this->_storeId(); if (($object instanceof Varien_Object) && $object->getStoreId()) { $storeId = $object->getStoreId(); } @@ -536,11 +577,10 @@ public function loadCollectionAttributes($entityType, $attributes) } /** - * @deprecated No longer required. All attribute data is cached on-access. - * * @param string|Mage_Eav_Model_Entity_Type $entityType * @param array $attributes * @return $this + * @deprecated No longer required. All attribute data is cached on-access. */ public function importAttributesData($entityType, array $attributes) { diff --git a/app/code/core/Mage/Eav/Model/Observer.php b/app/code/core/Mage/Eav/Model/Observer.php new file mode 100644 index 00000000000..b4f7041480c --- /dev/null +++ b/app/code/core/Mage/Eav/Model/Observer.php @@ -0,0 +1,48 @@ +getData('controller_action'); + + // initialize cached store_id for frontend controllers only to avoid issues with cron jobs and admin controllers which sometimes change store view + if ($controllerAction instanceof Mage_Core_Controller_Front_Action) { + Mage::getSingleton('eav/config')->setCurrentStoreId(Mage::app()->getStore()->getId()); + } + } + +} diff --git a/app/code/core/Mage/Eav/etc/config.xml b/app/code/core/Mage/Eav/etc/config.xml index 0a69d58c67f..1d0518aae4a 100644 --- a/app/code/core/Mage/Eav/etc/config.xml +++ b/app/code/core/Mage/Eav/etc/config.xml @@ -108,6 +108,16 @@ + + + + + eav/observer + onControllerActionPredispatch + + + + From af111e331fea4e1b233374e13d50af1b7d7507fa Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Fri, 27 Jan 2023 21:28:51 +0100 Subject: [PATCH 13/25] fixed cache being written every request --- app/code/core/Mage/Eav/Model/Config.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 3d54e2a6f24..26fd3342fa9 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -147,8 +147,9 @@ protected function _initializeStore($storeId = null) Varien_Profiler::start('EAV: ' . __METHOD__); - if ($this->_isCacheEnabled()) { - $this->_loadFromCache($storeId); + if ($this->_isCacheEnabled() && $this->_loadFromCache($storeId)) { + $this->_storeInitialized[$storeId] = true; + return; } if (empty($this->_entityTypes)) { @@ -233,6 +234,11 @@ protected function _loadAttributeSetInfo() $this->_attributeSetInfo = Mage::getResourceModel('eav/entity_attribute_set')->getSetInfo(); } + /** + * @param int $storeId + * @return bool true if successfully loaded from cache, false otherwise + * @throws Exception + */ protected function _loadFromCache($storeId) { Varien_Profiler::start('EAV: ' . __METHOD__); @@ -240,7 +246,7 @@ protected function _loadFromCache($storeId) $cacheData = Mage::app()->loadCache(self::ENTITIES_CACHE_ID . '_' . $storeId); if ($cacheData === false) { Varien_Profiler::stop('EAV: ' . __METHOD__); - return; + return false; } $cacheData = unserialize($cacheData); @@ -268,6 +274,7 @@ protected function _loadFromCache($storeId) $this->_attributeSetInfo = $cacheData['_attributeSetInfo']; Varien_Profiler::stop('EAV: ' . __METHOD__); + return true; } protected function _saveToCache($storeId) From cc7bce4a66203778ee915f913d82c36c6d89fbb3 Mon Sep 17 00:00:00 2001 From: Fabrizio Balliano Date: Sun, 29 Jan 2023 11:26:19 +0000 Subject: [PATCH 14/25] PHPCS --- app/code/core/Mage/Eav/Model/Observer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/core/Mage/Eav/Model/Observer.php b/app/code/core/Mage/Eav/Model/Observer.php index b4f7041480c..350f38f03e9 100644 --- a/app/code/core/Mage/Eav/Model/Observer.php +++ b/app/code/core/Mage/Eav/Model/Observer.php @@ -28,7 +28,6 @@ */ class Mage_Eav_Model_Observer { - /** * @param Varien_Event_Observer $event * @return void From 51824a938e7d15618a08c1380d566c885506334c Mon Sep 17 00:00:00 2001 From: Fabrizio Balliano Date: Sun, 29 Jan 2023 11:26:27 +0000 Subject: [PATCH 15/25] PHPCS --- app/code/core/Mage/Eav/Model/Observer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/core/Mage/Eav/Model/Observer.php b/app/code/core/Mage/Eav/Model/Observer.php index 350f38f03e9..c3cf36acc53 100644 --- a/app/code/core/Mage/Eav/Model/Observer.php +++ b/app/code/core/Mage/Eav/Model/Observer.php @@ -43,5 +43,4 @@ public function onControllerActionPredispatch($event) Mage::getSingleton('eav/config')->setCurrentStoreId(Mage::app()->getStore()->getId()); } } - } From f3027e1d070b89f4e14bd25474388c34b68c1aa4 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Sun, 29 Jan 2023 15:29:13 +0100 Subject: [PATCH 16/25] fixed phpstan issues and improved phpdoc --- app/code/core/Mage/Catalog/Model/Layer.php | 2 +- .../Mage/Catalog/Model/Resource/Category.php | 6 +++++- .../Product/Edit/Tab/Downloadable/Links.php | 8 +++++-- app/code/core/Mage/Eav/Model/Config.php | 21 ++++++++++--------- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/app/code/core/Mage/Catalog/Model/Layer.php b/app/code/core/Mage/Catalog/Model/Layer.php index 26a10e26fd2..262f9fab244 100644 --- a/app/code/core/Mage/Catalog/Model/Layer.php +++ b/app/code/core/Mage/Catalog/Model/Layer.php @@ -219,7 +219,7 @@ public function getFilterableAttributes() foreach ($setAttributeIds as $attributeId) { if (!isset($attributes[$attributeId])) { $attribute = $eavConfig->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeId); - if ($attribute && $attribute->getIsFilterable()) { + if ($attribute instanceof Mage_Catalog_Model_Resource_Eav_Attribute && $attribute->getIsFilterable()) { $attributes[$attributeId] = $attribute; } } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Category.php b/app/code/core/Mage/Catalog/Model/Resource/Category.php index c86cefda5a0..3821d793cb3 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Category.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Category.php @@ -483,9 +483,13 @@ public function getChildrenAmount($category, $isActiveFlag = true) protected function _getIsActiveAttributeId() { if ($this->_isActiveAttributeId === null) { - $this->_isActiveAttributeId = Mage::getSingleton('eav/config') + $attributeId = Mage::getSingleton('eav/config') ->getAttribute(Mage_Catalog_Model_Category::ENTITY, 'is_active') ->getId(); + if (!is_int($attributeId)) { + Mage::throwException("Failed to find category attribute is_active"); + } + $this->_isActiveAttributeId = $attributeId; } return $this->_isActiveAttributeId; diff --git a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php index 70e55bbabdc..1c85be6d1ce 100644 --- a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php +++ b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php @@ -67,11 +67,15 @@ public function getProduct() */ public function getPurchasedSeparatelyAttribute() { - if (is_null($this->_purchasedSeparatelyAttribute)) { + if ($this->_purchasedSeparatelyAttribute === null) { $_attributeCode = 'links_purchased_separately'; - $this->_purchasedSeparatelyAttribute = Mage::getSingleton('eav/config') + $attribute = Mage::getSingleton('eav/config') ->getAttribute(Mage_Catalog_Model_Product::ENTITY, $_attributeCode); + if (!($attribute instanceof Mage_Catalog_Model_Resource_Eav_Attribute)) { + Mage::throwException("Attribute links_purchased_separately must be of type Mage_Catalog_Model_Resource_Eav_Attribute"); + } + $this->_purchasedSeparatelyAttribute = $attribute; } return $this->_purchasedSeparatelyAttribute; diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 26fd3342fa9..e38b6f8345a 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -89,7 +89,7 @@ class Mage_Eav_Model_Config protected $_isCacheEnabled = null; /** - * @var int|false + * @var int|false|null */ protected $_currentStoreId; @@ -252,7 +252,7 @@ protected function _loadFromCache($storeId) $this->_entityTypes = []; $this->_entityTypeByCode = []; - /** @var Mage_Eav_Model_Entity_Type $entityType */ + /** @var array $entityTypeData */ foreach ($cacheData['_entityTypes'] as $entityTypeData) { $entityType = Mage::getModel('eav/entity_type') ->setData($entityTypeData); @@ -261,8 +261,10 @@ protected function _loadFromCache($storeId) } $this->_entityTypeAttributes[$storeId] = []; - /** @var Mage_Eav_Model_Entity_Type $entityType */ + /** @var int $entityTypeId */ + /** @var array $entityTypeAttributes */ foreach ($cacheData['_entityTypeAttributes'] as $entityTypeId => $entityTypeAttributes) { + /** @var array $attributeData */ foreach ($entityTypeAttributes as $attributeData) { $attributeId = $attributeData['attribute_id']; $attributeCode = $attributeData['attribute_code']; @@ -356,6 +358,7 @@ protected function _hydrateAttribute($attributeData) * * @param mixed $code * @param string|null $code + * @param string $field * @return Mage_Eav_Model_Entity_Type * @throws Mage_Core_Exception */ @@ -370,11 +373,10 @@ public function getEntityType($code, $field = null) $this->_initializeStore(); } - // lookup by id* + // lookup by id if (empty($field) && is_numeric($code)) { - $entity = $this->_entityTypes[$code]; - if ($entity !== null) { - return $entity; + if (isset($this->_entityTypes[$code])) { + return $this->_entityTypes[$code]; } else { Mage::throwException('Invalid entity type: ' . $code); } @@ -382,9 +384,8 @@ public function getEntityType($code, $field = null) // lookup by code if (empty($field) || $field == 'entity_type_code') { - $entity = $this->_entityTypeByCode[$code]; - if ($entity !== null) { - return $entity; + if (isset($this->_entityTypeByCode[$code])) { + return $this->_entityTypeByCode[$code]; } else { Mage::throwException('Invalid entity type: ' . $code); } From c6af34fa99530b6f7bc2e61aab1ac8fe8d7a58ab Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Sun, 29 Jan 2023 15:58:42 +0100 Subject: [PATCH 17/25] removed phpstan ignored patterns as those no longer apply --- phpstan.dist.baseline.neon | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/phpstan.dist.baseline.neon b/phpstan.dist.baseline.neon index 15cdb3bfd1f..326481b8ae6 100644 --- a/phpstan.dist.baseline.neon +++ b/phpstan.dist.baseline.neon @@ -2270,11 +2270,6 @@ parameters: count: 1 path: app/code/core/Mage/Catalog/Model/Resource/Category.php - - - message: "#^Property Mage_Catalog_Model_Resource_Category\\:\\:\\$_isActiveAttributeId \\(int\\) does not accept string\\|false\\|null\\.$#" - count: 1 - path: app/code/core/Mage/Catalog/Model/Resource/Category.php - - message: "#^Property Mage_Catalog_Model_Resource_Category\\:\\:\\$_storeId \\(int\\) on left side of \\?\\? is not nullable\\.$#" count: 1 @@ -4150,16 +4145,6 @@ parameters: count: 1 path: app/code/core/Mage/Directory/Model/Resource/Country/Collection.php - - - message: "#^Method Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Links\\:\\:getPurchasedSeparatelyAttribute\\(\\) should return Mage_Catalog_Model_Resource_Eav_Attribute but returns Mage_Eav_Model_Entity_Attribute\\.$#" - count: 1 - path: app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php - - - - message: "#^Property Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Links\\:\\:\\$_purchasedSeparatelyAttribute \\(Mage_Catalog_Model_Resource_Eav_Attribute\\|null\\) does not accept Mage_Eav_Model_Entity_Attribute\\.$#" - count: 1 - path: app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php - - message: "#^Left side of && is always true\\.$#" count: 1 @@ -4270,26 +4255,11 @@ parameters: count: 1 path: app/code/core/Mage/Eav/Model/Config.php - - - message: "#^Cannot call method loadByCode\\(\\) on Mage_Core_Model_Abstract\\|false\\.$#" - count: 1 - path: app/code/core/Mage/Eav/Model/Config.php - - - - message: "#^Left side of && is always true\\.$#" - count: 1 - path: app/code/core/Mage/Eav/Model/Config.php - - message: "#^Method Mage_Eav_Model_Config\\:\\:getAttribute\\(\\) should return Mage_Eav_Model_Entity_Attribute_Abstract\\|false but returns Mage_Eav_Model_Entity_Attribute_Interface\\.$#" count: 1 path: app/code/core/Mage/Eav/Model/Config.php - - - message: "#^Negated boolean expression is always true\\.$#" - count: 1 - path: app/code/core/Mage/Eav/Model/Config.php - - message: "#^Method Mage_Eav_Model_Convert_Adapter_Entity\\:\\:_getCollectionForLoad\\(\\) should return Mage_Eav_Model_Entity_Collection but returns Mage_Core_Model_Resource_Db_Collection_Abstract\\|false\\.$#" count: 1 From 75be655fea950d96a976052aaa9ca40fb11dfaa0 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Wed, 1 Feb 2023 17:12:04 +0100 Subject: [PATCH 18/25] avoid Varien_Object::__call for Mage_Catalog_Model_Resource_Eav_Attribute::getIsFilterable as it may be called several thousands of times during a request --- .../core/Mage/Catalog/Model/Resource/Eav/Attribute.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Attribute.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Attribute.php index 91eaf6a8ac2..afaef44d8e1 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Attribute.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Attribute.php @@ -37,7 +37,6 @@ * @method $this setIsSearchable(int $value) * @method int getSearchWeight() * @method $this setSearchWeight(int $value) - * @method int getIsFilterable() * @method $this setIsFilterable(int $value) * @method int getIsComparable() * @method $this setIsComparable(int $value) @@ -288,6 +287,15 @@ public function getFrontendLabel() return $this->_getData('frontend_label'); } + /** + * Retrieve is_filterable value + * @return int + */ + public function getIsFilterable() + { + return $this->_getData('is_filterable'); + } + /** * Get Attribute translated label for store * From 0baa7fab80eb09ae38db5ac998a219fdacf844e2 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 2 Feb 2023 23:34:41 +0100 Subject: [PATCH 19/25] code cleanup, hide internal exception from user --- .../core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php | 2 +- .../Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php | 2 +- app/code/core/Mage/Eav/Model/Config.php | 1 - app/code/core/Mage/Eav/Model/Observer.php | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php index b33cdcf428e..17adc97b28a 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Flat/Indexer.php @@ -273,7 +273,7 @@ public function getAttribute($attributeCode) $attribute->setEntity($entity); if (!($attribute instanceof Mage_Eav_Model_Entity_Attribute)) { - Mage::throwException('Product attribute(code=". $attributeCode . ") is expected to be of type Mage_Eav_Model_Entity_Attribute'); + throw new Exception('Product attribute(code=' . $attributeCode . ') is expected to be of type Mage_Eav_Model_Entity_Attribute'); } return $attribute; diff --git a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php index 1c85be6d1ce..22f5d9b71cf 100644 --- a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php +++ b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php @@ -73,7 +73,7 @@ public function getPurchasedSeparatelyAttribute() $attribute = Mage::getSingleton('eav/config') ->getAttribute(Mage_Catalog_Model_Product::ENTITY, $_attributeCode); if (!($attribute instanceof Mage_Catalog_Model_Resource_Eav_Attribute)) { - Mage::throwException("Attribute links_purchased_separately must be of type Mage_Catalog_Model_Resource_Eav_Attribute"); + Mage::throwException('Attribute links_purchased_separately must be of type Mage_Catalog_Model_Resource_Eav_Attribute'); } $this->_purchasedSeparatelyAttribute = $attribute; } diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index e38b6f8345a..38efec04699 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -111,7 +111,6 @@ public function clear() $this->_defaultAttributes = []; Mage::app()->cleanCache([self::ENTITIES_CACHE_ID]); - // Mage::app()->removeCache(self::ENTITIES_CACHE_ID . '_' . Mage::app()->getStore()->getId()); return $this; } diff --git a/app/code/core/Mage/Eav/Model/Observer.php b/app/code/core/Mage/Eav/Model/Observer.php index c3cf36acc53..708ebf24bf6 100644 --- a/app/code/core/Mage/Eav/Model/Observer.php +++ b/app/code/core/Mage/Eav/Model/Observer.php @@ -14,7 +14,6 @@ * * @category Mage * @package Mage_Eav - * @copyright Copyright (c) 2006-2020 Magento, Inc. (https://www.magento.com) * @copyright Copyright (c) 2019-2022 The OpenMage Contributors (https://www.openmage.org) * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ From 3fb665f7b4052d861f6daf2a6297576facbc1e4d Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Fri, 3 Feb 2023 17:12:48 +0100 Subject: [PATCH 20/25] fix Collection::getData() returning all column values as string by converting important attribute fields to int explicitly --- app/code/core/Mage/Eav/Model/Config.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 38efec04699..bda8fa38298 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -38,6 +38,15 @@ class Mage_Eav_Model_Config public const ENTITIES_CACHE_ID = 'EAV_ENTITY_TYPES'; + public const NUMERIC_ATTRIBUTE_COLUMNS = [ + // from table eav_attribute + "attribute_id", + "entity_type_id", + "is_required", + "is_user_defined", + "is_unique", + ]; + protected $_storeInitialized = []; /** @@ -215,6 +224,15 @@ protected function _loadEntityAttributes($entityType, $storeId) foreach ($entityAttributes as $entityAttributeData) { $attributeId = $entityAttributeData['attribute_id']; $attributeCode = $entityAttributeData['attribute_code']; + + // workaround for getAttributeCollection()->getData() returning all columns as string + foreach (self::NUMERIC_ATTRIBUTE_COLUMNS as $key) { + if (!isset($entityAttributeData[$key])) { + continue; + } + $entityAttributeData[$key] = (int)$entityAttributeData[$key]; + } + $this->_entityTypeAttributes[$storeId][$entityType->getId()][$attributeId] = $entityAttributeData; $this->_entityTypeAttributeIdByCode[$storeId][$entityType->getId()][$attributeCode] = $attributeId; $attributeCodes[] = $attributeCode; From d7371a72815cc3fda59522d6a14b2a7c7c96598b Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Fri, 3 Feb 2023 17:14:28 +0100 Subject: [PATCH 21/25] fix more phpdoc issues --- .../core/Mage/Catalog/Model/Layer/Filter/Attribute.php | 2 +- .../CatalogSearch/Model/Layer/Filter/Attribute.php | 2 +- .../core/Mage/Eav/Model/Entity/Attribute/Abstract.php | 2 +- phpstan.dist.baseline.neon | 10 ---------- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php b/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php index e45217892e4..69d42bbba91 100644 --- a/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php +++ b/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php @@ -97,7 +97,7 @@ public function apply(Zend_Controller_Request_Abstract $request, $filterBlock) * Check whether specified attribute can be used in LN * * @param Mage_Catalog_Model_Resource_Eav_Attribute $attribute - * @return Mage_Catalog_Model_Resource_Eav_Attribute + * @return int */ protected function _getIsFilterableAttribute($attribute) { diff --git a/app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php index 31c489f3ef0..0da2fae3b3b 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php +++ b/app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php @@ -33,7 +33,7 @@ class Mage_CatalogSearch_Model_Layer_Filter_Attribute extends Mage_Catalog_Model * Check whether specified attribute can be used in LN * * @param Mage_Catalog_Model_Resource_Eav_Attribute $attribute - * @return bool + * @return int */ protected function _getIsFilterableAttribute($attribute) { diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php index 82ded4a67c1..0d5d7e406d4 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php @@ -196,7 +196,7 @@ public function setAttributeId($data) } /** - * Get attribute identifuer + * Get attribute identifier * * @return int|null */ diff --git a/phpstan.dist.baseline.neon b/phpstan.dist.baseline.neon index 326481b8ae6..81276db8a49 100644 --- a/phpstan.dist.baseline.neon +++ b/phpstan.dist.baseline.neon @@ -1995,11 +1995,6 @@ parameters: count: 1 path: app/code/core/Mage/Catalog/Model/Factory.php - - - message: "#^Comparison operation \"\\=\\=\" between Mage_Catalog_Model_Resource_Eav_Attribute and 1 results in an error\\.$#" - count: 1 - path: app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php - - message: "#^Parameter \\#2 \\$filterBlock \\(Varien_Object\\) of method Mage_Catalog_Model_Layer_Filter_Attribute\\:\\:apply\\(\\) should be compatible with parameter \\$filterBlock \\(null\\) of method Mage_Catalog_Model_Layer_Filter_Abstract\\:\\:apply\\(\\)$#" count: 1 @@ -2695,11 +2690,6 @@ parameters: count: 1 path: app/code/core/Mage/CatalogSearch/Model/Advanced.php - - - message: "#^Return type \\(bool\\) of method Mage_CatalogSearch_Model_Layer_Filter_Attribute\\:\\:_getIsFilterableAttribute\\(\\) should be compatible with return type \\(Mage_Catalog_Model_Resource_Eav_Attribute\\) of method Mage_Catalog_Model_Layer_Filter_Attribute\\:\\:_getIsFilterableAttribute\\(\\)$#" - count: 1 - path: app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php - - message: "#^Call to function is_array\\(\\) with bool\\|string will always evaluate to false\\.$#" count: 1 From eeded1daba79121b9032e811a9b265dfa2683110 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 9 Feb 2023 12:43:17 +0100 Subject: [PATCH 22/25] fetch entity type via constant, add phpdoc type for Mage_Eav_Model_Entity_Attribute_Abstract::_getResource --- app/code/core/Mage/Catalog/Model/Api2/Product.php | 2 +- app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php | 1 + app/code/core/Mage/Sales/Model/Entity/Quote.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/core/Mage/Catalog/Model/Api2/Product.php b/app/code/core/Mage/Catalog/Model/Api2/Product.php index 2029654ff0b..1d034d7062a 100644 --- a/app/code/core/Mage/Catalog/Model/Api2/Product.php +++ b/app/code/core/Mage/Catalog/Model/Api2/Product.php @@ -39,7 +39,7 @@ public function getAvailableAttributes($userType, $operation) { $attributes = $this->getAvailableAttributesFromConfig(); /** @var Mage_Eav_Model_Entity_Type $entityType */ - $entityType = Mage::getSingleton('eav/config')->getEntityType('catalog_product'); + $entityType = Mage::getSingleton('eav/config')->getEntityType(Mage_Catalog_Model_Product::ENTITY); $entityOnlyAttrs = $this->getEntityOnlyAttributes($userType, $operation); /** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */ foreach ($entityType->getAttributeCollection() as $attribute) { diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php index 0d5d7e406d4..57992cedc6d 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php @@ -57,6 +57,7 @@ * @method string getStoreLabel() * @method string getUsedForSortBy() * @method array getValidateRules() + * @method Mage_Eav_Model_Resource_Entity_Attribute _getResource() */ abstract class Mage_Eav_Model_Entity_Attribute_Abstract extends Mage_Core_Model_Abstract implements Mage_Eav_Model_Entity_Attribute_Interface { diff --git a/app/code/core/Mage/Sales/Model/Entity/Quote.php b/app/code/core/Mage/Sales/Model/Entity/Quote.php index 6cf0cc1b8f2..dec7bbc5d44 100644 --- a/app/code/core/Mage/Sales/Model/Entity/Quote.php +++ b/app/code/core/Mage/Sales/Model/Entity/Quote.php @@ -116,6 +116,6 @@ public function loadByIdWithoutStore($quote, $quoteId) */ public function getReservedOrderId($quote) { - return Mage::getSingleton('eav/config')->getEntityType('order')->fetchNewIncrementId($quote->getStoreId()); + return Mage::getSingleton('eav/config')->getEntityType(Mage_Sales_Model_Order::ENTITY)->fetchNewIncrementId($quote->getStoreId()); } } From bcef7459c498f108345d67eb8f9417d9a79f3721 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 9 Feb 2023 13:05:43 +0100 Subject: [PATCH 23/25] allow repeated calls to Mage_Eav_Model_Resource_Entity_Attribute_Collection::addStoreLabel if the storeId matches --- .../Resource/Entity/Attribute/Collection.php | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Collection.php b/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Collection.php index df60f2bedb0..f5738d498bc 100644 --- a/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Collection.php +++ b/app/code/core/Mage/Eav/Model/Resource/Entity/Attribute/Collection.php @@ -41,6 +41,13 @@ class Mage_Eav_Model_Resource_Entity_Attribute_Collection extends Mage_Core_Mode */ protected $_addSetInfoFlag = false; + /** + * Tracks if addStoreLabel has been called to avoid conflicts on duplicate calls + * + * @var bool|int + */ + protected $_addedStoreLabelsFlag = false; + /** * Resource model initialization * @@ -418,14 +425,21 @@ public function setCodeFilter($code) */ public function addStoreLabel($storeId) { - $adapter = $this->getConnection(); - $joinExpression = $adapter - ->quoteInto('al.attribute_id = main_table.attribute_id AND al.store_id = ?', (int) $storeId); - $this->getSelect()->joinLeft( - ['al' => $this->getTable('eav/attribute_label')], - $joinExpression, - ['store_label' => $adapter->getIfNullSql('al.value', 'main_table.frontend_label')] - ); + // if not called previously + if ($this->_addedStoreLabelsFlag === false) { + $adapter = $this->getConnection(); + $joinExpression = $adapter + ->quoteInto('al.attribute_id = main_table.attribute_id AND al.store_id = ?', (int)$storeId); + $this->getSelect()->joinLeft( + ['al' => $this->getTable('eav/attribute_label')], + $joinExpression, + ['store_label' => $adapter->getIfNullSql('al.value', 'main_table.frontend_label')] + ); + $this->_addedStoreLabelsFlag = $storeId; + } // check that previous call $storeId matches current call + elseif ($this->_addedStoreLabelsFlag !== $storeId) { + throw new Exception('Cannot call addStoreLabel for different store views on the same collection'); + } return $this; } From f77d4a8dc16f5f2ee79a825f823e3eea80708e11 Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 16 Feb 2023 10:34:57 +0100 Subject: [PATCH 24/25] remove commented code, change quote style --- app/code/core/Mage/Eav/Model/Config.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index bda8fa38298..d73ac825f8a 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -40,11 +40,11 @@ class Mage_Eav_Model_Config public const NUMERIC_ATTRIBUTE_COLUMNS = [ // from table eav_attribute - "attribute_id", - "entity_type_id", - "is_required", - "is_user_defined", - "is_unique", + 'attribute_id', + 'entity_type_id', + 'is_required', + 'is_user_defined', + 'is_unique', ]; protected $_storeInitialized = []; @@ -170,7 +170,6 @@ protected function _initializeStore($storeId = null) $this->_entityTypeAttributes[$storeId] = []; foreach ($this->_entityTypes as $entityType) { $this->_loadEntityAttributes($entityType, $storeId); - // $this->_loadEntityDefaultAttributes($entityType, $storeId); } } @@ -196,9 +195,6 @@ protected function _loadEntityTypes() $entityTypeCollection = Mage::getResourceModel('eav/entity_type_collection'); /** @var Mage_Eav_Model_Entity_Type $entityType */ foreach ($entityTypeCollection as $entityType) { - // if (empty($entityType->getAttributeModel())) { - // $entityType->setAttributeModel('eav/entity_attribute'); - // } $this->_entityTypes[$entityType->getId()] = $entityType; $this->_entityTypeByCode[$entityType->getEntityTypeCode()] = $entityType; } From 6408cf17f18fb473c30d0ddb36b2b491ebd2418e Mon Sep 17 00:00:00 2001 From: David Hiendl Date: Thu, 16 Feb 2023 10:40:28 +0100 Subject: [PATCH 25/25] add note to README.md for openmage 19.x to 20.x mentioning Mage_Eav_Model_Config changes --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bd43b557cf9..bf985eaf2af 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ Do not use 20.x.x if you need IE support. - removed support for `global/sales/old_fields_map` defined in XML ([#921](https://github.com/OpenMage/magento-lts/pull/921)) - enabled website level config cache ([#2355](https://github.com/OpenMage/magento-lts/pull/2355)) - make overrides of Mage_Core_Model_Resource_Db_Abstract::delete respect parent api ([#1257](https://github.com/OpenMage/magento-lts/pull/1257)) +- rewrite Mage_Eav_Model_Config as cache for all eav entity and attribute reads ([#2993](https://github.com/OpenMage/magento-lts/pull/2993)) For full list of changes, you can [compare tags](https://github.com/OpenMage/magento-lts/compare/1.9.4.x...20.0).