Attribute Sets and Groups

An attribute set decides which fields a product has; groups decide which sections those fields appear in.

Syntax$this->productAttributeManagement->getAttributes($attributeSetId)

An attribute set is a named list of attributes. Every product points at one through the attribute_set_id column on catalog_product_entity, and that set is what decides which fields the product has. A clean installation has one set called Default.

An attribute group is a named subdivision of a set. Groups are what a merchant sees as the collapsible sections of the product form: Content, Images and Videos, Search Engine Optimization, Design.

The tables

TableHolds
eav_attribute_setOne row per set, with entity_type_id and a name
eav_attribute_groupOne row per section of one set
eav_entity_attributeThe assignment: set, group, attribute, sort order

An attribute can belong to many sets. The same attribute assigned to two sets is still one definition and one column of value rows.

Not the same as a product type

type_id (simple, configurable, bundle, grouped, virtual, downloadable) controls behaviour: how price is calculated, whether the product has children. The attribute set controls only which fields exist. A merchant picks both when creating a product, and they are independent.

After a code change that adds a set or a group, run bin/magento setup:upgrade and then bin/magento cache:flush; the product form is built from cached configuration and will otherwise keep the old sections.

Example

Example · php
<?php
// app/code/Vendor/EavDemo/Console/Command/ShowSet.php
declare(strict_types=1);

namespace Vendor\EavDemo\Console\Command;

use Magento\Catalog\Api\ProductAttributeManagementInterface;
use Magento\Framework\Console\Cli;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ShowSet extends Command
{
    public function __construct(
        private readonly ProductAttributeManagementInterface $attributeManagement,
        ?string $name = null
    ) {
        parent::__construct($name);
    }

    protected function configure(): void
    {
        $this->setName('vendor:eav:show-set');
        $this->setDescription('List the attributes assigned to an attribute set');
        $this->addArgument('set_id', InputArgument::REQUIRED, 'Attribute set id');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $setId = (int) $input->getArgument('set_id');

        foreach ($this->attributeManagement->getAttributes($setId) as $attribute) {
            $output->writeln(sprintf(
                '%-28s %s',
                $attribute->getAttributeCode(),
                $attribute->getFrontendInput()
            ));
        }

        return Cli::RETURN_SUCCESS;
    }
}

// bin/magento vendor:eav:show-set 4
// name                         text
// description                  textarea
// price                        price
// ... one line per assigned attribute, in group and sort order

When to use it

  • Giving one product category its own fields without adding them to every product.
  • Working out why a field a merchant created is missing from one product's form.
  • Grouping new fields into their own section of the admin product page.

More examples

Create a set in a data patch, copied from Default

This is the two-step the admin itself performs: save the set so it has an id, call initFromSkeleton() to copy another set's groups and assignments, then save again. validate() rejects a duplicate name. Applied by bin/magento setup:upgrade; follow it with cache:flush so the new set appears in the product form's set selector.

Example · php
<?php
// app/code/Vendor/Catalog/Setup/Patch/Data/AddBooksSet.php
declare(strict_types=1);

namespace Vendor\Catalog\Setup\Patch\Data;

use Magento\Catalog\Api\Data\ProductAttributeInterface;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Eav\Model\Config as EavConfig;
use Magento\Eav\Model\Entity\Attribute\SetFactory;
use Magento\Framework\Setup\Patch\DataPatchInterface;

class AddBooksSet implements DataPatchInterface
{
    public function __construct(
        private readonly EavConfig $eavConfig,
        private readonly SetFactory $attributeSetFactory,
        private readonly AttributeSetRepositoryInterface $attributeSetRepository
    ) {
    }

    public function apply(): void
    {
        $entityType = $this->eavConfig->getEntityType(
            ProductAttributeInterface::ENTITY_TYPE_CODE
        );

        $set = $this->attributeSetFactory->create();
        $set->setEntityTypeId((int) $entityType->getId());
        $set->setAttributeSetName('Books');
        $set->setSortOrder(200);
        $set->validate();

        $this->attributeSetRepository->save($set);
        $set->initFromSkeleton((int) $entityType->getDefaultAttributeSetId());
        $this->attributeSetRepository->save($set);
    }

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

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

Assign an existing attribute to a group

assign() takes ids, so it suits code that already has them, such as an admin controller or an import. The commented pair shows the shorthand that EavSetup offers inside a data patch: pass the set and group names and it resolves them to ids for you, which is the form you will meet most often in other modules. unassign() removes the assignment row and leaves the attribute definition alone.

Example · php
<?php
// app/code/Vendor/Catalog/Model/AssignAttribute.php
declare(strict_types=1);

namespace Vendor\Catalog\Model;

use Magento\Catalog\Api\ProductAttributeManagementInterface;

class AssignAttribute
{
    public function __construct(
        private readonly ProductAttributeManagementInterface $attributeManagement
    ) {
    }

    public function toGroup(int $setId, int $groupId, string $attributeCode): void
    {
        // assign($attributeSetId, $attributeGroupId, $attributeCode, $sortOrder)
        $this->attributeManagement->assign($setId, $groupId, $attributeCode, 100);
    }

    public function remove(int $setId, string $attributeCode): void
    {
        $this->attributeManagement->unassign($setId, $attributeCode);
    }
}

// The setup equivalent inside a data patch takes ids OR names,
// and these two calls do the same thing:
//
//     $eavSetup->addAttributeToSet('catalog_product', 4, 10, 'isbn', 100);
//     $eavSetup->addAttributeToSet('catalog_product', 'Default', 'Product Details', 'isbn', 100);

Discussion

  • Be the first to comment on this lesson.