Magento2: aggiunge a livello di codice le opzioni degli attributi del prodotto


32

Qual è il modo giusto (ufficiale) per aggiungere a livello di codice l'opzione di attributo del prodotto in M2? Ad esempio per manufacturerl'attributo del prodotto. Ovviamente l'opzione esistente verrebbe abbinata al valore del titolo "Amministratore".

Risposte:


55

Ecco l'approccio che ho escogitato per la gestione delle opzioni degli attributi. Classe di aiuto:

<?php
namespace My\Module\Helper;

class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
    /**
     * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface
     */
    protected $attributeRepository;

    /**
     * @var array
     */
    protected $attributeValues;

    /**
     * @var \Magento\Eav\Model\Entity\Attribute\Source\TableFactory
     */
    protected $tableFactory;

    /**
     * @var \Magento\Eav\Api\AttributeOptionManagementInterface
     */
    protected $attributeOptionManagement;

    /**
     * @var \Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory
     */
    protected $optionLabelFactory;

    /**
     * @var \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory
     */
    protected $optionFactory;

    /**
     * Data constructor.
     *
     * @param \Magento\Framework\App\Helper\Context $context
     * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
     * @param \Magento\Eav\Model\Entity\Attribute\Source\TableFactory $tableFactory
     * @param \Magento\Eav\Api\AttributeOptionManagementInterface $attributeOptionManagement
     * @param \Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory $optionLabelFactory
     * @param \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionFactory
     */
    public function __construct(
        \Magento\Framework\App\Helper\Context $context,
        \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
        \Magento\Eav\Model\Entity\Attribute\Source\TableFactory $tableFactory,
        \Magento\Eav\Api\AttributeOptionManagementInterface $attributeOptionManagement,
        \Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory $optionLabelFactory,
        \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionFactory
    ) {
        parent::__construct($context);

        $this->attributeRepository = $attributeRepository;
        $this->tableFactory = $tableFactory;
        $this->attributeOptionManagement = $attributeOptionManagement;
        $this->optionLabelFactory = $optionLabelFactory;
        $this->optionFactory = $optionFactory;
    }

    /**
     * Get attribute by code.
     *
     * @param string $attributeCode
     * @return \Magento\Catalog\Api\Data\ProductAttributeInterface
     */
    public function getAttribute($attributeCode)
    {
        return $this->attributeRepository->get($attributeCode);
    }

    /**
     * Find or create a matching attribute option
     *
     * @param string $attributeCode Attribute the option should exist in
     * @param string $label Label to find or add
     * @return int
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function createOrGetId($attributeCode, $label)
    {
        if (strlen($label) < 1) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Label for %1 must not be empty.', $attributeCode)
            );
        }

        // Does it already exist?
        $optionId = $this->getOptionId($attributeCode, $label);

        if (!$optionId) {
            // If no, add it.

            /** @var \Magento\Eav\Model\Entity\Attribute\OptionLabel $optionLabel */
            $optionLabel = $this->optionLabelFactory->create();
            $optionLabel->setStoreId(0);
            $optionLabel->setLabel($label);

            $option = $this->optionFactory->create();
            $option->setLabel($optionLabel);
            $option->setStoreLabels([$optionLabel]);
            $option->setSortOrder(0);
            $option->setIsDefault(false);

            $this->attributeOptionManagement->add(
                \Magento\Catalog\Model\Product::ENTITY,
                $this->getAttribute($attributeCode)->getAttributeId(),
                $option
            );

            // Get the inserted ID. Should be returned from the installer, but it isn't.
            $optionId = $this->getOptionId($attributeCode, $label, true);
        }

        return $optionId;
    }

    /**
     * Find the ID of an option matching $label, if any.
     *
     * @param string $attributeCode Attribute code
     * @param string $label Label to find
     * @param bool $force If true, will fetch the options even if they're already cached.
     * @return int|false
     */
    public function getOptionId($attributeCode, $label, $force = false)
    {
        /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
        $attribute = $this->getAttribute($attributeCode);

        // Build option array if necessary
        if ($force === true || !isset($this->attributeValues[ $attribute->getAttributeId() ])) {
            $this->attributeValues[ $attribute->getAttributeId() ] = [];

            // We have to generate a new sourceModel instance each time through to prevent it from
            // referencing its _options cache. No other way to get it to pick up newly-added values.

            /** @var \Magento\Eav\Model\Entity\Attribute\Source\Table $sourceModel */
            $sourceModel = $this->tableFactory->create();
            $sourceModel->setAttribute($attribute);

            foreach ($sourceModel->getAllOptions() as $option) {
                $this->attributeValues[ $attribute->getAttributeId() ][ $option['label'] ] = $option['value'];
            }
        }

        // Return option ID if exists
        if (isset($this->attributeValues[ $attribute->getAttributeId() ][ $label ])) {
            return $this->attributeValues[ $attribute->getAttributeId() ][ $label ];
        }

        // Return false if does not exist
        return false;
    }
}

Quindi, nella stessa classe o includendolo tramite l'iniezione delle dipendenze, puoi aggiungere o ottenere il tuo ID opzione chiamando createOrGetId($attributeCode, $label).

Ad esempio, se si inietta My\Module\Helper\Datacome $this->moduleHelper, è possibile chiamare:

$manufacturerId = $this->moduleHelper->createOrGetId('manufacturer', 'ABC Corp');

Se "ABC Corp" è un produttore esistente, estrarrà l'ID. In caso contrario, lo aggiungerà.

AGGIORNATO 2016-09-09: Per Ruud N., la soluzione originale ha utilizzato CatalogSetup, che ha provocato un bug a partire da Magento 2.1. Questa soluzione rivista ignora quel modello, creando in modo esplicito l'opzione e l'etichetta. Dovrebbe funzionare su 2.0+.


3
È ufficiale come hai intenzione di ottenere. Tutte le ricerche e l'aggiunta di opzioni passano attraverso il core Magento. La mia classe è solo un wrapper per quei metodi di base che li rendono più facili da usare.
Ryan Hoerr,

1
Ciao Ryan, non dovresti impostare il valore sull'opzione, questo è l'id interno utilizzato da Magento e ho scoperto nel modo più duro che se imposti il ​​valore su un valore di stringa con un numero iniziale come '123 abc corp' provoca alcuni seri problemi dovuti all'implementazione di Magento\Eav\Model\ResourceModel\Entity\Attribute::_processAttributeOptions. Vedi tu stesso, se rimuovi l' $option->setValue($label);istruzione dal tuo codice, salverà l'opzione, quindi quando la recuperi Magento restituirà il valore da un auto-incremento sulla eav_attribute_optiontabella.
cambio rapido

2
se lo aggiungo in una funzione foreach, nella seconda iterazione otterrò l'errore "Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () deve essere del tipo stringa, oggetto dato"
JELLEJ

1
Sì, questo codice non funziona
Sourav il

2
@JELLEJ Se riscontri problemi Uncaught TypeError: l'argomento 3 passato a Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () deve essere del tipo stringa, l'oggetto indicato nella funzione foreach quindi cambia $ option-> setLabel ( $ optionLabel); a $ option-> setLabel ($ label); alla linea 102
Nadeem0035 il

11

testato su Magento 2.1.3.

Non ho trovato alcun modo praticabile su come creare l'attributo con le opzioni contemporaneamente. Quindi inizialmente dobbiamo creare un attributo e quindi aggiungere opzioni per esso.

Iniettare la seguente classe \ Magento \ Eav \ Setup \ EavSetupFactory

 $setup->startSetup();

 /** @var \Magento\Eav\Setup\EavSetup $eavSetup */
 $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

Crea nuovo attributo:

$eavSetup->addAttribute(
    'catalog_product',
    $attributeCode,
    [
        'type' => 'varchar',
        'input' => 'select',
        'required' => false,
        ...
    ],
);

Aggiungi opzioni personalizzate.

La funzione addAttributenon restituisce nulla di utile che può essere utilizzato in futuro. Quindi, dopo la creazione dell'attributo, dobbiamo recuperare l'oggetto dell'attributo da soli. !!! Importante Ne abbiamo bisogno perché la funzione si aspetta solo attribute_id, ma non voglio lavorare con attribute_code.

In tal caso, dobbiamo ottenerlo attribute_ide passarlo alla funzione di creazione degli attributi.

$attributeId = $eavSetup->getAttributeId('catalog_product', 'attribute_code');

Quindi dobbiamo generare un array di opzioni nel modo in cui si aspetta Magento:

$options = [
        'values' => [
        'sort_order1' => 'title1',
        'sort_order2' => 'title2',
        'sort_order3' => 'title3',
    ],
    'attribute_id' => 'some_id',
];

Per esempio:

$options = [
        'values' => [
        '1' => 'Red',
        '2' => 'Yellow',
        '3' => 'Green',
    ],
    'attribute_id' => '32',
];

E passalo per funzionare:

$eavSetup->addAttributeOption($options);

Il terzo parametro di addAttribute può accettare il parametro array ['opzione']
DWils

10

L'uso della Magento \ Eav \ Setup \ EavSetupFactory o persino della classe \ Magento \ Catalog \ Setup \ CategorySetupFactory può portare al seguente problema: https://github.com/magento/magento2/issues/4896 .

Le classi che dovresti usare:

protected $_logger;

protected $_attributeRepository;

protected $_attributeOptionManagement;

protected $_option;

protected $_attributeOptionLabel;

 public function __construct(
    \Psr\Log\LoggerInterface $logger,
    \Magento\Eav\Model\AttributeRepository $attributeRepository,
    \Magento\Eav\Api\AttributeOptionManagementInterface $attributeOptionManagement,
    \Magento\Eav\Api\Data\AttributeOptionLabelInterface $attributeOptionLabel,
    \Magento\Eav\Model\Entity\Attribute\Option $option
  ){
    $this->_logger = $logger;
    $this->_attributeRepository = $attributeRepository;
    $this->_attributeOptionManagement = $attributeOptionManagement;
    $this->_option = $option;
    $this->_attributeOptionLabel = $attributeOptionLabel;
 }

Quindi nella tua funzione fai qualcosa del genere:

 $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
Grazie, hai ragione. Ho aggiornato la mia risposta di conseguenza. Si noti che $attributeOptionLabele $optionsono classi ORM; non dovresti iniettarli direttamente. L'approccio corretto è quello di iniettare la loro classe di fabbrica, quindi creare un'istanza secondo necessità. Si noti inoltre che non si utilizzano in modo coerente le interfacce dati API.
Ryan Hoerr,

3
Ciao @Rudd, vedi il mio commento sulla risposta di Ryan. Non vuoi chiamare $option->setValue()perché è per un option_idcampo magento interno sul eav_attribute_optiontavolo.
cambio rapido

Grazie. È quello che ho scoperto anche io. Modificherò la mia risposta di conseguenza.
Ruud N.

0

Per Magento 2.3.3 ho scoperto che puoi adottare l'approccio Magento DevTeam.

  • Aggiungi patch
bin/magento setup:db-declaration:generate-patch Vendor_Module PatchName
  • Aggiungi CategorySetupFactory al costruttore
public function __construct(
        ModuleDataSetupInterface $moduleDataSetup,
        Factory $configFactory
        CategorySetupFactory $categorySetupFactory
    ) {
        $this->moduleDataSetup = $moduleDataSetup;
        $this->configFactory = $configFactory;
        $this->categorySetupFactory = $categorySetupFactory;
}
  • Aggiungi attributo nella funzione apply ()

    public function apply()
    {
        $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]);
    
        $categorySetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'custom_layout',
            [
                'type' => 'varchar',
                'label' => 'New Layout',
                'input' => 'select',
                'source' => \Magento\Catalog\Model\Product\Attribute\Source\Layout::class,
                'required' => false,
                'sort_order' => 50,
                'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE,
                'group' => 'Schedule Design Update',
                'is_used_in_grid' => true,
                'is_visible_in_grid' => false,
                'is_filterable_in_grid' => false
            ]
        );
    }

uhm, ho appena scoperto che volevo aggiungere questa risposta a una domanda diversa. Lo vivrò qui e aggiungerò un riferimento a questa risposta. Spero sia tutto ok Questa è una risposta parziale anche a questa domanda :)
incorporata0

-4

Questa NON è una risposta. Solo una soluzione alternativa.

Presuppone che tu abbia accesso a Magento Backend tramite il browser e ti trovi nella pagina di modifica degli attributi (l'URL è simile a admin / catalog / product_attribute / edit / attributo_id / XXX / chiave ..)

Vai alla console del browser (CTRL + MAIUSC + J su Chrome) e incolla il seguente codice dopo aver modificato il mimim dell'array .

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

- testato su Magento 2.2.2

Articolo dettagliato - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/


1
Questa è una terribile soluzione a lungo termine. Non puoi aspettarti che quei selettori rimangano invariati. Questa è una soluzione alternativa, se funziona davvero come previsto.
domdambrogia,

@domdambrogia è d'accordo. È una soluzione alternativa.
th3pirat3,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.