Magento2 - Programmgesteuertes Hinzufügen von Produktattributoptionen

32

Was ist der richtige (offizielle) Weg, um die Produktattributoption in M2 programmgesteuert hinzuzufügen? ZB für manufacturerProduktattribut. Offensichtlich würde die vorhandene Option mit dem Titelwert "Admin" übereinstimmen.

werd
quelle

Antworten:

55

Hier ist der Ansatz, den ich für den Umgang mit Attributoptionen entwickelt habe. Hilfsklasse:

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

Anschließend können Sie Ihre Options-ID entweder in derselben Klasse oder über die Abhängigkeitsinjektion hinzufügen oder durch Aufrufen abrufen createOrGetId($attributeCode, $label).

Wenn Sie beispielsweise My\Module\Helper\Dataals injizieren $this->moduleHelper, können Sie Folgendes aufrufen:

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

Wenn 'ABC Corp' ein bestehender Hersteller ist, wird die ID gezogen. Wenn nicht, wird es hinzugefügt.

AKTUALISIERT 2016-09-09: Per Ruud N., die ursprüngliche Lösung verwendete CatalogSetup, was zu einem Fehler führte, der in Magento 2.1 begann. Diese überarbeitete Lösung umgeht dieses Modell und erstellt die Option und die Bezeichnung explizit. Es sollte auf 2.0+ funktionieren.

Ryan Hoerr
quelle
3
Es ist so offiziell wie du nur willst. Alle Suchvorgänge und das Hinzufügen von Optionen werden über Magento ausgeführt. Meine Klasse ist nur ein Wrapper für die Kernmethoden, mit denen sie einfacher zu verwenden sind.
Ryan Hoerr
1
Hallo Ryan, du solltest den Wert nicht auf die Option setzen, dies ist die interne ID, die von Magento verwendet wird, und ich habe herausgefunden, wie schwer es ist, wenn du den Wert auf einen Zeichenkettenwert mit einer führenden Zahl wie '123 abc corp' setzt einige schwerwiegende Probleme aufgrund der Umsetzung von Magento\Eav\Model\ResourceModel\Entity\Attribute::_processAttributeOptions. Sehen Sie selbst, wenn Sie die $option->setValue($label);Anweisung aus Ihrem Code entfernen , wird die Option gespeichert, und wenn Sie sie abrufen, gibt Magento den Wert von einem automatischen Inkrement in der eav_attribute_optionTabelle zurück.
Quickshiftin
2
Wenn ich dies in einer foreach-Funktion hinzufüge, erhalte ich in der zweiten Iteration die Fehlermeldung "Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () muss vom Typ string sein, Objekt angegeben"
JELLEJ
1
Ja, dieser Code funktioniert nicht
Sourav
2
@JELLEJ Wenn Sie das Problem Uncaught TypeError: Argument 3 erhalten, das an Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () übergeben wird, muss der Typ string sein. Das in foreach angegebene Objekt muss dann $ option-> setLabel ( $ optionLabel); zu $ option-> setLabel ($ label); in der Linie 102
Nadeem0035
11

Getestet mit Magento 2.1.3.

Ich habe keine praktikable Möglichkeit gefunden, ein Attribut mit Optionen auf einmal zu erstellen. Zunächst müssen wir ein Attribut erstellen und dann Optionen hinzufügen.

Injizieren Sie die folgende Klasse \ Magento \ Eav \ Setup \ EavSetupFactory

 $setup->startSetup();

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

Neues Attribut erstellen:

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

Fügen Sie benutzerdefinierte Optionen hinzu.

Die Funktion addAttributegibt nichts Nützliches zurück, das in Zukunft verwendet werden kann. Nach der Attributerstellung müssen wir das Attributobjekt also selbst abrufen. !!! Wichtig Wir brauchen es, weil die Funktion nur erwartet attribute_id, aber nicht damit arbeiten will attribute_code.

In diesem Fall müssen wir sie abrufen attribute_idund an die Attributerstellungsfunktion übergeben.

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

Dann müssen wir das options array so generieren, wie es von magento erwartet wird:

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

Zum Beispiel:

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

Und übergeben Sie es an Funktion:

$eavSetup->addAttributeOption($options);
zhartaunik
quelle
Der dritte Parameter von addAttribute kann den Array-Parameter ['option']
annehmen
10

Die Verwendung der Klasse Magento \ Eav \ Setup \ EavSetupFactory oder sogar der Klasse \ Magento \ Catalog \ Setup \ CategorySetupFactory kann zu folgendem Problem führen: https://github.com/magento/magento2/issues/4896 .

Die Klassen, die Sie verwenden sollten:

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

Dann machen Sie in Ihrer Funktion so etwas:

 $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);
Ruud N.
quelle
1
Danke, du hast recht. Ich habe meine Antwort entsprechend aktualisiert. Beachten Sie, dass $attributeOptionLabelund $optionORM-Klassen sind; Sie sollten sie nicht direkt injizieren. Der richtige Ansatz besteht darin, die Factory-Klasse zu injizieren und dann nach Bedarf eine Instanz zu erstellen. Beachten Sie auch, dass Sie die API-Datenschnittstellen nicht konsistent verwenden.
Ryan Hoerr
3
Hallo @Rudd, siehe meinen Kommentar zu Ryans Antwort. Sie möchten nicht aufrufen, $option->setValue()da dies für ein internes Magento- option_idFeld in der eav_attribute_optionTabelle gilt.
Quickshiftin
Vielen Dank. Das habe ich auch herausgefunden. Wird meine Antwort entsprechend bearbeiten.
Ruud N.
0

Für Magento 2.3.3 habe ich festgestellt, dass Sie den Magento DevTeam-Ansatz verwenden können.

  • Patch hinzufügen
bin/magento setup:db-declaration:generate-patch Vendor_Module PatchName
  • Fügen Sie dem Konstruktor CategorySetupFactory hinzu
public function __construct(
        ModuleDataSetupInterface $moduleDataSetup,
        Factory $configFactory
        CategorySetupFactory $categorySetupFactory
    ) {
        $this->moduleDataSetup = $moduleDataSetup;
        $this->configFactory = $configFactory;
        $this->categorySetupFactory = $categorySetupFactory;
}
  • Attribut in Funktion apply () hinzufügen

    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
            ]
        );
    }
embed0
quelle
ähm ich finde gerade heraus, dass ich diese Antwort zu einer anderen Frage hinzufügen wollte. Ich werde es einfach hier leben und dort auf diese Antwort verweisen. Ich hoffe das es ok ist Dies ist auch eine teilweise Antwort auf diese Frage :)
embed0
-4

Dies ist keine Antwort. Nur eine Problemumgehung.

Es wird davon ausgegangen, dass Sie über einen Browser auf Magento Backend zugreifen können und sich auf der Seite zum Bearbeiten von Attributen befinden (die URL sieht aus wie admin / catalog / product_attribute / edit / attribute_id / XXX / key.)

Wechseln Sie zur Browserkonsole (STRG + UMSCHALT + J in Chrome) und fügen Sie den folgenden Code ein, nachdem Sie den Array-Mimim geändert haben .

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

- getestet auf Magento 2.2.2

Ausführlicher Artikel - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/

th3pirat3
quelle
1
Dies ist eine schreckliche langfristige Lösung. Sie können nicht zuverlässig erwarten, dass diese Selektoren gleich bleiben. Dies ist bestenfalls eine Problemumgehung, wenn es tatsächlich wie erwartet funktioniert.
Domdambrogia
@domdambrogia einverstanden. Es ist eine Problemumgehung.
th3pirat3