Jak Magento2 programowo dodaje opcję atrybutu (nie w konfiguracji)

15

Próbuję dodać opcje atrybutów rozmiaru i koloru w moim module importera, ale nie wiem jak ...:

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);

Zgłoszenie błędu (zmodyfikowałem raportowanie wyjątku OptionMaganger.phpdo komunikatu Wyjątek-> )

Nie można zapisać rozmiaru atrybutu Uwaga: Niezdefiniowany indeks: usuń w /var/www/html/magento2/vendor/magento/module-swatches/Model/Plugin/EavAttribute.php w linii 177

  • OptionManagement i Option pochodzą z _contstructor
  • Dzięki OptionManagement mogę odzyskać istniejące elementy, więc powinno być w porządku ..

setLabel()i setValue()są domyślne, ale próbowałem setData , załadowałem instancję opcji i przekazałem, OptionManagement->getItemsaby dodać (...) „ponownie”, ale błąd nadal występuje ...

Masz pomysł, jak mogę dołączyć Opcje EAV (próbki?) Podczas procesu importowania? (nie w konfiguracji modułu)


Aktualizacja :

Innym sposobem mogę dodać opcję:

$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();
}

W ten sposób Magento2 może zapisać opcję atrybutu, ale nie wiem jaki jest „oficjalny” sposób :)

Interpigeon
źródło
opcja dodała dowolną wartość jako ciąg nieobsługiwany dla liczby całkowitej
Ajay Patel

Odpowiedzi:

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

ogłosić:

protected $_eavSetupFactory;

konstruktor:

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;
}

wykonaj metodę:

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)

}
Ronak Chauhan
źródło
Wystąpił poważny błąd w kodzie: $ opcja ['wartość'] [$ wartość] [0] - nie powinna to być $ wartość, ale „id_opcji” - liczba całkowita, dla nowego zestawu opcji 0. Ten jest rzutowany na liczbę całkowitą, więc jeśli masz ciąg bez cyfry, np. „czarny”, będzie to 0 poprawnie. Ale jeśli twoja wartość $ to coś w stylu „10 czarnych”, rzutuje na 10 i aktualizuje encję bazy danych opcją id_wartości = 10, zamiast tego tworząc nową. Może to spowodować poważny bałagan w bazie danych sklepu.
A.Maksymiuk
dzięki poinformować brata. Jeśli znalazłeś błąd, niż możesz zaktualizować moją odpowiedź @ A.Maksymiuk
Ronak Chauhan
Zrobiłem to Proszę zaakceptuj, a następnie cofnę moje zdanie.
A.Maksymiuk
Zatwierdzony, ale głosuj w dół każdą odpowiedzią nie w odpowiedni sposób, stary. Jeśli uważasz, że ta odpowiedź nie jest powiązana lub nie jest zgodna z pytaniem, nie możesz głosować w dół odpowiedzią nikogo. @ A.Maksymiuk
Ronak Chauhan
Zrobiłem to, aby ostrzec wszystkich przed użyciem tego kodu, ponieważ spowodowałoby to poważne uszkodzenie integralności danych. Np. Zamiast tego dodając nową opcję o nazwie „42” (rozmiar) twój skrypt zaktualizował option_id = 42 (która była istniejącą opcją o zupełnie innym atrybucie). Na szczęście przydarzyło mi się to na serwerze testowym i świeżej nowej bazie danych.
A.Maksymiuk
2

Innym sposobem mogę dodać opcję:

$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();
}

W ten sposób Magento2 może zapisać opcję atrybutu, ale nie wiem jaki jest „oficjalny” sposób.

Interpigeon
źródło
Zobacz moją drogę Uważam, że to „oficjalne”
CarComp
twoje rozwiązanie działa, ale tylko wtedy, gdy opcja jest dostępna, opcja nie działa dla liczby całkowitej
Ajay Patel
dlaczego nie miałoby działać dla wartości całkowitych?
Vincent Teyssier,
0

Wydaje się, że jest to problem z weryfikacją. Klucz usuwania w danych pochodzi z formularza w wewnętrznej bazie danych, więc spróbuj dodać pusty klucz usuwania w ten sposób:

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

To może działać.

MauroNigrele
źródło
Niestety nie. OptionManager add (..) ponownie analizuje parametr $ option i pozostawia klawisz „delete” pusty, nie wiem dlaczego… Ale znalazłem inny sposób…
Interpigeon
Świetnie, dodaj swoje rozwiązanie jako odpowiedź na pytanie.
MauroNigrele,
Nie mogę odpowiedzieć na własne pytanie, ale zaktualizowałem pytanie. Myślę, że moje rozwiązanie jest obejściem ...
Interpigeon
Hej, możesz odpowiedzieć na twoje pytanie jest dość powszechne.
MauroNigrele,
0

Skończyłem przepisywanie całej odpowiedzi przy użyciu metod ObjectFactory sugerowanych przez Ryana H.

Ostatecznie była klasą pomocniczą, która wykorzystywała niektóre atrybuty, które utworzyłem na obiekcie klienta, ale istnieje pomysł, jak wykorzystać EAV + ObjectFactories do manipulowania opcjami atrybutów

<?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;
        }
    }
}
CarComp
źródło
1
Powinieneś wstrzykiwać obiekt ObjectFactory i tworzyć z niego instancje obiektu, a nie sam obiekt. Obiekty ORM nie mogą być wstrzykiwane bezpośrednio.
Ryan Hoerr
Który obiektowy? Jest 50. Patrzę na \ Magento \ Framework \ Api \ ObjectFactory, ale wygląda to jak opakowanie dla ObjectManager. Nie jestem pewien, dlaczego nie wdrożyłbym samego menedżera obiektów. W tej nowej wersji jest tak wiele opakowań dla opakowań rzeczy.
CarComp
1
Mówiłem w sposób abstrakcyjny. Wstrzyknij fabrykę dla danego obiektu, a nie dosłownie „ObjectFactory”. Powinieneś wstrzyknąć fabrykę dla każdego używanego typu i utworzyć je w razie potrzeby. Tak, na początku wydaje się bałagan ... ale są ku temu bardzo dobre powody. Poza tym standardy kodu EKG niemal całkowicie zabraniają bezpośredniego korzystania z ObjectManager. Zobacz artykuł Alana Storma wyjaśniający cały temat: alanstorm.com/magento_2_object_manager_instance_objects
Ryan
Co powinienem zrobić, gdy obiekty, których chcę użyć, nie mają fabryki? Na przykład nie mogę znaleźć sposobu na „fabryczne” zarządzanie opcjami. Magento \ Eav \ Model \ AttributeFactory używa również dziwnego -> createAttribute (vars) zamiast po prostu -> create (). Mówię tylko, że kiedy nie pracujesz z produktem lub kategorią wbudowaną, rzeczy stają się trochę dziwne.
CarComp,
1
Nie wszystkie obiekty wymagają fabryki. W takich przypadkach fabryka może nie istnieć od razu po wyjęciu z pudełka - wszystkie, które nie istnieją, zostaną utworzone w czasie wykonywania (z generowaniem DI) lub podczas kompilacji. Przeczytaj artykuł, który podłączyłem. W każdym razie musisz nauczyć się pracować z Magento2, a nie przeciw. Tak, istnieje krzywa uczenia się. Jeśli jeszcze tego nie zrobiłeś, zdecydowanie zalecamy wybranie PhpStorm lub podobnego IDE.
Ryan Hoerr
0

AKTUALIZACJA 11.09.2016: Jak wskazano w szybkiej zmianie, to rozwiązanie nie działa dla M2.1 +. Próba wstrzyknięcia zależności CategorySetuppoza klasę spowoduje błąd krytyczny. Tutaj znajdziesz bardziej niezawodne rozwiązanie: /magento//a/103951/1905


Użyj \Magento\Catalog\Setup\CategorySetupdo tego klasy. Zawiera addAttributeOption()metodę, która działa dokładnie tak samo jakeav/entity_setup::addAttributeOption() w 1.x. Istnieje również kilka innych metod atrybutów, które mogą być pomocne.

Możesz użyć wstrzykiwania zależności, aby uzyskać tę klasę w dowolnym momencie, nawet poza procesem instalacji.

Konkretnie:

/**
 * 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
                ],
            ],
        ]
    );
}

W razie potrzeby możesz wyeliminować attributeRepositoryklasę i korzystać getAttribute()bezpośrednio z niej categorySetup. Wystarczy za każdym razem podać identyfikator typu jednostki.

Ryan Hoerr
źródło
Cześć Ryan, próbuję użyć CategorySetupFactorydo utworzenia wystąpienia CategorySetupz Console\Command, jednak gdy $factory->setup()wywołuję błąd krytyczny, pojawia się: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
quickshiftin
Achh, natknąłem się teraz na ten wątek, w którym twierdzisz, że przestał on działać w Magento 2.1 (którego używam). Teraz zmieniam mój kod, ale najlepiej też zanotować tę odpowiedź na ten efekt ...
quickshiftin
0

Magento 2 dodaj opcję konkretnego atrybutu Wartość programowo.

Uruchom ten skrypt w katalogu głównym magento po adresie 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();
    }
Baharuni Asif
źródło