17

I try to add options for size and color attributes in my importer module but I don't how :

private function addOption($attributeCode, $value)
{
    $ob = $this->_objectManager;      
    /* @var $m \Magento\Eav\Model\Entity\Attribute\OptionManagement */
    $m = $this->optionManagement;
    /* @var $option \Magento\Eav\Model\Entity\Attribute\Option */
    $option = $this->attributeOption;
    
    $option->setLabel($value);      
    $option->setValue($value);
    
    $m->add(\Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE,
            $attributeCode,
            $option);

This report an error (I modified exception reporting on OptionMaganger.php to Exception->message)

Cannot save attribute size Notice: Undefined index: delete in /var/www/html/magento2/vendor/magento/module-swatches/Model/Plugin/EavAttribute.php on line 177

  • The OptionManagement and Option come from _contstructor
  • With OptionManagement I can retrieve the existing items, so should be ok..

setLabel() and setValue() are default, but I tried setData, load option instance and pass by OptionManagement->getItems to add(...) "again", but error still exists...

Any idea, how can I append EAV Options (swatches?) during the import process? (not in module setup)


Update:

Other way I can add option:

$attributeCode = 137; /* on size, 90 on color ... */

$languageValues[0]='Admin Label'; 

$languageValues[1]='Default Store Label - XXXXL';
$ob = $this->_objectManager;

private function addOption($attributeCode,$languageValues){
$ob = $this->_objectManager;
/* @var $attr \Magento\Eav\Model\Entity\Attribute */
$attr = $ob->create('\Magento\Eav\Model\Entity\Attribute'); 
$attr->load($attributeCode); 
$option = []; 
$option['value'][$languageValues[0]] = $languageValues; 
$attr->addData(array('option' => $option));
$attr->save();
}

This way Magento2 can save an option to attribute, but I don't know what is the "official" way :)

1
  • option added any value as string not supported for integer
    – Ajay Patel
    Nov 22, 2017 at 3:38

8 Answers 8

2
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Store\Model\StoreManagerInterface;

declare:

protected $_eavSetupFactory;

constructor :

public function __construct(
    \Magento\Eav\Setup\EavSetupFactory $eavSetupFactory,
    \Magento\Store\Model\StoreManagerInterface $storeManager,
    \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attributeFactory,
    \Magento\Framework\ObjectManagerInterface $objectmanager,
    ModuleDataSetupInterface $setup,
    \Magento\Catalog\Model\ProductFactory $productloader
) {
    $this->_eavSetupFactory = $eavSetupFactory;
    $this->_storeManager = $storeManager;
    $this->_attributeFactory = $attributeFactory;
    $this->_objectManager = $objectmanager;
    $this->setup = $setup;
    $this->_productloader = $productloader;
}

execute methode :

public function execute(EventObserver $observer)
{
    /** @var $brand \Ktpl\Brand\Model\Brand */
    $brand = $observer->getEvent()->getBrand();
    $option_id = "";

    $data = [];
    $attribute_arr = [$brand['brand_id'] => $brand['brand_title']];
    $optionTable = $this->setup->getTable('eav_attribute_option');
    $attributeInfo=$this->_attributeFactory->getCollection()
           ->addFieldToFilter('attribute_code',['eq'=>"shop_by_brand"])
           ->getFirstItem();

    $attribute_id = $attributeInfo->getAttributeId();

    $eavAttribute = $this->_objectManager->create('Magento\Eav\Model\Config');

    $option=array();
    $option['attribute_id'] = $attributeInfo->getAttributeId();
    $option['value'] = array(0=>array()); // 0 means "new option_id", other values like "14" means "update option_id=14" - this array index is casted to integer

    $storeManager = $this->_objectManager->get('Magento\Store\Model\StoreManagerInterface');
    $stores = $storeManager->getStores();
    $storeArray[0] = "All Store Views";       

    foreach ($stores  as $store) {
        $storeArray[$store->getId()] = $store->getName();
    }


    if (empty($brand['optionId'])) {
        foreach($attribute_arr as $key => $value){
            $option['value'][0][0]=$value;
                foreach($storeArray as $storeKey => $store){
                    $option['value'][0][$storeKey] = $value;
                }                
        }
    }
    else
    {
        foreach($attribute_arr as $key => $value){
                foreach($storeArray as $storeKey => $store){
                    $option['value'][$brand['optionId']][$storeKey] = $value;
                }                
        }
    }

    $eavSetup = $this->_eavSetupFactory->create();
    $eavSetup->addAttributeOption($option)

}
5
  • You have serious error in your code:$option['value'][$value][0] - it should not be $value but "option_id" - integer, for new one option set 0. This one is cast to integer, so if you have string with no numer, eg "black" it will be 0 correctly. But if your $value is something like "10 Black" it will cast to 10 and update database entity with option_id = 10 instead creating new one. This may cause serious mess in your shop database. Mar 15, 2018 at 21:28
  • thanks to inform brother. If you found any error than you can update my answer @A.Maksymiuk Mar 16, 2018 at 5:25
  • Did it. Please accept, then I'll revert my downvote. Mar 16, 2018 at 8:41
  • Approved, but down-vote any answer is not the proper way bro, If you think that answer is not related or not as per asked then you can else don't down-vote anyone's answer. @A.Maksymiuk Mar 16, 2018 at 9:12
  • I did it to warn anyone to use this code, because it would cause serious damage in data integrity. E.g. instead adding new option named "42" (size) your script updated option_id = 42 (which was existing option of completly different attribute). Luckily it happend to me on test server and fresh new database. Mar 16, 2018 at 9:20
2

Other way I can add option:

$attributeCode = 137; /* on size, 90 on color ... */

$languageValues[0]='Admin Label'; 

$languageValues[1]='Default Store Label - XXXXL';
$ob = $this->_objectManager;



private function addOption($attributeCode,$languageValues){
$ob = $this->_objectManager;
/* @var $attr \Magento\Eav\Model\Entity\Attribute */
$attr = $ob->create('\Magento\Eav\Model\Entity\Attribute'); 
$attr->load($attributeCode); 
$option = []; 
$option['value'][$languageValues[0]] = $languageValues; 
$attr->addData(array('option' => $option));
$attr->save();
}

This way Magento2 can save an option to attribute, but I dont know what is the "official" way.

3
  • See my way. I believe it to be 'official'
    – CarComp
    Mar 31, 2016 at 16:00
  • your solution is work but only when option are option not work for integer
    – Ajay Patel
    Nov 21, 2017 at 14:05
  • why wouldn't it work for integer values? Dec 8, 2019 at 16:16
1

This is not a clean code but you can use this to add options to attribute.

$attributeCode = 'color';
$value = 'red';
addAttributeOption($attributeCode, $value);

function addAttributeOption($attributeCode, $value)
{

    $objectManagerFactorys = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, []);

    $objectManagers = $objectManagerFactorys->create([]);

    $state = $objectManagers->get('Magento\Framework\App\State');
    $state->setAreaCode('global');

    $optionFactory  = $objectManagers->create('\Magento\Eav\Api\Data\AttributeOptionInterfaceFactory');
    $option = $optionFactory->create();
    $option->setLabel($value);
    $option->setIsDefault(false);

    $attributeOptionManagement = $objectManagers->create('\Magento\Eav\Api\AttributeOptionManagementInterface');
    $attributeOptionManagement->add(
        'catalog_product',
        $attributeCode,
        $option
    );



    return true;
}
1
  • Sometimes you just need a few lines you can't remember, thanks! Dec 23, 2021 at 9:19
0

Seems to be a validation issue. The delete key in data comes from the form in the backend, so try to add an empty delete key that way:

$option->setData('delete','');

It could works.

4
  • Sadly no. The OptionManager add(..) reparse the $option parameter and leave the 'delete' key empty, I don't no why... But I found another way.... Jan 2, 2016 at 15:50
  • Great, please add your solution as an answer to resolve the question. Jan 2, 2016 at 15:59
  • I can't answer my own question, but i updated the question. I think my solution is a workaround... Jan 2, 2016 at 16:14
  • Hey, you can answer your question is pretty common. Jan 2, 2016 at 16:16
0

I ended up rewriting this entire answer using ObjectFactory methods suggested by Ryan H.

It ended up being a Helper Class that utilized some attributes I created on the customer object, but the idea is there on how to utilize EAV + ObjectFactories to manipulate attribute options

<?php


namespace Stti\Healthday\Helper {
    use Magento\Eav\Model\Entity\AttributeFactory;
    use Magento\Eav\Model\Entity\Attribute\OptionFactory;
    use Magento\Eav\Model\Entity\Attribute\OptionManagementFactory;
    use Magento\Framework\App\Helper\AbstractHelper;
    use Magento\Framework\App\Helper\Context;
    use Magento\Eav\Model\Entity\Attribute;
    use Stti\Healthday\Model\RelationFactory;


    /**
     * Eav data helper
     */
    class Data extends AbstractHelper {

        protected $optionFactory;

        protected $attributeFactory;

        protected $relationFactory;

        protected $optionManagementFactory;

        public function __construct(Context $context, AttributeFactory $attributeFactory, OptionFactory $optionFactory,
            RelationFactory $relationFactory,
            OptionManagementFactory $optionManagementFactory) {
            $this->optionFactory = $optionFactory;
            $this->attributeFactory = $attributeFactory;
            $this->optionFactory = $optionFactory;
            $this->relationFactory = $relationFactory;
            $this->optionManagementFactory = $optionManagementFactory;
            parent::__construct($context);
        }

        public function addRelationsHelper($answerJson, $attributeCode) {
            // IMPORTANT: READ THIS OR THE CODE BELOW WONT MAKE SENSE!!!!
            // Since magento's attribute option model was never meant to
            // hold guids, we'll be saving the guid as the label. An option_id will
            // get created, which can then be saved to the relationship table.  Then
            // the label in the attribute_option table can be changed to the actual 'word'
            // by looking up all of the options, matching on the guid, and doing a replace.
            // At that point, there will be a 1:1 relation between guid, option_id, and the 'word'



            // Get the attribute requested
            $attribute = $this->attributeFactory->create();
            $attribute  = $attribute->loadByCode("customer", $attributeCode);

            $answers = json_decode($answerJson, true);

            // Prepare vars
            //$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
            $prekeys = array();
            $prevalues = array();

            foreach ($answers as $answer) {
                $prekeys[] = $answer['Key'];
                $prevalues[] = $answer['Value'];
            }

            // load up all relations
            // generate an array of matching indexes so we can remove
            // them from the array to process
            $collection = $this->relationFactory->create()->getCollection();

            $removalIds = array();
            foreach ($collection as $relation) {
                // if the item is already in the magento relations,
                // don't attempt to add it to the attribute options
                for($cnt = 0; $cnt < sizeof($answers); $cnt++) {
                    if ($relation['stti_guid'] == $prekeys[$cnt]) {
                        $removalIds[] = $cnt;
                    }
                }
            }

            // Remove the values from the arrays we are going to process
            foreach($removalIds as $removalId) {
                unset($prekeys[$removalId]);
                unset($prevalues[$removalId]);
            }

            // "reindex" the arrays
            $keys = array_values($prekeys);
            $values = array_values($prevalues);

            // prepare the array that will be sent into the attribute model to
            // update its option values
            $updates = array();
            $updates['attribute_id'] = $attribute->getId();

            // This section utilizes the DI generated OptionFactory and OptionManagementFactory
            // to add the options to the customer attribute specified in the parameters.
            for($cnt = 0; $cnt < sizeof($keys); $cnt++) {
                $option = $this->optionFactory->create();
                $option->setLabel($keys[$cnt]);
                $this->optionManagementFactory->create()->add("customer", $attributeCode, $option);
            }

            // After save, pull all attribute options, compare to the 'keys' array
            // and create healthday/relation models if needed
            $relationIds = $attribute->getOptions();
            $updatedAttributeCount = 0;
            $options = array();
            $options['value'] = array();

            for($cnt = 0; $cnt < sizeof($keys); $cnt++) {

                $option_id = 0;
                foreach($relationIds as $relationId) {
                    if ($relationId->getLabel() == $keys[$cnt]) {
                        $option_id = $relationId['value'];
                        break;
                    }
                }
                if ($option_id > 0) {
                    // Create the healthday relation utilizing our custom models DI generated ObjectFactories
                    $relation = $this->relationFactory->create();
                    $relation->setAttributeId($attribute->getId());
                    $relation->setOptionId($option_id);
                    $relation->setSttiGuid($keys[$cnt]);
                    $relation->save();

                    // Rename the attribute option value to the 'word'
                    $options['value'][$option_id][] = $values[$cnt];
                    $updatedAttributeCount++;
                }
            }

            if ($updatedAttributeCount > 0) {
                $attribute->setData('option', $options);
                $attribute->save();
            }

            // Save the relationship to the guid
            return $updatedAttributeCount;
        }
    }
}
6
  • 1
    You should be injecting ObjectFactory and create instances of Object from that, not injecting Object itself. ORM objects are not to be injected directly.
    – Ryan Hoerr
    Apr 1, 2016 at 17:04
  • Which ObjectFactory? Theres like 50. I'm looking at \Magento\Framework\Api\ObjectFactory but it just looks like a wrapper for ObjectManager. I'm not sure why I wouldn't just implement the objectmanager itself. Theres SO many wrappers for wrappers of things in this new version.
    – CarComp
    Apr 8, 2016 at 12:56
  • 1
    I was speaking in the abstract. Inject the Factory for your given object, not literally 'ObjectFactory'. You should inject the factory for each specific type you use, and create them as needed. Yes, it seems messy at first... but there are very good reasons for it. Beside, ECG code standards all but prohibit direct use of ObjectManager. See Alan Storm's article explaining the whole subject: alanstorm.com/magento_2_object_manager_instance_objects
    – Ryan Hoerr
    Apr 8, 2016 at 13:05
  • What should I do when the objects I want to use do not have a factory? For instance, I can't find a way to 'factory' the options management stuff. Magento\Eav\Model\AttributeFactory also uses a weird ->createAttribute(vars) instead of just ->create(). I'm just saying, when you don't work with a product, or a category built in stuff, things get a little weird.
    – CarComp
    Apr 8, 2016 at 13:53
  • 1
    Not all objects will require a factory. For ones that do, the factory may not exist out-of-box -- any that don't exist will be created at runtime (with DI generation), or during compilation. Read the article I linked. Any way about it, you need to learn to work with Magento2, not against it. Yes, there is a learning curve. If you don't already, I strongly recommend picking up PhpStorm or a similar IDE.
    – Ryan Hoerr
    Apr 8, 2016 at 14:03
0

Magento 2 add Specific attribute option Value programmatically.

Run this Script in root directory of magento after url.

$objectManager = \Magento\Framework\App\ObjectManager::getInstance(); // instance of object manager
try{
      $entityType = 'catalog_product';
      $attributeCode = 'manufacturer';
      $attributeInfo = $objectManager->get(\Magento\Eav\Model\Entity\Attribute::class)
                                 ->loadByCode($entityType, $attributeCode);


      $attributeFactory = $objectManager->get('\Magento\Catalog\Model\ResourceModel\Eav\Attribute');

      $attributeId = $attributeInfo->getAttributeId();
      //$manufacturer = $item->{'Manufacturer'};
      $attribute_arr = ['aaa','bbb','ccc','ddd'];

      $option = array();
      $option['attribute_id'] = $attributeId;
      foreach($attribute_arr as $key=>$value){
          $option['value'][$value][0]=$value;
          foreach($storeManager as $store){
              $option['value'][$value][$store->getId()] = $value;
          }
      }
      if ($option) {
        $eavSetupFactory = $objectManager->create('\Magento\Eav\Setup\EavSetup');
        print_r($eavSetupFactory->getAttributeOption());
        die();
        $eavSetupFactory->addAttributeOption($option);
      }
    }catch(Exception $e){
      echo $e->getMessage();
    }
0
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
$attribute = $eavSetup->getAttribute(MyModel::ENTITY, "attribute_code");
$option = ["value" => [0 => [0 => "new_attribute"]], "attribute_id" => $attribute["attribute_id"]];
$eavSetup->addAttributeOption($option);
0

UPDATE 2016-09-11: As quickshiftin pointed out, this solution does not work for M2.1+. Attempting to dependency-inject the CategorySetup class outside of setup will give you a fatal error. See here for a more robust solution: https://magento.stackexchange.com/a/103951/1905


Use the \Magento\Catalog\Setup\CategorySetup class for this. It includes an addAttributeOption() method, which works exactly the same way as eav/entity_setup::addAttributeOption() in 1.x. There are some other attribute methods there that might be helpful as well.

You can use dependency injection to get this class at any time, even outside a setup process.

Specifically:

/**
 * Constructor.
 *
 * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
 * @param \Magento\Catalog\Setup\CategorySetupFactory $categorySetupFactory
 */
public function __construct(
    \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
    \Magento\Catalog\Setup\CategorySetupFactory $categorySetupFactory
) {
    $this->attributeRepository = $attributeRepository;
    $this->categorySetupFactory = $categorySetupFactory;
}

/**
 * Create a matching attribute option
 *
 * @param string $attributeCode Attribute the option should exist in
 * @param string $label Label to add
 * @return void
 */
public function addOption($attributeCode, $label) {
    $attribute = $this->attributeRepository->get($attributeCode);
    
    $categorySetup = $this->categorySetupFactory->create();
    $categorySetup->addAttributeOption(
        [
            'attribute_id'  => $attribute->getAttributeId(),
            'order'         => [0],
            'value'         => [
                [
                    0 => $label, // store_id => label
                ],
            ],
        ]
    );
}

If desired, you could eliminate the attributeRepository class and use getAttribute() directly on categorySetup. You'd just have to include the entity type ID each time.

2
  • Hi Ryan, I'm trying to use the CategorySetupFactory to instantiate a CategorySetup from a Console\Command, however when I call $factory->setup() a fatal error occurs: PHP Fatal error: Uncaught TypeError: Argument 1 passed to Magento\Setup\Module\DataSetup::__construct() must be an instance of Magento\Framework\Module\Setup\Context, instance of Magento\Framework\ObjectManager\ObjectManager given Sep 10, 2016 at 21:50
  • Ahh, I've now stumbled upon this thread in which you state this stopped working in Magento 2.1 (which I'm using). Revising my code now, but prob best to put a note on this answer to that effect as well... Sep 10, 2016 at 22:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.