Respostas:
Aqui está a abordagem que eu propus para lidar com opções de atributos. Classe auxiliar:
<?php
namespace My\Module\Helper;
class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
/**
* @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface
*/
protected $attributeRepository;
/**
* @var array
*/
protected $attributeValues;
/**
* @var \Magento\Eav\Model\Entity\Attribute\Source\TableFactory
*/
protected $tableFactory;
/**
* @var \Magento\Eav\Api\AttributeOptionManagementInterface
*/
protected $attributeOptionManagement;
/**
* @var \Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory
*/
protected $optionLabelFactory;
/**
* @var \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory
*/
protected $optionFactory;
/**
* Data constructor.
*
* @param \Magento\Framework\App\Helper\Context $context
* @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
* @param \Magento\Eav\Model\Entity\Attribute\Source\TableFactory $tableFactory
* @param \Magento\Eav\Api\AttributeOptionManagementInterface $attributeOptionManagement
* @param \Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory $optionLabelFactory
* @param \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionFactory
*/
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
\Magento\Eav\Model\Entity\Attribute\Source\TableFactory $tableFactory,
\Magento\Eav\Api\AttributeOptionManagementInterface $attributeOptionManagement,
\Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory $optionLabelFactory,
\Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionFactory
) {
parent::__construct($context);
$this->attributeRepository = $attributeRepository;
$this->tableFactory = $tableFactory;
$this->attributeOptionManagement = $attributeOptionManagement;
$this->optionLabelFactory = $optionLabelFactory;
$this->optionFactory = $optionFactory;
}
/**
* Get attribute by code.
*
* @param string $attributeCode
* @return \Magento\Catalog\Api\Data\ProductAttributeInterface
*/
public function getAttribute($attributeCode)
{
return $this->attributeRepository->get($attributeCode);
}
/**
* Find or create a matching attribute option
*
* @param string $attributeCode Attribute the option should exist in
* @param string $label Label to find or add
* @return int
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function createOrGetId($attributeCode, $label)
{
if (strlen($label) < 1) {
throw new \Magento\Framework\Exception\LocalizedException(
__('Label for %1 must not be empty.', $attributeCode)
);
}
// Does it already exist?
$optionId = $this->getOptionId($attributeCode, $label);
if (!$optionId) {
// If no, add it.
/** @var \Magento\Eav\Model\Entity\Attribute\OptionLabel $optionLabel */
$optionLabel = $this->optionLabelFactory->create();
$optionLabel->setStoreId(0);
$optionLabel->setLabel($label);
$option = $this->optionFactory->create();
$option->setLabel($optionLabel);
$option->setStoreLabels([$optionLabel]);
$option->setSortOrder(0);
$option->setIsDefault(false);
$this->attributeOptionManagement->add(
\Magento\Catalog\Model\Product::ENTITY,
$this->getAttribute($attributeCode)->getAttributeId(),
$option
);
// Get the inserted ID. Should be returned from the installer, but it isn't.
$optionId = $this->getOptionId($attributeCode, $label, true);
}
return $optionId;
}
/**
* Find the ID of an option matching $label, if any.
*
* @param string $attributeCode Attribute code
* @param string $label Label to find
* @param bool $force If true, will fetch the options even if they're already cached.
* @return int|false
*/
public function getOptionId($attributeCode, $label, $force = false)
{
/** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
$attribute = $this->getAttribute($attributeCode);
// Build option array if necessary
if ($force === true || !isset($this->attributeValues[ $attribute->getAttributeId() ])) {
$this->attributeValues[ $attribute->getAttributeId() ] = [];
// We have to generate a new sourceModel instance each time through to prevent it from
// referencing its _options cache. No other way to get it to pick up newly-added values.
/** @var \Magento\Eav\Model\Entity\Attribute\Source\Table $sourceModel */
$sourceModel = $this->tableFactory->create();
$sourceModel->setAttribute($attribute);
foreach ($sourceModel->getAllOptions() as $option) {
$this->attributeValues[ $attribute->getAttributeId() ][ $option['label'] ] = $option['value'];
}
}
// Return option ID if exists
if (isset($this->attributeValues[ $attribute->getAttributeId() ][ $label ])) {
return $this->attributeValues[ $attribute->getAttributeId() ][ $label ];
}
// Return false if does not exist
return false;
}
}
Em seguida, na mesma classe ou incluindo-o via injeção de dependência, você pode adicionar ou obter o ID da opção ligando createOrGetId($attributeCode, $label)
.
Por exemplo, se você injetar My\Module\Helper\Data
como $this->moduleHelper
, poderá chamar:
$manufacturerId = $this->moduleHelper->createOrGetId('manufacturer', 'ABC Corp');
Se 'ABC Corp' for um fabricante existente, ele puxará o ID. Caso contrário, ele será adicionado.
ATUALIZADO 09-09-2016: Per Ruud N., a solução original usou o CatalogSetup, que resultou em um bug começando no Magento 2.1. Esta solução revisada ignora esse modelo, criando a opção e o rótulo explicitamente. Deve funcionar em 2.0+.
Magento\Eav\Model\ResourceModel\Entity\Attribute::_processAttributeOptions
. Veja você mesmo, se você remover a $option->setValue($label);
instrução do seu código, ela salvará a opção; quando você a buscar, o Magento retornará o valor de um incremento automático na eav_attribute_option
tabela.
testado no Magento 2.1.3.
Não encontrei nenhuma maneira viável de criar atributos com opções de uma só vez. Então, inicialmente precisamos criar um atributo e, em seguida, adicionar opções para ele.
Injete a seguinte classe \ Magento \ Eav \ Setup \ EavSetupFactory
$setup->startSetup();
/** @var \Magento\Eav\Setup\EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
$eavSetup->addAttribute(
'catalog_product',
$attributeCode,
[
'type' => 'varchar',
'input' => 'select',
'required' => false,
...
],
);
A função addAttribute
não retorna nada útil que possa ser usado no futuro. Então, após a criação do atributo, precisamos recuperar o objeto de atributo por nós mesmos. !!! Importante Precisamos disso porque a função espera apenas attribute_id
, mas não deseja trabalhar attribute_code
.
Nesse caso, precisamos obtê-lo attribute_id
e passá-lo para atribuir a função de criação.
$attributeId = $eavSetup->getAttributeId('catalog_product', 'attribute_code');
Então precisamos gerar uma matriz de opções da maneira que o magento espera:
$options = [
'values' => [
'sort_order1' => 'title1',
'sort_order2' => 'title2',
'sort_order3' => 'title3',
],
'attribute_id' => 'some_id',
];
Como exemplo:
$options = [
'values' => [
'1' => 'Red',
'2' => 'Yellow',
'3' => 'Green',
],
'attribute_id' => '32',
];
E passe-o para funcionar:
$eavSetup->addAttributeOption($options);
O uso do Magento \ Eav \ Setup \ EavSetupFactory ou mesmo da classe \ Magento \ Catalog \ Setup \ CategorySetupFactory pode levar ao seguinte problema: https://github.com/magento/magento2/issues/4896 .
As classes que você deve usar:
protected $_logger;
protected $_attributeRepository;
protected $_attributeOptionManagement;
protected $_option;
protected $_attributeOptionLabel;
public function __construct(
\Psr\Log\LoggerInterface $logger,
\Magento\Eav\Model\AttributeRepository $attributeRepository,
\Magento\Eav\Api\AttributeOptionManagementInterface $attributeOptionManagement,
\Magento\Eav\Api\Data\AttributeOptionLabelInterface $attributeOptionLabel,
\Magento\Eav\Model\Entity\Attribute\Option $option
){
$this->_logger = $logger;
$this->_attributeRepository = $attributeRepository;
$this->_attributeOptionManagement = $attributeOptionManagement;
$this->_option = $option;
$this->_attributeOptionLabel = $attributeOptionLabel;
}
Em sua função, faça algo assim:
$attribute_id = $this->_attributeRepository->get('catalog_product', 'your_attribute')->getAttributeId();
$options = $this->_attributeOptionManagement->getItems('catalog_product', $attribute_id);
/* if attribute option already exists, remove it */
foreach($options as $option) {
if ($option->getLabel() == $oldname) {
$this->_attributeOptionManagement->delete('catalog_product', $attribute_id, $option->getValue());
}
}
/* new attribute option */
$this->_option->setValue($name);
$this->_attributeOptionLabel->setStoreId(0);
$this->_attributeOptionLabel->setLabel($name);
$this->_option->setLabel($this->_attributeOptionLabel);
$this->_option->setStoreLabels([$this->_attributeOptionLabel]);
$this->_option->setSortOrder(0);
$this->_option->setIsDefault(false);
$this->_attributeOptionManagement->add('catalog_product', $attribute_id, $this->_option);
$attributeOptionLabel
e $option
são classes ORM; você não deve injetá-los diretamente. A abordagem adequada é injetar sua classe de fábrica e criar uma instância, conforme necessário. Observe também que você não está usando as interfaces de dados da API de forma consistente.
$option->setValue()
pois isso é para um option_id
campo magento interno na eav_attribute_option
mesa.
Para o Magento 2.3.3, descobri que você pode adotar a abordagem Magento DevTeam.
bin/magento setup:db-declaration:generate-patch Vendor_Module PatchName
public function __construct( ModuleDataSetupInterface $moduleDataSetup, Factory $configFactory CategorySetupFactory $categorySetupFactory ) { $this->moduleDataSetup = $moduleDataSetup; $this->configFactory = $configFactory; $this->categorySetupFactory = $categorySetupFactory; }
Adicionar atributo na função apply ()
public function apply()
{
$categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]);
$categorySetup->addAttribute(
\Magento\Catalog\Model\Product::ENTITY,
'custom_layout',
[
'type' => 'varchar',
'label' => 'New Layout',
'input' => 'select',
'source' => \Magento\Catalog\Model\Product\Attribute\Source\Layout::class,
'required' => false,
'sort_order' => 50,
'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE,
'group' => 'Schedule Design Update',
'is_used_in_grid' => true,
'is_visible_in_grid' => false,
'is_filterable_in_grid' => false
]
);
}
Ele pressupõe que você tenha acesso ao Magento Backend usando o navegador e esteja na página de atributos de edição (o URL se parece com admin / catalog / product_attribute / edit / attribute_id / XXX / key ..)
Vá para o console do navegador (CTRL + SHIFT + J no chrome) e cole o código a seguir depois de alterar o mimim da matriz .
$jq=new jQuery.noConflict();
var mimim=["xxx","yyy","VALUES TO BE ADDED"];
$jq.each(mimim,function(a,b){
$jq("#add_new_option_button").click();
$jq("#manage-options-panel tbody tr:last-child td:nth-child(3) input").val(b);
});
- testado no Magento 2.2.2
Artigo detalhado - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/