Skip to content
intermediate Phase 41 · EAV Advanced

Creating Custom EAV Attributes

Creating custom EAV attributes with install data scripts, attribute creation, source models, and frontend models

45m
0 problems
Topic Progress 0%

Attribute Creation Methods

Method 1: Data Patch (Recommended)

<?php
namespace Vendor\Module\Setup\Patch\Data;

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

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

    public function install(): void
    {
        $this->setup->startSetup();
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->setup]);

        // Text attribute
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'custom_text',
            [
                'type' => 'varchar',
                'label' => 'Custom Text',
                'input' => 'text',
                'required' => false,
                'sort_order' => 100,
                'group' => 'General',
                'global' => \Magento\Eav\Model\Entity\Attribute\Scope::SCOPE_STORE,
                'visible_on_front' => true,
                'used_in_product_listing' => true,
            ]
        );

        // Select attribute
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'custom_select',
            [
                'type' => 'int',
                'label' => 'Custom Select',
                'input' => 'select',
                'source' => 'Vendor\Module\Model\Product\Attribute\Source\CustomOptions',
                'required' => false,
                'sort_order' => 101,
                'group' => 'General',
            ]
        );

        // Textarea attribute
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'custom_textarea',
            [
                'type' => 'text',
                'label' => 'Custom Textarea',
                'input' => 'textarea',
                'required' => false,
                'sort_order' => 102,
                'group' => 'General',
                'wysiwyg_enabled' => true,
                'is_html_allowed_on_front' => true,
            ]
        );

        $this->setup->endSetup();
    }

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

Attribute Properties

Complete Property List

$eavSetup->addAttribute(
    \Magento\Catalog\Model\Product::ENTITY,
    'attribute_code',
    [
        // Core properties
        'type' => 'varchar',              // Backend type
        'label' => 'Label',               // Admin label
        'input' => 'text',               // Frontend input
        'required' => false,             // Required in admin
        'sort_order' => 100,             // Display order
        'group' => 'General',            // Attribute group

        // Scope
        'global' => \Magento\Eav\Model\Entity\Attribute\Scope::SCOPE_STORE,
        // SCOPE_STORE (0), SCOPE_WEBSITE (1), SCOPE_GLOBAL (2)

        // Source model (for select/multiselect)
        'source' => 'Vendor\Module\Model\Source\Options',

        // Backend model (for data processing)
        'backend' => 'Vendor\Module\Model\Backend\Custom',

        // Frontend model (for rendering)
        'frontend' => 'Vendor\Module\Model\Frontend\Custom',

        // Validation
        'unique' => false,
        'validate_rules' => [
            'max_text_length' => 255,
            'min_text_length' => 1,
        ],

        // Visibility
        'visible' => true,
        'visible_on_front' => true,
        'used_in_product_listing' => true,

        // Search and filtering
        'filterable' => true,
        'searchable' => true,
        'comparable' => true,
        'used_for_sort_by' => true,
        'used_for_promo_rules' => true,

        // WYSIWYG
        'wysiwyg_enabled' => false,
        'is_html_allowed_on_front' => false,

        // Default value
        'default' => '',

        // Apply to product types
        'apply_to' => 'simple,configurable,virtual,downloadable',
    ]
);

Key Properties

Property Description Values
type Backend storage type varchar, text, int, decimal, datetime
input Admin input type text, textarea, select, boolean, price, date
global Value scope SCOPE_STORE, SCOPE_WEBSITE, SCOPE_GLOBAL
source Source model class Full class name for options
backend Backend model class Full class name for processing
frontend Frontend model class Full class name for rendering

Custom Source Models

Database-Driven Source Model

<?php
namespace Vendor\Module\Model\Product\Attribute\Source;

use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;

class CustomOptions extends AbstractSource
{
    public function __construct(
        private \Vendor\Module\Model\ResourceModel\Option\Collection $optionCollection
    ) {}

    public function toOptionArray(): array
    {
        $options = [
            ['value' => '', 'label' => __('-- Please Select --')]
        ];

        foreach ($this->optionCollection as $option) {
            $options[] = [
                'value' => $option->getId(),
                'label' => $option->getName()
            ];
        }

        return $options;
    }
}

Config-Driven Source Model

<?php
namespace Vendor\Module\Model\Product\Attribute\Source;

use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
use Magento\Framework\App\Config\ScopeConfigInterface;

class ConfigOptions extends AbstractSource
{
    public function __construct(
        private ScopeConfigInterface $config
    ) {}

    public function toOptionArray(): array
    {
        $options = [];
        $configValue = $this->config->getValue('vendor/module/options');

        if ($configValue) {
            $items = explode(',', $configValue);
            foreach ($items as $item) {
                $options[] = [
                    'value' => trim($item),
                    'label' => trim($item)
                ];
            }
        }

        return $options;
    }
}

Register Source Model

<!-- di.xml -->
<config>
    <virtualType name="CustomOptions" type="Vendor\Module\Model\Product\Attribute\Source\CustomOptions">
        <arguments>
            <argument name="optionCollection" xsi:type="object">Vendor\Module\Model\ResourceModel\Option\Collection</argument>
        </arguments>
    </virtualType>
</config>

Frontend and Backend Models

Frontend Model

<?php
namespace Vendor\Module\Model\Product\Attribute\Frontend;

use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend;
use Magento\Framework\DataObject;

class CustomFrontend extends AbstractFrontend
{
    public function getValue(DataObject $object): ?string
    {
        $value = $object->getData($this->getAttribute()->getAttributeCode());
        
        if ($value) {
            return strtoupper($value);
        }
        
        return null;
    }

    public function getOutputHtml(DataObject $object): string
    {
        $value = $this->getValue($object);
        return '<span class="custom-attribute">' . htmlspecialchars($value) . '</span>';
    }
}

Backend Model

<?php
namespace Vendor\Module\Model\Product\Attribute\Backend;

use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend;

class CustomBackend extends AbstractBackend
{
    public function validate($object)
    {
        $value = $object->getData($this->getAttribute()->getAttributeCode());
        
        if ($this->getAttribute()->getIsRequired() && empty($value)) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Attribute %1 is required.', $this->getAttribute()->getFrontendLabel())
            );
        }
        
        return true;
    }

    public function beforeSave($object)
    {
        $value = $object->getData($this->getAttribute()->getAttributeCode());
        
        // Transform value before save
        $object->setData(
            $this->getAttribute()->getAttributeCode(),
            trim($value)
        );
        
        return $this;
    }

    public function afterSave($object)
    {
        // Process after save
        return $this;
    }
}

Complete Registration

// Data patch
class AddCustomAttributeWithModels implements DataPatchInterface
{
    public function install(): void
    {
        $this->setup->startSetup();
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->setup]);

        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'custom_attribute',
            [
                'type' => 'varchar',
                'label' => 'Custom Attribute',
                'input' => 'select',
                'source' => 'Vendor\Module\Model\Product\Attribute\Source\CustomOptions',
                'backend' => 'Vendor\Module\Model\Product\Attribute\Backend\CustomBackend',
                'frontend' => 'Vendor\Module\Model\Product\Attribute\Frontend\CustomFrontend',
                'required' => false,
                'sort_order' => 100,
                'group' => 'General',
            ]
        );

        $this->setup->endSetup();
    }
}

Quiz

1. What is the recommended way to create custom attributes?

Question 1 options

2. What does the 'source' property define?

Question 2 options

3. What does 'apply_to' control?

Question 3 options

Flashcards

Question

How to create a custom attribute?

Answer

Use $eavSetup->addAttribute() with type, label, input, source properties

Question

What is a source model?

Answer

Class providing options for select/multiselect attributes

Question

What is a backend model?

Answer

Class handling data validation and processing on save

Question

What is a frontend model?

Answer

Class controlling attribute rendering on frontend

Question

What does apply_to control?

Answer

Which product types can use the attribute

Revision Notes

Key Takeaways

  • 1. Use data patches to create custom attributes (recommended method)
  • 2. Key properties: type, label, input, source, backend, frontend, group
  • 3. Source models provide options for select/multiselect attributes
  • 4. Backend models handle validation and data processing
  • 5. Frontend models control attribute rendering

Interview Tips

  • Demonstrate creating a custom attribute with data patch
  • Explain source, backend, and frontend model purposes
  • Discuss attribute properties and their effects

Cheat Sheet

addAttribute() Properties:
  type      → varchar, text, int, decimal, datetime
  input     → text, textarea, select, boolean, price, date
  label     → Admin display name
  source    → Options source model
  backend   → Data processing model
  frontend  → Rendering model
  group     → Attribute group name
  required  → Required in admin
  global    → SCOPE_STORE, SCOPE_WEBSITE, SCOPE_GLOBAL
  apply_to  → simple,configurable,bundle,grouped

Methods:
  $eavSetup->addAttribute($entity, $code, $options)
  $eavSetup->addAttributeToSet($entity, $setId, $groupId, $code)