Skip to main content

Known Issues & Gotchas

Common pitfalls, performance traps, and solutions for EAV development

Issue Summary

3
Critical
4
Major
3
Minor
10
Total Issues
Critical Data loss or major performance degradation
Major Significant impact, workaround required
Minor Edge cases or minor inconvenience
Info Best practice guidance

Critical Issues

N+1 Query Problem on Entity Load
Critical

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 collections
Flat Table Sync Failures
Critical

Affected 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-sync
Store Scope Value Inheritance Confusion
Critical

Affected 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 inheritance

Major Issues

Cache Invalidation After Attribute Changes
Major

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 admin
Attribute Option Order Persistence
Major

Affected 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 admin
Backend Model Validation Bypass
Major

Affected 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-integrity
Attribute Set Assignment During Import
Major

Affected 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-data

Minor Issues

Source Model Caching
Minor

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;
}
Multiselect Value Delimiter Handling
Minor

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);
Attribute Label Translation Scope
Minor

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

  1. Never use addAttributeToSelect('*') - Select only needed attributes
  2. Enable flat tables for catalog in production
  3. Use collection filters before loading, not after
  4. Batch operations when creating/updating multiple entities
  5. 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) { ... }