Magento2 - programmatically add product attribute options
What is the right(official) way to programmatically add product attribute option in M2?
E.g. for manufacturer
product attribute. Obviously existing option would be matched by "Admin" title value.
magento2
add a comment |
What is the right(official) way to programmatically add product attribute option in M2?
E.g. for manufacturer
product attribute. Obviously existing option would be matched by "Admin" title value.
magento2
add a comment |
What is the right(official) way to programmatically add product attribute option in M2?
E.g. for manufacturer
product attribute. Obviously existing option would be matched by "Admin" title value.
magento2
What is the right(official) way to programmatically add product attribute option in M2?
E.g. for manufacturer
product attribute. Obviously existing option would be matched by "Admin" title value.
magento2
magento2
asked Feb 29 '16 at 13:00
werdwerd
255147
255147
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
Here's the approach I've come up with for handling attribute options. Helper class:
<?php
namespace MyModuleHelper;
class Data extends MagentoFrameworkAppHelperAbstractHelper
{
/**
* @var MagentoCatalogApiProductAttributeRepositoryInterface
*/
protected $attributeRepository;
/**
* @var array
*/
protected $attributeValues;
/**
* @var MagentoEavModelEntityAttributeSourceTableFactory
*/
protected $tableFactory;
/**
* @var MagentoEavApiAttributeOptionManagementInterface
*/
protected $attributeOptionManagement;
/**
* @var MagentoEavApiDataAttributeOptionLabelInterfaceFactory
*/
protected $optionLabelFactory;
/**
* @var MagentoEavApiDataAttributeOptionInterfaceFactory
*/
protected $optionFactory;
/**
* Data constructor.
*
* @param MagentoFrameworkAppHelperContext $context
* @param MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository
* @param MagentoEavModelEntityAttributeSourceTableFactory $tableFactory
* @param MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement
* @param MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory
* @param MagentoEavApiDataAttributeOptionInterfaceFactory $optionFactory
*/
public function __construct(
MagentoFrameworkAppHelperContext $context,
MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository,
MagentoEavModelEntityAttributeSourceTableFactory $tableFactory,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory,
MagentoEavApiDataAttributeOptionInterfaceFactory $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 MagentoCatalogApiDataProductAttributeInterface
*/
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 MagentoFrameworkExceptionLocalizedException
*/
public function createOrGetId($attributeCode, $label)
{
if (strlen($label) < 1) {
throw new MagentoFrameworkExceptionLocalizedException(
__('Label for %1 must not be empty.', $attributeCode)
);
}
// Does it already exist?
$optionId = $this->getOptionId($attributeCode, $label);
if (!$optionId) {
// If no, add it.
/** @var MagentoEavModelEntityAttributeOptionLabel $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(
MagentoCatalogModelProduct::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 MagentoCatalogModelResourceModelEavAttribute $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 MagentoEavModelEntityAttributeSourceTable $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;
}
}
Then, either in the same class or including it via dependency injection, you can add or get your option ID by calling createOrGetId($attributeCode, $label)
.
For example, if you inject MyModuleHelperData
as $this->moduleHelper
, then you can call:
$manufacturerId = $this->moduleHelper->createOrGetId('manufacturer', 'ABC Corp');
If 'ABC Corp' is an existing manufacturer, it will pull the ID. If not, it will add it.
UPDATED 2016-09-09: Per Ruud N., the original solution used CatalogSetup, which resulted in a bug starting in Magento 2.1. This revised solution bypasses that model, creating the option and label explicitly. It should work on 2.0+.
Thanks! I guess this is not the "official" way of solving the issue, right? However I was not able to find any actual reference to product attribute option management in Magento2 source itself.
– werd
Feb 29 '16 at 15:27
2
It's as official as you're going to get. All of the lookups and option adding go through core Magento. My class is just a wrapper for those core methods that makes them easier to use.
– Ryan Hoerr
Feb 29 '16 at 15:35
Thanks Ryan H. it's very useful piece of code. Actually I have a question. If option is added to store with id 0 (admin) it will be rendered with the same label in front in store f.e. with id 1. What can I do to change label for frontend? Just1 => 'something else'
? If I have done it adopting this tutorial:http://magentorex.com/magento-get-product-attributes-option-id-from-option-label/
But function getting option id, only returns ids of attributes for store 1 (frontend) and not for admin.
– Bartosz Kubicki
Jun 17 '16 at 17:48
1
Hi Ryan, you shouldn't set the value on the option, this is the internal id magento uses and I found out the hard way that if you set the value to a string value with a leading number like '123 abc corp' it causes some serious problems due to the implementation ofMagentoEavModelResourceModelEntityAttribute::_processAttributeOptions
. See for yourself, if you remove the$option->setValue($label);
statement from your code, it will save the option, then when you fetch it Magento will return the value from an auto-increment on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:37
1
if I add this in a foreach function, in the second iteration I will get the error "MagentoEavModelEntityAttributeOptionManagement::setOptionValue() must be of the type string, object given"
– JELLEJ
Dec 12 '18 at 14:57
|
show 7 more comments
Using the MagentoEavSetupEavSetupFactory or even the MagentoCatalogSetupCategorySetupFactory class may lead to the following problem: https://github.com/magento/magento2/issues/4896.
The classes you should use:
protected $_logger;
protected $_attributeRepository;
protected $_attributeOptionManagement;
protected $_option;
protected $_attributeOptionLabel;
public function __construct(
PsrLogLoggerInterface $logger,
MagentoEavModelAttributeRepository $attributeRepository,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterface $attributeOptionLabel,
MagentoEavModelEntityAttributeOption $option
){
$this->_logger = $logger;
$this->_attributeRepository = $attributeRepository;
$this->_attributeOptionManagement = $attributeOptionManagement;
$this->_option = $option;
$this->_attributeOptionLabel = $attributeOptionLabel;
}
Then in your function do something like this:
$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);
1
Thanks, you are correct. I've updated my answer accordingly. Note that$attributeOptionLabel
and$option
are ORM classes; you should not inject them directly. The proper approach is to inject their factory class, then create an instance as needed. Also note you aren't using the API data interfaces consistently.
– Ryan Hoerr
Sep 9 '16 at 14:55
3
Hi @Rudd, see my comment on Ryan's answer. You don't want to call$option->setValue()
as that is for an internal magentooption_id
field on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:42
Thank you. That's what I found out too. Will edit my answer accordingly.
– Ruud N.
Sep 14 '16 at 5:49
add a comment |
tested on Magento 2.1.3.
I didn't find any workable way how to create attribute with options at once. So initially we need to create an attribute and then add options for it.
Inject following class MagentoEavSetupEavSetupFactory
$setup->startSetup();
/** @var MagentoEavSetupEavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
Create new attribute:
$eavSetup->addAttribute(
'catalog_product',
$attributeCode,
[
'type' => 'varchar',
'input' => 'select',
'required' => false,
...
],
);
Add custom options.
Function addAttribute
doesn't return anything useful which can be used in future. So after attribute creation we need to retrieve attribute object by ourself. !!!Important We need it because function expects only attribute_id
, but don't want to work with attribute_code
.
In that case we need to get attribute_id
and pass it to attribute creation function.
$attributeId = $eavSetup->getAttributeId('catalog_product', 'attribute_code');
Then we need to generate options array in the way magento expects:
$options = [
'values' => [
'sort_order1' => 'title1',
'sort_order2' => 'title2',
'sort_order3' => 'title3',
],
'attribute_id' => 'some_id',
];
As example:
$options = [
'values' => [
'1' => 'Red',
'2' => 'Yellow',
'3' => 'Green',
],
'attribute_id' => '32',
];
And pass it to function:
$eavSetup->addAttributeOption($options);
add a comment |
This is NOT an answer. Just a workaround.
It assumes that you have access to Magento Backend using browser and you are on the edit attribute page (url looks like admin/catalog/product_attribute/edit/attribute_id/XXX/key..)
Go to Browser console (CTRL + SHIFT + J on chrome)
and paste the following code after changing the array mimim.
$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);
});
-- tested on Magento 2.2.2
Detailed article - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/
1
This is a terrible long term solution. You can't reliably expect those selectors to stay the same. This is a workaround at best, if it actually works as expected.
– domdambrogia
Apr 21 '18 at 0:24
@domdambrogia agree. It is a workaround.
– th3pirat3
Apr 23 '18 at 17:26
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "479"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmagento.stackexchange.com%2fquestions%2f103934%2fmagento2-programmatically-add-product-attribute-options%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Here's the approach I've come up with for handling attribute options. Helper class:
<?php
namespace MyModuleHelper;
class Data extends MagentoFrameworkAppHelperAbstractHelper
{
/**
* @var MagentoCatalogApiProductAttributeRepositoryInterface
*/
protected $attributeRepository;
/**
* @var array
*/
protected $attributeValues;
/**
* @var MagentoEavModelEntityAttributeSourceTableFactory
*/
protected $tableFactory;
/**
* @var MagentoEavApiAttributeOptionManagementInterface
*/
protected $attributeOptionManagement;
/**
* @var MagentoEavApiDataAttributeOptionLabelInterfaceFactory
*/
protected $optionLabelFactory;
/**
* @var MagentoEavApiDataAttributeOptionInterfaceFactory
*/
protected $optionFactory;
/**
* Data constructor.
*
* @param MagentoFrameworkAppHelperContext $context
* @param MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository
* @param MagentoEavModelEntityAttributeSourceTableFactory $tableFactory
* @param MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement
* @param MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory
* @param MagentoEavApiDataAttributeOptionInterfaceFactory $optionFactory
*/
public function __construct(
MagentoFrameworkAppHelperContext $context,
MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository,
MagentoEavModelEntityAttributeSourceTableFactory $tableFactory,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory,
MagentoEavApiDataAttributeOptionInterfaceFactory $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 MagentoCatalogApiDataProductAttributeInterface
*/
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 MagentoFrameworkExceptionLocalizedException
*/
public function createOrGetId($attributeCode, $label)
{
if (strlen($label) < 1) {
throw new MagentoFrameworkExceptionLocalizedException(
__('Label for %1 must not be empty.', $attributeCode)
);
}
// Does it already exist?
$optionId = $this->getOptionId($attributeCode, $label);
if (!$optionId) {
// If no, add it.
/** @var MagentoEavModelEntityAttributeOptionLabel $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(
MagentoCatalogModelProduct::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 MagentoCatalogModelResourceModelEavAttribute $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 MagentoEavModelEntityAttributeSourceTable $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;
}
}
Then, either in the same class or including it via dependency injection, you can add or get your option ID by calling createOrGetId($attributeCode, $label)
.
For example, if you inject MyModuleHelperData
as $this->moduleHelper
, then you can call:
$manufacturerId = $this->moduleHelper->createOrGetId('manufacturer', 'ABC Corp');
If 'ABC Corp' is an existing manufacturer, it will pull the ID. If not, it will add it.
UPDATED 2016-09-09: Per Ruud N., the original solution used CatalogSetup, which resulted in a bug starting in Magento 2.1. This revised solution bypasses that model, creating the option and label explicitly. It should work on 2.0+.
Thanks! I guess this is not the "official" way of solving the issue, right? However I was not able to find any actual reference to product attribute option management in Magento2 source itself.
– werd
Feb 29 '16 at 15:27
2
It's as official as you're going to get. All of the lookups and option adding go through core Magento. My class is just a wrapper for those core methods that makes them easier to use.
– Ryan Hoerr
Feb 29 '16 at 15:35
Thanks Ryan H. it's very useful piece of code. Actually I have a question. If option is added to store with id 0 (admin) it will be rendered with the same label in front in store f.e. with id 1. What can I do to change label for frontend? Just1 => 'something else'
? If I have done it adopting this tutorial:http://magentorex.com/magento-get-product-attributes-option-id-from-option-label/
But function getting option id, only returns ids of attributes for store 1 (frontend) and not for admin.
– Bartosz Kubicki
Jun 17 '16 at 17:48
1
Hi Ryan, you shouldn't set the value on the option, this is the internal id magento uses and I found out the hard way that if you set the value to a string value with a leading number like '123 abc corp' it causes some serious problems due to the implementation ofMagentoEavModelResourceModelEntityAttribute::_processAttributeOptions
. See for yourself, if you remove the$option->setValue($label);
statement from your code, it will save the option, then when you fetch it Magento will return the value from an auto-increment on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:37
1
if I add this in a foreach function, in the second iteration I will get the error "MagentoEavModelEntityAttributeOptionManagement::setOptionValue() must be of the type string, object given"
– JELLEJ
Dec 12 '18 at 14:57
|
show 7 more comments
Here's the approach I've come up with for handling attribute options. Helper class:
<?php
namespace MyModuleHelper;
class Data extends MagentoFrameworkAppHelperAbstractHelper
{
/**
* @var MagentoCatalogApiProductAttributeRepositoryInterface
*/
protected $attributeRepository;
/**
* @var array
*/
protected $attributeValues;
/**
* @var MagentoEavModelEntityAttributeSourceTableFactory
*/
protected $tableFactory;
/**
* @var MagentoEavApiAttributeOptionManagementInterface
*/
protected $attributeOptionManagement;
/**
* @var MagentoEavApiDataAttributeOptionLabelInterfaceFactory
*/
protected $optionLabelFactory;
/**
* @var MagentoEavApiDataAttributeOptionInterfaceFactory
*/
protected $optionFactory;
/**
* Data constructor.
*
* @param MagentoFrameworkAppHelperContext $context
* @param MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository
* @param MagentoEavModelEntityAttributeSourceTableFactory $tableFactory
* @param MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement
* @param MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory
* @param MagentoEavApiDataAttributeOptionInterfaceFactory $optionFactory
*/
public function __construct(
MagentoFrameworkAppHelperContext $context,
MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository,
MagentoEavModelEntityAttributeSourceTableFactory $tableFactory,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory,
MagentoEavApiDataAttributeOptionInterfaceFactory $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 MagentoCatalogApiDataProductAttributeInterface
*/
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 MagentoFrameworkExceptionLocalizedException
*/
public function createOrGetId($attributeCode, $label)
{
if (strlen($label) < 1) {
throw new MagentoFrameworkExceptionLocalizedException(
__('Label for %1 must not be empty.', $attributeCode)
);
}
// Does it already exist?
$optionId = $this->getOptionId($attributeCode, $label);
if (!$optionId) {
// If no, add it.
/** @var MagentoEavModelEntityAttributeOptionLabel $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(
MagentoCatalogModelProduct::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 MagentoCatalogModelResourceModelEavAttribute $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 MagentoEavModelEntityAttributeSourceTable $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;
}
}
Then, either in the same class or including it via dependency injection, you can add or get your option ID by calling createOrGetId($attributeCode, $label)
.
For example, if you inject MyModuleHelperData
as $this->moduleHelper
, then you can call:
$manufacturerId = $this->moduleHelper->createOrGetId('manufacturer', 'ABC Corp');
If 'ABC Corp' is an existing manufacturer, it will pull the ID. If not, it will add it.
UPDATED 2016-09-09: Per Ruud N., the original solution used CatalogSetup, which resulted in a bug starting in Magento 2.1. This revised solution bypasses that model, creating the option and label explicitly. It should work on 2.0+.
Thanks! I guess this is not the "official" way of solving the issue, right? However I was not able to find any actual reference to product attribute option management in Magento2 source itself.
– werd
Feb 29 '16 at 15:27
2
It's as official as you're going to get. All of the lookups and option adding go through core Magento. My class is just a wrapper for those core methods that makes them easier to use.
– Ryan Hoerr
Feb 29 '16 at 15:35
Thanks Ryan H. it's very useful piece of code. Actually I have a question. If option is added to store with id 0 (admin) it will be rendered with the same label in front in store f.e. with id 1. What can I do to change label for frontend? Just1 => 'something else'
? If I have done it adopting this tutorial:http://magentorex.com/magento-get-product-attributes-option-id-from-option-label/
But function getting option id, only returns ids of attributes for store 1 (frontend) and not for admin.
– Bartosz Kubicki
Jun 17 '16 at 17:48
1
Hi Ryan, you shouldn't set the value on the option, this is the internal id magento uses and I found out the hard way that if you set the value to a string value with a leading number like '123 abc corp' it causes some serious problems due to the implementation ofMagentoEavModelResourceModelEntityAttribute::_processAttributeOptions
. See for yourself, if you remove the$option->setValue($label);
statement from your code, it will save the option, then when you fetch it Magento will return the value from an auto-increment on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:37
1
if I add this in a foreach function, in the second iteration I will get the error "MagentoEavModelEntityAttributeOptionManagement::setOptionValue() must be of the type string, object given"
– JELLEJ
Dec 12 '18 at 14:57
|
show 7 more comments
Here's the approach I've come up with for handling attribute options. Helper class:
<?php
namespace MyModuleHelper;
class Data extends MagentoFrameworkAppHelperAbstractHelper
{
/**
* @var MagentoCatalogApiProductAttributeRepositoryInterface
*/
protected $attributeRepository;
/**
* @var array
*/
protected $attributeValues;
/**
* @var MagentoEavModelEntityAttributeSourceTableFactory
*/
protected $tableFactory;
/**
* @var MagentoEavApiAttributeOptionManagementInterface
*/
protected $attributeOptionManagement;
/**
* @var MagentoEavApiDataAttributeOptionLabelInterfaceFactory
*/
protected $optionLabelFactory;
/**
* @var MagentoEavApiDataAttributeOptionInterfaceFactory
*/
protected $optionFactory;
/**
* Data constructor.
*
* @param MagentoFrameworkAppHelperContext $context
* @param MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository
* @param MagentoEavModelEntityAttributeSourceTableFactory $tableFactory
* @param MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement
* @param MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory
* @param MagentoEavApiDataAttributeOptionInterfaceFactory $optionFactory
*/
public function __construct(
MagentoFrameworkAppHelperContext $context,
MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository,
MagentoEavModelEntityAttributeSourceTableFactory $tableFactory,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory,
MagentoEavApiDataAttributeOptionInterfaceFactory $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 MagentoCatalogApiDataProductAttributeInterface
*/
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 MagentoFrameworkExceptionLocalizedException
*/
public function createOrGetId($attributeCode, $label)
{
if (strlen($label) < 1) {
throw new MagentoFrameworkExceptionLocalizedException(
__('Label for %1 must not be empty.', $attributeCode)
);
}
// Does it already exist?
$optionId = $this->getOptionId($attributeCode, $label);
if (!$optionId) {
// If no, add it.
/** @var MagentoEavModelEntityAttributeOptionLabel $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(
MagentoCatalogModelProduct::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 MagentoCatalogModelResourceModelEavAttribute $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 MagentoEavModelEntityAttributeSourceTable $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;
}
}
Then, either in the same class or including it via dependency injection, you can add or get your option ID by calling createOrGetId($attributeCode, $label)
.
For example, if you inject MyModuleHelperData
as $this->moduleHelper
, then you can call:
$manufacturerId = $this->moduleHelper->createOrGetId('manufacturer', 'ABC Corp');
If 'ABC Corp' is an existing manufacturer, it will pull the ID. If not, it will add it.
UPDATED 2016-09-09: Per Ruud N., the original solution used CatalogSetup, which resulted in a bug starting in Magento 2.1. This revised solution bypasses that model, creating the option and label explicitly. It should work on 2.0+.
Here's the approach I've come up with for handling attribute options. Helper class:
<?php
namespace MyModuleHelper;
class Data extends MagentoFrameworkAppHelperAbstractHelper
{
/**
* @var MagentoCatalogApiProductAttributeRepositoryInterface
*/
protected $attributeRepository;
/**
* @var array
*/
protected $attributeValues;
/**
* @var MagentoEavModelEntityAttributeSourceTableFactory
*/
protected $tableFactory;
/**
* @var MagentoEavApiAttributeOptionManagementInterface
*/
protected $attributeOptionManagement;
/**
* @var MagentoEavApiDataAttributeOptionLabelInterfaceFactory
*/
protected $optionLabelFactory;
/**
* @var MagentoEavApiDataAttributeOptionInterfaceFactory
*/
protected $optionFactory;
/**
* Data constructor.
*
* @param MagentoFrameworkAppHelperContext $context
* @param MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository
* @param MagentoEavModelEntityAttributeSourceTableFactory $tableFactory
* @param MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement
* @param MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory
* @param MagentoEavApiDataAttributeOptionInterfaceFactory $optionFactory
*/
public function __construct(
MagentoFrameworkAppHelperContext $context,
MagentoCatalogApiProductAttributeRepositoryInterface $attributeRepository,
MagentoEavModelEntityAttributeSourceTableFactory $tableFactory,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterfaceFactory $optionLabelFactory,
MagentoEavApiDataAttributeOptionInterfaceFactory $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 MagentoCatalogApiDataProductAttributeInterface
*/
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 MagentoFrameworkExceptionLocalizedException
*/
public function createOrGetId($attributeCode, $label)
{
if (strlen($label) < 1) {
throw new MagentoFrameworkExceptionLocalizedException(
__('Label for %1 must not be empty.', $attributeCode)
);
}
// Does it already exist?
$optionId = $this->getOptionId($attributeCode, $label);
if (!$optionId) {
// If no, add it.
/** @var MagentoEavModelEntityAttributeOptionLabel $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(
MagentoCatalogModelProduct::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 MagentoCatalogModelResourceModelEavAttribute $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 MagentoEavModelEntityAttributeSourceTable $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;
}
}
Then, either in the same class or including it via dependency injection, you can add or get your option ID by calling createOrGetId($attributeCode, $label)
.
For example, if you inject MyModuleHelperData
as $this->moduleHelper
, then you can call:
$manufacturerId = $this->moduleHelper->createOrGetId('manufacturer', 'ABC Corp');
If 'ABC Corp' is an existing manufacturer, it will pull the ID. If not, it will add it.
UPDATED 2016-09-09: Per Ruud N., the original solution used CatalogSetup, which resulted in a bug starting in Magento 2.1. This revised solution bypasses that model, creating the option and label explicitly. It should work on 2.0+.
edited Sep 12 '16 at 20:35
answered Feb 29 '16 at 14:54
Ryan HoerrRyan Hoerr
8,61953144
8,61953144
Thanks! I guess this is not the "official" way of solving the issue, right? However I was not able to find any actual reference to product attribute option management in Magento2 source itself.
– werd
Feb 29 '16 at 15:27
2
It's as official as you're going to get. All of the lookups and option adding go through core Magento. My class is just a wrapper for those core methods that makes them easier to use.
– Ryan Hoerr
Feb 29 '16 at 15:35
Thanks Ryan H. it's very useful piece of code. Actually I have a question. If option is added to store with id 0 (admin) it will be rendered with the same label in front in store f.e. with id 1. What can I do to change label for frontend? Just1 => 'something else'
? If I have done it adopting this tutorial:http://magentorex.com/magento-get-product-attributes-option-id-from-option-label/
But function getting option id, only returns ids of attributes for store 1 (frontend) and not for admin.
– Bartosz Kubicki
Jun 17 '16 at 17:48
1
Hi Ryan, you shouldn't set the value on the option, this is the internal id magento uses and I found out the hard way that if you set the value to a string value with a leading number like '123 abc corp' it causes some serious problems due to the implementation ofMagentoEavModelResourceModelEntityAttribute::_processAttributeOptions
. See for yourself, if you remove the$option->setValue($label);
statement from your code, it will save the option, then when you fetch it Magento will return the value from an auto-increment on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:37
1
if I add this in a foreach function, in the second iteration I will get the error "MagentoEavModelEntityAttributeOptionManagement::setOptionValue() must be of the type string, object given"
– JELLEJ
Dec 12 '18 at 14:57
|
show 7 more comments
Thanks! I guess this is not the "official" way of solving the issue, right? However I was not able to find any actual reference to product attribute option management in Magento2 source itself.
– werd
Feb 29 '16 at 15:27
2
It's as official as you're going to get. All of the lookups and option adding go through core Magento. My class is just a wrapper for those core methods that makes them easier to use.
– Ryan Hoerr
Feb 29 '16 at 15:35
Thanks Ryan H. it's very useful piece of code. Actually I have a question. If option is added to store with id 0 (admin) it will be rendered with the same label in front in store f.e. with id 1. What can I do to change label for frontend? Just1 => 'something else'
? If I have done it adopting this tutorial:http://magentorex.com/magento-get-product-attributes-option-id-from-option-label/
But function getting option id, only returns ids of attributes for store 1 (frontend) and not for admin.
– Bartosz Kubicki
Jun 17 '16 at 17:48
1
Hi Ryan, you shouldn't set the value on the option, this is the internal id magento uses and I found out the hard way that if you set the value to a string value with a leading number like '123 abc corp' it causes some serious problems due to the implementation ofMagentoEavModelResourceModelEntityAttribute::_processAttributeOptions
. See for yourself, if you remove the$option->setValue($label);
statement from your code, it will save the option, then when you fetch it Magento will return the value from an auto-increment on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:37
1
if I add this in a foreach function, in the second iteration I will get the error "MagentoEavModelEntityAttributeOptionManagement::setOptionValue() must be of the type string, object given"
– JELLEJ
Dec 12 '18 at 14:57
Thanks! I guess this is not the "official" way of solving the issue, right? However I was not able to find any actual reference to product attribute option management in Magento2 source itself.
– werd
Feb 29 '16 at 15:27
Thanks! I guess this is not the "official" way of solving the issue, right? However I was not able to find any actual reference to product attribute option management in Magento2 source itself.
– werd
Feb 29 '16 at 15:27
2
2
It's as official as you're going to get. All of the lookups and option adding go through core Magento. My class is just a wrapper for those core methods that makes them easier to use.
– Ryan Hoerr
Feb 29 '16 at 15:35
It's as official as you're going to get. All of the lookups and option adding go through core Magento. My class is just a wrapper for those core methods that makes them easier to use.
– Ryan Hoerr
Feb 29 '16 at 15:35
Thanks Ryan H. it's very useful piece of code. Actually I have a question. If option is added to store with id 0 (admin) it will be rendered with the same label in front in store f.e. with id 1. What can I do to change label for frontend? Just
1 => 'something else'
? If I have done it adopting this tutorial: http://magentorex.com/magento-get-product-attributes-option-id-from-option-label/
But function getting option id, only returns ids of attributes for store 1 (frontend) and not for admin.– Bartosz Kubicki
Jun 17 '16 at 17:48
Thanks Ryan H. it's very useful piece of code. Actually I have a question. If option is added to store with id 0 (admin) it will be rendered with the same label in front in store f.e. with id 1. What can I do to change label for frontend? Just
1 => 'something else'
? If I have done it adopting this tutorial: http://magentorex.com/magento-get-product-attributes-option-id-from-option-label/
But function getting option id, only returns ids of attributes for store 1 (frontend) and not for admin.– Bartosz Kubicki
Jun 17 '16 at 17:48
1
1
Hi Ryan, you shouldn't set the value on the option, this is the internal id magento uses and I found out the hard way that if you set the value to a string value with a leading number like '123 abc corp' it causes some serious problems due to the implementation of
MagentoEavModelResourceModelEntityAttribute::_processAttributeOptions
. See for yourself, if you remove the $option->setValue($label);
statement from your code, it will save the option, then when you fetch it Magento will return the value from an auto-increment on the eav_attribute_option
table.– quickshiftin
Sep 12 '16 at 19:37
Hi Ryan, you shouldn't set the value on the option, this is the internal id magento uses and I found out the hard way that if you set the value to a string value with a leading number like '123 abc corp' it causes some serious problems due to the implementation of
MagentoEavModelResourceModelEntityAttribute::_processAttributeOptions
. See for yourself, if you remove the $option->setValue($label);
statement from your code, it will save the option, then when you fetch it Magento will return the value from an auto-increment on the eav_attribute_option
table.– quickshiftin
Sep 12 '16 at 19:37
1
1
if I add this in a foreach function, in the second iteration I will get the error "MagentoEavModelEntityAttributeOptionManagement::setOptionValue() must be of the type string, object given"
– JELLEJ
Dec 12 '18 at 14:57
if I add this in a foreach function, in the second iteration I will get the error "MagentoEavModelEntityAttributeOptionManagement::setOptionValue() must be of the type string, object given"
– JELLEJ
Dec 12 '18 at 14:57
|
show 7 more comments
Using the MagentoEavSetupEavSetupFactory or even the MagentoCatalogSetupCategorySetupFactory class may lead to the following problem: https://github.com/magento/magento2/issues/4896.
The classes you should use:
protected $_logger;
protected $_attributeRepository;
protected $_attributeOptionManagement;
protected $_option;
protected $_attributeOptionLabel;
public function __construct(
PsrLogLoggerInterface $logger,
MagentoEavModelAttributeRepository $attributeRepository,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterface $attributeOptionLabel,
MagentoEavModelEntityAttributeOption $option
){
$this->_logger = $logger;
$this->_attributeRepository = $attributeRepository;
$this->_attributeOptionManagement = $attributeOptionManagement;
$this->_option = $option;
$this->_attributeOptionLabel = $attributeOptionLabel;
}
Then in your function do something like this:
$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);
1
Thanks, you are correct. I've updated my answer accordingly. Note that$attributeOptionLabel
and$option
are ORM classes; you should not inject them directly. The proper approach is to inject their factory class, then create an instance as needed. Also note you aren't using the API data interfaces consistently.
– Ryan Hoerr
Sep 9 '16 at 14:55
3
Hi @Rudd, see my comment on Ryan's answer. You don't want to call$option->setValue()
as that is for an internal magentooption_id
field on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:42
Thank you. That's what I found out too. Will edit my answer accordingly.
– Ruud N.
Sep 14 '16 at 5:49
add a comment |
Using the MagentoEavSetupEavSetupFactory or even the MagentoCatalogSetupCategorySetupFactory class may lead to the following problem: https://github.com/magento/magento2/issues/4896.
The classes you should use:
protected $_logger;
protected $_attributeRepository;
protected $_attributeOptionManagement;
protected $_option;
protected $_attributeOptionLabel;
public function __construct(
PsrLogLoggerInterface $logger,
MagentoEavModelAttributeRepository $attributeRepository,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterface $attributeOptionLabel,
MagentoEavModelEntityAttributeOption $option
){
$this->_logger = $logger;
$this->_attributeRepository = $attributeRepository;
$this->_attributeOptionManagement = $attributeOptionManagement;
$this->_option = $option;
$this->_attributeOptionLabel = $attributeOptionLabel;
}
Then in your function do something like this:
$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);
1
Thanks, you are correct. I've updated my answer accordingly. Note that$attributeOptionLabel
and$option
are ORM classes; you should not inject them directly. The proper approach is to inject their factory class, then create an instance as needed. Also note you aren't using the API data interfaces consistently.
– Ryan Hoerr
Sep 9 '16 at 14:55
3
Hi @Rudd, see my comment on Ryan's answer. You don't want to call$option->setValue()
as that is for an internal magentooption_id
field on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:42
Thank you. That's what I found out too. Will edit my answer accordingly.
– Ruud N.
Sep 14 '16 at 5:49
add a comment |
Using the MagentoEavSetupEavSetupFactory or even the MagentoCatalogSetupCategorySetupFactory class may lead to the following problem: https://github.com/magento/magento2/issues/4896.
The classes you should use:
protected $_logger;
protected $_attributeRepository;
protected $_attributeOptionManagement;
protected $_option;
protected $_attributeOptionLabel;
public function __construct(
PsrLogLoggerInterface $logger,
MagentoEavModelAttributeRepository $attributeRepository,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterface $attributeOptionLabel,
MagentoEavModelEntityAttributeOption $option
){
$this->_logger = $logger;
$this->_attributeRepository = $attributeRepository;
$this->_attributeOptionManagement = $attributeOptionManagement;
$this->_option = $option;
$this->_attributeOptionLabel = $attributeOptionLabel;
}
Then in your function do something like this:
$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);
Using the MagentoEavSetupEavSetupFactory or even the MagentoCatalogSetupCategorySetupFactory class may lead to the following problem: https://github.com/magento/magento2/issues/4896.
The classes you should use:
protected $_logger;
protected $_attributeRepository;
protected $_attributeOptionManagement;
protected $_option;
protected $_attributeOptionLabel;
public function __construct(
PsrLogLoggerInterface $logger,
MagentoEavModelAttributeRepository $attributeRepository,
MagentoEavApiAttributeOptionManagementInterface $attributeOptionManagement,
MagentoEavApiDataAttributeOptionLabelInterface $attributeOptionLabel,
MagentoEavModelEntityAttributeOption $option
){
$this->_logger = $logger;
$this->_attributeRepository = $attributeRepository;
$this->_attributeOptionManagement = $attributeOptionManagement;
$this->_option = $option;
$this->_attributeOptionLabel = $attributeOptionLabel;
}
Then in your function do something like this:
$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);
answered Aug 1 '16 at 14:55
Ruud N.Ruud N.
12117
12117
1
Thanks, you are correct. I've updated my answer accordingly. Note that$attributeOptionLabel
and$option
are ORM classes; you should not inject them directly. The proper approach is to inject their factory class, then create an instance as needed. Also note you aren't using the API data interfaces consistently.
– Ryan Hoerr
Sep 9 '16 at 14:55
3
Hi @Rudd, see my comment on Ryan's answer. You don't want to call$option->setValue()
as that is for an internal magentooption_id
field on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:42
Thank you. That's what I found out too. Will edit my answer accordingly.
– Ruud N.
Sep 14 '16 at 5:49
add a comment |
1
Thanks, you are correct. I've updated my answer accordingly. Note that$attributeOptionLabel
and$option
are ORM classes; you should not inject them directly. The proper approach is to inject their factory class, then create an instance as needed. Also note you aren't using the API data interfaces consistently.
– Ryan Hoerr
Sep 9 '16 at 14:55
3
Hi @Rudd, see my comment on Ryan's answer. You don't want to call$option->setValue()
as that is for an internal magentooption_id
field on theeav_attribute_option
table.
– quickshiftin
Sep 12 '16 at 19:42
Thank you. That's what I found out too. Will edit my answer accordingly.
– Ruud N.
Sep 14 '16 at 5:49
1
1
Thanks, you are correct. I've updated my answer accordingly. Note that
$attributeOptionLabel
and $option
are ORM classes; you should not inject them directly. The proper approach is to inject their factory class, then create an instance as needed. Also note you aren't using the API data interfaces consistently.– Ryan Hoerr
Sep 9 '16 at 14:55
Thanks, you are correct. I've updated my answer accordingly. Note that
$attributeOptionLabel
and $option
are ORM classes; you should not inject them directly. The proper approach is to inject their factory class, then create an instance as needed. Also note you aren't using the API data interfaces consistently.– Ryan Hoerr
Sep 9 '16 at 14:55
3
3
Hi @Rudd, see my comment on Ryan's answer. You don't want to call
$option->setValue()
as that is for an internal magento option_id
field on the eav_attribute_option
table.– quickshiftin
Sep 12 '16 at 19:42
Hi @Rudd, see my comment on Ryan's answer. You don't want to call
$option->setValue()
as that is for an internal magento option_id
field on the eav_attribute_option
table.– quickshiftin
Sep 12 '16 at 19:42
Thank you. That's what I found out too. Will edit my answer accordingly.
– Ruud N.
Sep 14 '16 at 5:49
Thank you. That's what I found out too. Will edit my answer accordingly.
– Ruud N.
Sep 14 '16 at 5:49
add a comment |
tested on Magento 2.1.3.
I didn't find any workable way how to create attribute with options at once. So initially we need to create an attribute and then add options for it.
Inject following class MagentoEavSetupEavSetupFactory
$setup->startSetup();
/** @var MagentoEavSetupEavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
Create new attribute:
$eavSetup->addAttribute(
'catalog_product',
$attributeCode,
[
'type' => 'varchar',
'input' => 'select',
'required' => false,
...
],
);
Add custom options.
Function addAttribute
doesn't return anything useful which can be used in future. So after attribute creation we need to retrieve attribute object by ourself. !!!Important We need it because function expects only attribute_id
, but don't want to work with attribute_code
.
In that case we need to get attribute_id
and pass it to attribute creation function.
$attributeId = $eavSetup->getAttributeId('catalog_product', 'attribute_code');
Then we need to generate options array in the way magento expects:
$options = [
'values' => [
'sort_order1' => 'title1',
'sort_order2' => 'title2',
'sort_order3' => 'title3',
],
'attribute_id' => 'some_id',
];
As example:
$options = [
'values' => [
'1' => 'Red',
'2' => 'Yellow',
'3' => 'Green',
],
'attribute_id' => '32',
];
And pass it to function:
$eavSetup->addAttributeOption($options);
add a comment |
tested on Magento 2.1.3.
I didn't find any workable way how to create attribute with options at once. So initially we need to create an attribute and then add options for it.
Inject following class MagentoEavSetupEavSetupFactory
$setup->startSetup();
/** @var MagentoEavSetupEavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
Create new attribute:
$eavSetup->addAttribute(
'catalog_product',
$attributeCode,
[
'type' => 'varchar',
'input' => 'select',
'required' => false,
...
],
);
Add custom options.
Function addAttribute
doesn't return anything useful which can be used in future. So after attribute creation we need to retrieve attribute object by ourself. !!!Important We need it because function expects only attribute_id
, but don't want to work with attribute_code
.
In that case we need to get attribute_id
and pass it to attribute creation function.
$attributeId = $eavSetup->getAttributeId('catalog_product', 'attribute_code');
Then we need to generate options array in the way magento expects:
$options = [
'values' => [
'sort_order1' => 'title1',
'sort_order2' => 'title2',
'sort_order3' => 'title3',
],
'attribute_id' => 'some_id',
];
As example:
$options = [
'values' => [
'1' => 'Red',
'2' => 'Yellow',
'3' => 'Green',
],
'attribute_id' => '32',
];
And pass it to function:
$eavSetup->addAttributeOption($options);
add a comment |
tested on Magento 2.1.3.
I didn't find any workable way how to create attribute with options at once. So initially we need to create an attribute and then add options for it.
Inject following class MagentoEavSetupEavSetupFactory
$setup->startSetup();
/** @var MagentoEavSetupEavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
Create new attribute:
$eavSetup->addAttribute(
'catalog_product',
$attributeCode,
[
'type' => 'varchar',
'input' => 'select',
'required' => false,
...
],
);
Add custom options.
Function addAttribute
doesn't return anything useful which can be used in future. So after attribute creation we need to retrieve attribute object by ourself. !!!Important We need it because function expects only attribute_id
, but don't want to work with attribute_code
.
In that case we need to get attribute_id
and pass it to attribute creation function.
$attributeId = $eavSetup->getAttributeId('catalog_product', 'attribute_code');
Then we need to generate options array in the way magento expects:
$options = [
'values' => [
'sort_order1' => 'title1',
'sort_order2' => 'title2',
'sort_order3' => 'title3',
],
'attribute_id' => 'some_id',
];
As example:
$options = [
'values' => [
'1' => 'Red',
'2' => 'Yellow',
'3' => 'Green',
],
'attribute_id' => '32',
];
And pass it to function:
$eavSetup->addAttributeOption($options);
tested on Magento 2.1.3.
I didn't find any workable way how to create attribute with options at once. So initially we need to create an attribute and then add options for it.
Inject following class MagentoEavSetupEavSetupFactory
$setup->startSetup();
/** @var MagentoEavSetupEavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
Create new attribute:
$eavSetup->addAttribute(
'catalog_product',
$attributeCode,
[
'type' => 'varchar',
'input' => 'select',
'required' => false,
...
],
);
Add custom options.
Function addAttribute
doesn't return anything useful which can be used in future. So after attribute creation we need to retrieve attribute object by ourself. !!!Important We need it because function expects only attribute_id
, but don't want to work with attribute_code
.
In that case we need to get attribute_id
and pass it to attribute creation function.
$attributeId = $eavSetup->getAttributeId('catalog_product', 'attribute_code');
Then we need to generate options array in the way magento expects:
$options = [
'values' => [
'sort_order1' => 'title1',
'sort_order2' => 'title2',
'sort_order3' => 'title3',
],
'attribute_id' => 'some_id',
];
As example:
$options = [
'values' => [
'1' => 'Red',
'2' => 'Yellow',
'3' => 'Green',
],
'attribute_id' => '32',
];
And pass it to function:
$eavSetup->addAttributeOption($options);
answered Feb 23 '17 at 7:46
zhartaunikzhartaunik
2,65511444
2,65511444
add a comment |
add a comment |
This is NOT an answer. Just a workaround.
It assumes that you have access to Magento Backend using browser and you are on the edit attribute page (url looks like admin/catalog/product_attribute/edit/attribute_id/XXX/key..)
Go to Browser console (CTRL + SHIFT + J on chrome)
and paste the following code after changing the array mimim.
$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);
});
-- tested on Magento 2.2.2
Detailed article - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/
1
This is a terrible long term solution. You can't reliably expect those selectors to stay the same. This is a workaround at best, if it actually works as expected.
– domdambrogia
Apr 21 '18 at 0:24
@domdambrogia agree. It is a workaround.
– th3pirat3
Apr 23 '18 at 17:26
add a comment |
This is NOT an answer. Just a workaround.
It assumes that you have access to Magento Backend using browser and you are on the edit attribute page (url looks like admin/catalog/product_attribute/edit/attribute_id/XXX/key..)
Go to Browser console (CTRL + SHIFT + J on chrome)
and paste the following code after changing the array mimim.
$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);
});
-- tested on Magento 2.2.2
Detailed article - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/
1
This is a terrible long term solution. You can't reliably expect those selectors to stay the same. This is a workaround at best, if it actually works as expected.
– domdambrogia
Apr 21 '18 at 0:24
@domdambrogia agree. It is a workaround.
– th3pirat3
Apr 23 '18 at 17:26
add a comment |
This is NOT an answer. Just a workaround.
It assumes that you have access to Magento Backend using browser and you are on the edit attribute page (url looks like admin/catalog/product_attribute/edit/attribute_id/XXX/key..)
Go to Browser console (CTRL + SHIFT + J on chrome)
and paste the following code after changing the array mimim.
$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);
});
-- tested on Magento 2.2.2
Detailed article - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/
This is NOT an answer. Just a workaround.
It assumes that you have access to Magento Backend using browser and you are on the edit attribute page (url looks like admin/catalog/product_attribute/edit/attribute_id/XXX/key..)
Go to Browser console (CTRL + SHIFT + J on chrome)
and paste the following code after changing the array mimim.
$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);
});
-- tested on Magento 2.2.2
Detailed article - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/
edited 9 mins ago
answered Oct 14 '17 at 18:18
th3pirat3th3pirat3
1615
1615
1
This is a terrible long term solution. You can't reliably expect those selectors to stay the same. This is a workaround at best, if it actually works as expected.
– domdambrogia
Apr 21 '18 at 0:24
@domdambrogia agree. It is a workaround.
– th3pirat3
Apr 23 '18 at 17:26
add a comment |
1
This is a terrible long term solution. You can't reliably expect those selectors to stay the same. This is a workaround at best, if it actually works as expected.
– domdambrogia
Apr 21 '18 at 0:24
@domdambrogia agree. It is a workaround.
– th3pirat3
Apr 23 '18 at 17:26
1
1
This is a terrible long term solution. You can't reliably expect those selectors to stay the same. This is a workaround at best, if it actually works as expected.
– domdambrogia
Apr 21 '18 at 0:24
This is a terrible long term solution. You can't reliably expect those selectors to stay the same. This is a workaround at best, if it actually works as expected.
– domdambrogia
Apr 21 '18 at 0:24
@domdambrogia agree. It is a workaround.
– th3pirat3
Apr 23 '18 at 17:26
@domdambrogia agree. It is a workaround.
– th3pirat3
Apr 23 '18 at 17:26
add a comment |
Thanks for contributing an answer to Magento Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmagento.stackexchange.com%2fquestions%2f103934%2fmagento2-programmatically-add-product-attribute-options%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown