Magento2 - ajouter par programme des options d'attribut de produit

32

Quelle est la bonne façon (officielle) d'ajouter par programme l'option d'attribut de produit dans M2? Par exemple pour l' manufacturerattribut de produit. De toute évidence, l'option existante correspondrait à la valeur du titre "Admin".

werd
la source

Réponses:

55

Voici l'approche que j'ai trouvée pour gérer les options d'attribut. Classe d'assistance:

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

Ensuite, dans la même classe ou en l'incluant via l'injection de dépendances, vous pouvez ajouter ou obtenir votre ID d'option en appelant createOrGetId($attributeCode, $label).

Par exemple, si vous injectez en My\Module\Helper\Datatant que $this->moduleHelper, vous pouvez appeler:

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

Si «ABC Corp» est un fabricant existant, il extraira l'ID. Sinon, il l'ajoutera.

MISE À JOUR 2016-09-09: Per Ruud N., la solution d'origine a utilisé CatalogSetup, ce qui a entraîné un bogue à partir de Magento 2.1. Cette solution révisée contourne ce modèle, créant l'option et l'étiquette explicitement. Cela devrait fonctionner sur 2.0+.

Ryan Hoerr
la source
3
C'est aussi officiel que vous allez l'obtenir. Toutes les recherches et l'ajout d'options passent par le cœur de Magento. Ma classe est juste un wrapper pour ces méthodes de base qui les rendent plus faciles à utiliser.
Ryan Hoerr
1
Salut Ryan, vous ne devriez pas définir la valeur de l'option, c'est l'identifiant interne utilisé par magento et j'ai découvert à la dure que si vous définissez la valeur sur une valeur de chaîne avec un nombre principal comme '123 abc corp', cela provoque de graves problèmes dus à la mise en œuvre de Magento\Eav\Model\ResourceModel\Entity\Attribute::_processAttributeOptions. Voyez par vous-même, si vous supprimez l' $option->setValue($label);instruction de votre code, elle enregistrera l'option, puis lorsque vous la récupérerez, Magento renverra la valeur d'un incrémentation automatique sur la eav_attribute_optiontable.
quickshift du
2
si j'ajoute ceci dans une fonction foreach, dans la deuxième itération j'obtiendrai l'erreur "Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () doit être du type chaîne, objet donné"
JELLEJ
1
Oui, ce code ne fonctionne pas
Sourav
2
@JELLEJ Si vous obtenez le problème Uncaught TypeError: l'argument 3 est passé à Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () doit être de type chaîne, l'objet donné dans la fonction foreach puis changer $ option-> setLabel ( $ optionLabel); à $ option-> setLabel ($ label); à la ligne 102
Nadeem0035
11

testé sur Magento 2.1.3.

Je n'ai trouvé aucun moyen pratique de créer un attribut avec des options à la fois. Donc, au départ, nous devons créer un attribut, puis ajouter des options pour celui-ci.

Injectez la classe suivante \ Magento \ Eav \ Setup \ EavSetupFactory

 $setup->startSetup();

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

Créer un nouvel attribut:

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

Ajoutez des options personnalisées.

La fonction addAttributene renvoie rien d'utile qui puisse être utilisé à l'avenir. Ainsi, après la création de l'attribut, nous devons récupérer l'objet attribut par nous-mêmes. !!! Important Nous en avons besoin car la fonction attend uniquement attribute_id, mais ne veut pas travailler avec attribute_code.

Dans ce cas, nous devons l'obtenir attribute_idet le transmettre à la fonction de création d'attribut.

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

Ensuite, nous devons générer un tableau d'options de la manière attendue par magento:

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

Comme exemple:

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

Et passez-le pour fonctionner:

$eavSetup->addAttributeOption($options);
zhartaunik
la source
Le 3ème paramètre d'addAttribute peut prendre le paramètre de tableau ['option']
DWils
10

L'utilisation de la classe Magento \ Eav \ Setup \ EavSetupFactory ou même de la classe \ Magento \ Catalog \ Setup \ CategorySetupFactory peut entraîner le problème suivant: https://github.com/magento/magento2/issues/4896 .

Les classes que vous devez utiliser:

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

Ensuite, dans votre fonction, faites quelque chose comme ceci:

 $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.
la source
1
Merci, vous avez raison. J'ai mis à jour ma réponse en conséquence. Notez que $attributeOptionLabelet $optionsont des classes ORM; vous ne devez pas les injecter directement. L'approche appropriée consiste à injecter leur classe d'usine, puis à créer une instance selon les besoins. Notez également que vous n'utilisez pas les interfaces de données API de manière cohérente.
Ryan Hoerr
3
Bonjour @Rudd, voir mon commentaire sur la réponse de Ryan. Vous ne voulez pas appeler $option->setValue()car c'est pour un option_idchamp magento interne sur la eav_attribute_optiontable.
quickshiftin
Merci. C'est ce que j'ai découvert moi aussi. Modifie ma réponse en conséquence.
Ruud N.
0

Pour Magento 2.3.3, j'ai constaté que vous pouvez adopter l'approche Magento DevTeam.

  • Ajouter un patch
bin/magento setup:db-declaration:generate-patch Vendor_Module PatchName
  • Ajouter CategorySetupFactory au constructeur
public function __construct(
        ModuleDataSetupInterface $moduleDataSetup,
        Factory $configFactory
        CategorySetupFactory $categorySetupFactory
    ) {
        $this->moduleDataSetup = $moduleDataSetup;
        $this->configFactory = $configFactory;
        $this->categorySetupFactory = $categorySetupFactory;
}
  • Ajouter un attribut dans la fonction 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
            ]
        );
    }
embed0
la source
euhmm je viens de découvrir que je voulais ajouter cette réponse à une question différente. Je vais juste le vivre ici et ajouter une référence à cette réponse là-bas. J'espère que ça va. C'est aussi une réponse partielle à cette question :)
embed0
-4

Ce n'est PAS une réponse. Juste une solution de contournement.

Il suppose que vous avez accès à Magento Backend à l'aide du navigateur et que vous êtes sur la page d'édition d'attribut (l'URL ressemble à admin / catalogue / product_attribute / edit / attribute_id / XXX / key ..)

Accédez à la console du navigateur (CTRL + MAJ + J sur Chrome) et collez le code suivant après avoir modifié le mimim du tableau .

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

- testé sur Magento 2.2.2

Article détaillé - https://tutes.in/how-to-manage-magento-2-product-attribute-values-options-using-console/

th3pirat3
la source
1
Il s'agit d'une terrible solution à long terme. Vous ne pouvez pas vous attendre à ce que ces sélecteurs restent les mêmes. C'est une solution de contournement au mieux, si cela fonctionne réellement comme prévu.
domdambrogia
@domdambrogia est d'accord. Il s'agit d'une solution de contournement.
th3pirat3