Issue Summary
Critical Issues
Affected Code
Magento\Eav\Model\Entity\AbstractEntity::load()
Magento\Eav\Model\Entity\Collection\AbstractCollection::load()
Description
Loading EAV entities performs one query per value table type (varchar, int, decimal, text, datetime). When loading collections, this multiplies: N entities x 5+ value tables = severe performance degradation.
// BAD: Loading 100 products = 500+ queries $collection = $productCollectionFactory->create(); $collection->addAttributeToSelect('*'); foreach ($collection as $product) { // Each iteration may trigger lazy-loaded attribute queries }
Workaround
1. Use flat tables for read-heavy operations (catalog_product_flat, catalog_category_flat)
2. Explicitly select only needed attributes:
// GOOD: Only load required attributes $collection->addAttributeToSelect(['name', 'price', 'sku']);
3. Use joins for specific attributes rather than loading full entities
Tags
performance queries collectionsAffected Code
Magento\Catalog\Model\Indexer\Product\Flat
Magento\Catalog\Model\Indexer\Category\Flat
Description
When EAV attributes are modified but flat table indexers fail or are not triggered, the frontend shows stale data. This is especially problematic with:
- New attribute creation (flat tables missing columns)
- Attribute deletion (orphaned flat table columns)
- Attribute type changes (column type mismatch)
- Multi-store deployments with partial reindexing
Workaround
1. Always run full reindex after attribute schema changes:
bin/magento indexer:reindex catalog_product_flat catalog_category_flat
2. Monitor indexer status in production
3. Consider disabling flat tables if attribute schema changes frequently
Tags
indexer flat-tables data-syncAffected Code
Magento\Eav\Model\Entity\Attribute\AbstractAttribute::getValue()
Description
EAV attributes support store-scoped values that inherit from website then global scope. When developers directly query value tables without proper scope resolution, they may:
- Return wrong scope value (store-specific instead of global)
- Return NULL when inheritance should provide a value
- Overwrite global values when saving store-scoped entities
// BAD: Direct query ignores scope inheritance $value = $connection->fetchOne( "SELECT value FROM catalog_product_entity_varchar WHERE attribute_id = ? AND entity_id = ?" ); // Missing: AND store_id IN (0, $currentStoreId) ORDER BY store_id DESC LIMIT 1
Workaround
Always use the entity model or repository pattern which handles scope resolution:
// GOOD: Proper scope handling $product = $productRepository->getById( $productId, false, $storeId // Scope properly resolved ); $value = $product->getName();
Tags
store-scope multi-store inheritanceMajor Issues
Affected Code
Magento\Eav\Model\Config
Description
EAV attribute configuration is heavily cached. When attributes are modified programmatically (via setup scripts or API), the cached configuration may not be invalidated properly, leading to:
- New attributes not appearing in admin forms
- Attribute options showing stale values
- Validation rules not being applied
Workaround
// After programmatic attribute changes $cacheManager = $objectManager->get(CacheManager::class); $cacheManager->clean([EavConfig::CACHE_TAG]); // Or flush all caches bin/magento cache:clean config eav
Tags
cache config adminAffected Code
Magento\Eav\Model\Entity\Attribute\Source\Table
eav_attribute_option table
Description
Attribute option sort order is stored in the sort_order column, but:
- Admin UI doesn't always persist drag-drop order changes
- Import processes may reset sort order to insertion order
- API option creation ignores sort_order parameter in some versions
Workaround
Explicitly set sort_order via direct database update or custom setup script:
$connection->update( 'eav_attribute_option', ['sort_order' => $sortOrder], ['option_id = ?' => $optionId] );
Tags
options sort-order adminAffected Code
Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::validate()
Description
Backend model validation only runs when saving through the entity model. Direct database updates or ResourceModel::save() without entity context bypasses validation:
// BAD: Bypasses backend model validation $resourceModel->getConnection()->insert( 'catalog_product_entity_varchar', ['value' => $invalidValue, ...] ); // GOOD: Backend validation runs $product->setData('attribute_code', $value); $productRepository->save($product);
Workaround
Always use repository or model save() for EAV entity persistence. If direct SQL is required for performance, manually validate:
$attribute = $eavConfig->getAttribute('catalog_product', 'attribute_code'); $backend = $attribute->getBackend(); $backend->validate($entityObject); // Throws exception if invalid
Tags
validation backend-model data-integrityAffected Code
Magento\ImportExport\Model\Import\Entity\AbstractEntity
Description
During product import, attribute set changes can cause:
- Orphaned attribute values (attribute removed from set but values remain)
- Missing required attributes (new set has required attrs not in CSV)
- Type conflicts (attribute exists in both sets with different types)
Workaround
1. Validate attribute set compatibility before import
2. Clean orphaned values after attribute set changes:
-- Find orphaned values SELECT v.* FROM catalog_product_entity_varchar v LEFT JOIN eav_entity_attribute ea ON v.attribute_id = ea.attribute_id AND ea.attribute_set_id = ( SELECT attribute_set_id FROM catalog_product_entity WHERE entity_id = v.entity_id ) WHERE ea.entity_attribute_id IS NULL;
Tags
import attribute-set orphaned-dataMinor Issues
Description
Custom source models that fetch options from external sources (APIs, databases) are called multiple times per request without caching. This can cause performance issues with slow external services.
Workaround
Implement internal caching in custom source models:
protected $options = null; public function getAllOptions() { if ($this->options === null) { $this->options = $this->fetchFromExternalSource(); } return $this->options; }
Description
Multiselect attribute values are stored as comma-separated option IDs. If option values themselves contain commas, parsing can fail. The backend model handles this, but direct SQL queries may break.
Workaround
Always use the attribute's backend model for multiselect handling:
$attribute = $eavConfig->getAttribute('catalog_product', 'multiselect_attr'); $values = $attribute->getBackend()->getArrayValue($product);
Description
Attribute labels can be translated per store view via eav_attribute_label table. However, the admin interface doesn't always load the correct store's label when editing, leading to confusion about which translation is being modified.
Workaround
Verify store scope when working with attribute labels in admin:
// Get label for specific store $label = $attribute->getStoreLabel($storeId); // Or via table directly SELECT value FROM eav_attribute_label WHERE attribute_id = ? AND store_id = ?;
Performance Best Practices
EAV Performance Golden Rules
- Never use
addAttributeToSelect('*')- Select only needed attributes - Enable flat tables for catalog in production
- Use collection filters before loading, not after
- Batch operations when creating/updating multiple entities
- Profile queries - Watch for N+1 patterns in New Relic/logs
// Collection Performance Pattern // BAD: Filter after load (all entities loaded) $collection->addAttributeToSelect('*'); foreach ($collection as $item) { if ($item->getStatus() == 'active') { ... } } // GOOD: Filter before load (SQL WHERE clause) $collection->addAttributeToSelect(['name', 'status']) ->addAttributeToFilter('status', 'active'); foreach ($collection as $item) { ... }