Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Category Attribute: Bonus Multiplier per Category

Category Attribute: Bonus Multiplier per Category

~6 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

catalog_category is - just like catalog_product from chapter 19 - a standalone, already fully set up EAV entity with its own entity type code and its own value tables (catalog_category_entity_decimal and so on). loyalty_bonus_category (type decimal, default 0.0) is the second of the two parameters PointsCalculator::calculatePoints() from chapter 5 already accepts as $categoryBonus - an additive bonus point rate, not a multiplier like on the product.

The setup patch for the category attribute

app/code/Mironsoft/Loyalty/Setup/Patch/Data/InstallCategoryLoyaltyAttribute.php
<?php

declare(strict_types=1);

namespace Mironsoft\Loyalty\Setup\Patch\Data;

use Magento\Catalog\Model\Category;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;

/**
 * Registers the loyalty_bonus_category decimal attribute on catalog_category.
 */
class InstallCategoryLoyaltyAttribute implements DataPatchInterface
{
    /**
     * @param ModuleDataSetupInterface $moduleDataSetup Provides the setup connection for the patch.
     * @param EavSetupFactory $eavSetupFactory Creates the EavSetup helper used to register the attribute.
     */
    public function __construct(
        private readonly ModuleDataSetupInterface $moduleDataSetup,
        private readonly EavSetupFactory $eavSetupFactory
    ) {
    }

    /**
     * Adds the loyalty_bonus_category attribute to the existing catalog_category entity.
     *
     * @return void
     */
    public function apply(): void
    {
        $this->moduleDataSetup->getConnection()->startSetup();

        /** @var \Magento\Eav\Setup\EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);

        $eavSetup->addAttribute(Category::ENTITY, 'loyalty_bonus_category', [
            'type' => 'decimal',
            'label' => 'Loyalty Bonus',
            'input' => 'text',
            'required' => false,
            'default' => '0.0000',
            'global' => ScopedAttributeInterface::SCOPE_WEBSITE,
            'group' => 'General Information',
            'sort_order' => 100,
            'visible' => true,
            'user_defined' => true,
        ]);

        $this->moduleDataSetup->getConnection()->endSetup();
    }

    /**
     * @return array<int, string>
     */
    public static function getDependencies(): array
    {
        return [];
    }

    /**
     * @return array<int, string>
     */
    public function getAliases(): array
    {
        return [];
    }
}

One important difference from the product attribute

Unlike chapter 19, the scope here is SCOPE_WEBSITE from the start instead of SCOPE_GLOBAL - a deliberate decision, not an oversight. Many Magento shops map different assortments to different websites (e.g. a B2C and a B2B website on the same installation); a bonus that can vary at the website level reflects that reality directly, without chapter 25 having to change anything here after the fact.

group => 'General Information' instead of 'General' is not a typo: the default attribute set for categories actually names its first group differently from the default set for products. A wrong group name doesn't cause an error during the patch run - it just makes Magento create a new, empty group with that name, and the attribute then shows up in an unexpected, usually empty tab in the category form.

Achtung: catalog_category - unlike catalog_product - has no apply_to key; there is only a single "category type". If it's passed in the array anyway, addAttribute() silently ignores it, which easily leads to wrong assumptions while debugging.

Inheritance in the category tree is not an EAV feature

A commonly misunderstood point: a value for loyalty_bonus_category does not automatically "inherit" from a parent category to its subcategories just because both are categories. EAV has no tree-structure logic - each category stores its own, independent value (or NULL if none was set). Actual inheritance would have to be implemented by the calling code itself, for example by walking up the category's path field when a value is missing - a consideration chapter 27 picks back up when it covers conflicts between the category and product bonus.

bin/magento setup:upgrade
bin/magento indexer:reindex catalog_category_flat
bin/magento cache:flush

Tipp: Since loyalty_bonus_category can vary per website, it's worth actually checking the value separately for every relevant website in the admin - the website selector dropdown in the category form only appears once the shop has more than one website configured, and it's easy to overlook on a single-website installation even though the scope is technically set correctly.

With the product and category attributes done, chapter 21 switches entities: the customer gets two new attributes - a points balance and a loyalty tier - and with them the first case in this block that needs a dedicated source model.