Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Magento\Catalog\Api\Data\ProductAttributeInterface;
use Magento\Catalog\Controller\Adminhtml\Product\Attribute;
use Magento\Catalog\Helper\Product;
use Magento\Catalog\Model\Product\Attribute\FilterableAllowedInputTypes;
use Magento\Catalog\Model\Product\Attribute\Frontend\Inputtype\Presentation;
use Magento\Framework\Serialize\Serializer\FormData;
use Magento\Catalog\Model\Product\AttributeSet\BuildFactory;
Expand All @@ -38,41 +39,6 @@
*/
class Save extends Attribute implements HttpPostActionInterface
{
/**
* @var BuildFactory
*/
protected $buildFactory;

/**
* @var FilterManager
*/
protected $filterManager;

/**
* @var Product
*/
protected $productHelper;

/**
* @var AttributeFactory
*/
protected $attributeFactory;

/**
* @var ValidatorFactory
*/
protected $validatorFactory;

/**
* @var CollectionFactory
*/
protected $groupCollectionFactory;

/**
* @var LayoutFactory
*/
private $layoutFactory;

/**
* @var Presentation
*/
Expand All @@ -83,6 +49,11 @@ class Save extends Attribute implements HttpPostActionInterface
*/
private $formDataSerializer;

/**
* @var FilterableAllowedInputTypes
*/
private $filterableAllowedInputTypes;

/**
* @param Context $context
* @param FrontendInterface $attributeLabelCache
Expand All @@ -97,34 +68,31 @@ class Save extends Attribute implements HttpPostActionInterface
* @param LayoutFactory $layoutFactory
* @param Presentation|null $presentation
* @param FormData|null $formDataSerializer
* @param FilterableAllowedInputTypes|null $filterableAllowedInputTypes
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
Context $context,
FrontendInterface $attributeLabelCache,
Registry $coreRegistry,
PageFactory $resultPageFactory,
BuildFactory $buildFactory,
AttributeFactory $attributeFactory,
ValidatorFactory $validatorFactory,
CollectionFactory $groupCollectionFactory,
FilterManager $filterManager,
Product $productHelper,
LayoutFactory $layoutFactory,
protected BuildFactory $buildFactory,
protected AttributeFactory $attributeFactory,
protected ValidatorFactory $validatorFactory,
protected CollectionFactory $groupCollectionFactory,
protected FilterManager $filterManager,
protected Product $productHelper,
private LayoutFactory $layoutFactory,
?Presentation $presentation = null,
?FormData $formDataSerializer = null
?FormData $formDataSerializer = null,
?FilterableAllowedInputTypes $filterableAllowedInputTypes = null
) {
parent::__construct($context, $attributeLabelCache, $coreRegistry, $resultPageFactory);
$this->buildFactory = $buildFactory;
$this->filterManager = $filterManager;
$this->productHelper = $productHelper;
$this->attributeFactory = $attributeFactory;
$this->validatorFactory = $validatorFactory;
$this->groupCollectionFactory = $groupCollectionFactory;
$this->layoutFactory = $layoutFactory;
$this->presentation = $presentation ?: ObjectManager::getInstance()->get(Presentation::class);
$this->formDataSerializer = $formDataSerializer
?: ObjectManager::getInstance()->get(FormData::class);
$this->filterableAllowedInputTypes = $filterableAllowedInputTypes
?: ObjectManager::getInstance()->get(FilterableAllowedInputTypes::class);
}

/**
Expand Down Expand Up @@ -254,6 +222,11 @@ public function execute()

$data += ['is_filterable' => 0, 'is_filterable_in_search' => 0];

$filterableValidationResult = $this->validateFilterableFlags($data, $attributeId);
if ($filterableValidationResult) {
return $filterableValidationResult;
}

$defaultValueField = $model->getDefaultValueByInput($data['frontend_input']);
if ($defaultValueField) {
$data['default_value'] = $this->getRequest()->getParam($defaultValueField);
Expand Down Expand Up @@ -356,6 +329,34 @@ public function execute()
return $this->returnResult('catalog/*/', [], ['error' => true]);
}

/**
* Reject layered-navigation flags for input types the admin form does not allow.
*
* @param array $data
* @param mixed $attributeId
* @return Json|Redirect|null
*/
private function validateFilterableFlags(array $data, $attributeId)
{
if ($this->filterableAllowedInputTypes->isAllowed($data['frontend_input'] ?? null)) {
return null;
}

if ((int)($data['is_filterable'] ?? 0) || (int)($data['is_filterable_in_search'] ?? 0)) {
$this->messageManager->addErrorMessage(
__('Can be used only with catalog input type Yes/No, Dropdown, Multiple Select and Price.')
);
$this->_session->setAttributeData($data);
return $this->returnResult(
'catalog/*/edit',
['attribute_id' => $attributeId, '_current' => true],
['error' => true]
);
}

return null;
}

/**
* Provides an initialized Result object.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
/**
* Copyright 2026 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);

namespace Magento\Catalog\Model\Product\Attribute;

/**
* Catalog input types that may be used in layered navigation.
*/
class FilterableAllowedInputTypes
{
/**
* @var string[]
*/
private array $inputTypes;

/**
* @param string[] $inputTypes
*/
public function __construct(array $inputTypes = [])
{
$this->inputTypes = $inputTypes;
}

/**
* Check whether the catalog input type may be used in layered navigation.
*
* @param mixed $frontendInput
* @return bool
*/
public function isAllowed(mixed $frontendInput): bool
{
return in_array((string)$frontendInput, $this->inputTypes, true);
}
}
96 changes: 36 additions & 60 deletions app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@

use Laminas\Validator\Regex;
use Magento\Catalog\Api\Data\EavAttributeInterface;
use Magento\Catalog\Helper\Product;
use Magento\Catalog\Model\ResourceModel\Attribute as AttributeResource;
use Magento\Eav\Api\AttributeRepositoryInterface;
use Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\ValidatorFactory;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Attribute;
use Magento\Eav\Model\Validator\Attribute\Code;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Filter\FilterManager;

/**
* Product attribute repository
Expand All @@ -18,76 +27,43 @@
*/
class Repository implements \Magento\Catalog\Api\ProductAttributeRepositoryInterface
{
private const FILTERABLE_ALLOWED_INPUT_TYPES = ['date', 'datetime', 'text', 'textarea', 'texteditor'];

/**
* @var \Magento\Catalog\Model\ResourceModel\Attribute
*/
protected $attributeResource;

/**
* @var \Magento\Eav\Model\AttributeRepository
*/
protected $eavAttributeRepository;

/**
* @var \Magento\Eav\Model\Config
*/
protected $eavConfig;

/**
* @var \Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\ValidatorFactory
* @var ValidatorFactory
* @deprecated
* @see $validatorFactory
*/
protected $inputtypeValidatorFactory;

/**
* @var \Magento\Catalog\Helper\Product
*/
protected $productHelper;

/**
* @var \Magento\Framework\Filter\FilterManager
*/
protected $filterManager;

/**
* @var \Magento\Framework\Api\SearchCriteriaBuilder
*/
protected $searchCriteriaBuilder;

/**
* @var \Magento\Eav\Model\Validator\Attribute\Code
* @var FilterableAllowedInputTypes
*/
protected $attributeCodeValidator;
private FilterableAllowedInputTypes $filterableAllowedInputTypes;

/**
* @param \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource
* @param \Magento\Catalog\Helper\Product $productHelper
* @param \Magento\Framework\Filter\FilterManager $filterManager
* @param \Magento\Eav\Api\AttributeRepositoryInterface $eavAttributeRepository
* @param \Magento\Eav\Model\Config $eavConfig
* @param \Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\ValidatorFactory $validatorFactory
* @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
* @param \Magento\Eav\Model\Validator\Attribute\Code $attributeCodeValidator
* @param AttributeResource $attributeResource
* @param Product $productHelper
* @param FilterManager $filterManager
* @param AttributeRepositoryInterface $eavAttributeRepository
* @param Config $eavConfig
* @param ValidatorFactory $validatorFactory
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param Code $attributeCodeValidator
* @param FilterableAllowedInputTypes|null $filterableAllowedInputTypes
*/
public function __construct(
\Magento\Catalog\Model\ResourceModel\Attribute $attributeResource,
\Magento\Catalog\Helper\Product $productHelper,
\Magento\Framework\Filter\FilterManager $filterManager,
\Magento\Eav\Api\AttributeRepositoryInterface $eavAttributeRepository,
\Magento\Eav\Model\Config $eavConfig,
\Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\ValidatorFactory $validatorFactory,
\Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
\Magento\Eav\Model\Validator\Attribute\Code $attributeCodeValidator
protected AttributeResource $attributeResource,
protected Product $productHelper,
protected FilterManager $filterManager,
protected AttributeRepositoryInterface $eavAttributeRepository,
protected Config $eavConfig,
protected ValidatorFactory $validatorFactory,
protected SearchCriteriaBuilder $searchCriteriaBuilder,
protected Code $attributeCodeValidator,
?FilterableAllowedInputTypes $filterableAllowedInputTypes = null
) {
$this->attributeResource = $attributeResource;
$this->productHelper = $productHelper;
$this->filterManager = $filterManager;
$this->eavAttributeRepository = $eavAttributeRepository;
$this->eavConfig = $eavConfig;
$this->inputtypeValidatorFactory = $validatorFactory;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->attributeCodeValidator = $attributeCodeValidator;
$this->filterableAllowedInputTypes = $filterableAllowedInputTypes
?? ObjectManager::getInstance()->get(FilterableAllowedInputTypes::class);
}

/**
Expand Down Expand Up @@ -120,7 +96,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr
*/
public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attribute)
{
if (in_array($attribute->getFrontendInput(), self::FILTERABLE_ALLOWED_INPUT_TYPES)) {
if (!$this->filterableAllowedInputTypes->isAllowed($attribute->getFrontendInput())) {
if ($attribute->getIsFilterable()) {
throw InputException::invalidFieldValue(
EavAttributeInterface::IS_FILTERABLE,
Expand Down Expand Up @@ -312,7 +288,7 @@ protected function validateCode($code)
protected function validateFrontendInput($frontendInput)
{
/** @var \Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\Validator $validator */
$validator = $this->inputtypeValidatorFactory->create();
$validator = $this->validatorFactory->create();
if (!$validator->isValid($frontendInput)) {
throw InputException::invalidFieldValue('frontend_input', $frontendInput);
}
Expand Down
Loading