Understanding Magento 2 EAV and Custom Attributes

Understanding Magento 2 EAV and Custom Attributes

October 27, 2025 · By Magento Company
Understanding Magento 2 EAV and Custom Attributes

Magento’s product data model is famously flexible: merchants can add attributes - colour, material, care instructions - without touching the database schema. The mechanism behind that flexibility is EAV (entity-attribute-value), and understanding it explains both Magento’s power and several of its performance traps.

How EAV Stores Data

A traditional table stores one row per product with a column per field. EAV instead splits data across four table families:

  • Entity: catalog_product_entity - just the ID, SKU, type and timestamps
  • Attribute: eav_attribute - the definition of each attribute (code, type, input)
  • Value tables: one per data type - catalog_product_entity_varchar, _int, _text, _decimal, _datetime - each row storing (entity, attribute, value)

A product with 80 attributes is therefore 80+ rows scattered across five value tables. Reading a product means joining them all; the flat catalog index exists precisely to hide that cost from the storefront.

Adding an Attribute Programmatically

Admin-added attributes are fine for merchants, but modules should define attributes in code so they deploy with the project. Use a data patch:

namespace Acme\Catalog\Setup\Patch\Data;

use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;

class AddCareInstructions implements DataPatchInterface
{
    public function __construct(
        private ModuleDataSetupInterface $setup,
        private EavSetupFactory $eavSetupFactory
    ) {}

    public function apply(): void
    {
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->setup]);
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'care_instructions',
            [
                'type' => 'text',
                'label' => 'Care Instructions',
                'input' => 'textarea',
                'required' => false,
                'visible_on_front' => true,
                'used_in_product_listing' => true,
                'user_defined' => true,
            ]
        );
    }

    public static function getDependencies(): array { return []; }
    public function getAliases(): array { return []; }
}

The flags matter: used_in_product_listing puts the value into the flat index and collection queries; visible_on_front controls display; user_defined means uninstall scripts leave it alone.

EAV vs Flat Tables for Custom Data

EAV is right when the data is genuinely merchant-extensible: product attributes, customer attributes, category fields. It is wrong for structured module data. A custom module storing, say, warehouse locations should create its own declarative-schema table with real columns and indexes - faster, queryable with SQL, and far easier to maintain. The anti-pattern we see in audits is modules stuffing structured relational data into EAV because “that’s what Magento does”.

Performance Notes

  • Every attribute joined into a listing costs query time; keep used_in_product_listing to attributes the grid actually shows
  • Hundreds of rarely-used attributes bloat the flat index and slow reindexing - prune ruthlessly
  • Swatch and filterable attributes multiply layered-navigation work; only mark attributes filterable when customers genuinely filter by them

EAV is a deliberate trade: flexibility for merchants in exchange for complexity in the engine. Use it for what it is for, put everything else in real tables, and keep the attribute set lean - your reindex times will thank you.

Development Database Magento 2