Magento2 - secara terprogram menambahkan opsi atribut produk

32

Apa cara (resmi) yang tepat untuk secara terprogram menambahkan opsi atribut produk dalam M2? Misalnya untuk manufactureratribut produk. Jelas opsi yang ada akan dicocokkan dengan nilai judul "Admin".

Werd
sumber

Jawaban:

55

Inilah pendekatan yang saya buat untuk menangani opsi atribut. Kelas pembantu:

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

Kemudian, baik di kelas yang sama atau termasuk melalui injeksi ketergantungan, Anda dapat menambah atau mendapatkan ID opsi Anda dengan menelepon createOrGetId($attributeCode, $label).

Misalnya, jika Anda menyuntikkan My\Module\Helper\Datasebagai $this->moduleHelper, maka Anda dapat menghubungi:

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

Jika 'ABC Corp' adalah pabrikan yang ada, itu akan menarik ID. Jika tidak, itu akan menambahkannya.

DIPERBARUI 2016-09-09: Per Ruud N., solusi asli menggunakan CatalogSetup, yang menghasilkan bug yang dimulai pada Magento 2.1. Solusi yang direvisi ini memintas model itu, menciptakan opsi dan label secara eksplisit. Ini harus bekerja di 2.0+.

Ryan Hoerr
sumber
3
Ini resmi seperti yang akan Anda dapatkan. Semua pencarian dan penambahan opsi melalui inti Magento. Kelas saya hanyalah pembungkus untuk metode inti yang membuatnya lebih mudah digunakan.
Ryan Hoerr
1
Hai Ryan, Anda tidak harus menetapkan nilai pada opsi, ini adalah penggunaan internal id magento dan saya menemukan cara yang sulit bahwa jika Anda menetapkan nilai ke nilai string dengan nomor terkemuka seperti '123 abc corp' itu menyebabkan beberapa masalah serius akibat penerapan Magento\Eav\Model\ResourceModel\Entity\Attribute::_processAttributeOptions. Lihat sendiri, jika Anda menghapus $option->setValue($label);pernyataan dari kode Anda, itu akan menyimpan opsi, maka ketika Anda mengambilnya Magento akan mengembalikan nilai dari kenaikan otomatis di atas eav_attribute_optionmeja.
quickshiftin
2
jika saya menambahkan ini dalam fungsi foreach, dalam iterasi kedua saya akan mendapatkan kesalahan "Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () harus dari tipe string, objek yang diberikan"
JELLEJ
1
Ya kode ini tidak berfungsi
Sourav
2
@JELLEJ Jika Anda mendapatkan masalah Uncaught TypeError: Argumen 3 diteruskan ke Magento \ Eav \ Model \ Entity \ Attribute \ OptionManagement :: setOptionValue () harus dari tipe string, objek yang diberikan dalam fungsi foreach kemudian ubah $ option-> setLabel ( $ optionLabel); ke $ option-> setLabel ($ label); pada baris 102
Nadeem0035
11

diuji pada Magento 2.1.3.

Saya tidak menemukan cara yang bisa diterapkan bagaimana cara membuat atribut dengan opsi sekaligus. Jadi pada awalnya kita perlu membuat atribut dan kemudian menambahkan opsi untuk itu.

Suntikkan kelas berikut \ Magento \ Eav \ Setup \ EavSetupFactory

 $setup->startSetup();

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

Buat atribut baru:

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

Tambahkan opsi khusus.

Fungsi addAttributetidak mengembalikan apa pun yang berguna yang dapat digunakan di masa depan. Jadi setelah penciptaan atribut kita perlu mengambil objek atribut sendiri. !!! Penting Kami membutuhkannya karena fungsi hanya mengharapkan attribute_id, tetapi tidak ingin bekerja dengannya attribute_code.

Dalam hal ini kita perlu mendapatkan attribute_iddan meneruskannya ke fungsi pembuatan atribut.

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

Maka kita perlu membuat array opsi dengan cara yang diharapkan magento:

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

Sebagai contoh:

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

Dan meneruskannya ke fungsi:

$eavSetup->addAttributeOption($options);
zhartaunik
sumber
Param ketiga addAttribute dapat mengambil parameter array ['option']
DWils
10

Menggunakan kelas Magento \ Eav \ Setup \ EavSetupFactory atau bahkan kelas \ Magento \ Catalog \ Setup \ CategorySetupFactory dapat menyebabkan masalah berikut: https://github.com/magento/magento2/issues/4896 .

Kelas yang harus Anda gunakan:

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

Kemudian dalam fungsi Anda lakukan sesuatu seperti ini:

 $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.
sumber
1
Terima kasih, kamu benar. Saya telah memperbarui jawaban saya sesuai dengan itu. Catat itu $attributeOptionLabeldan $optionmerupakan kelas ORM; Anda tidak harus menyuntikkannya secara langsung. Pendekatan yang tepat adalah menyuntikkan kelas pabrik mereka, lalu membuat instance sesuai kebutuhan. Perhatikan juga Anda tidak menggunakan antarmuka data API secara konsisten.
Ryan Hoerr
3
Hai @Rudd, lihat komentar saya tentang jawaban Ryan. Anda tidak ingin menelepon $option->setValue()karena itu untuk option_idbidang magento internal di atas eav_attribute_optionmeja.
quickshiftin
Terima kasih. Itu yang saya temukan juga. Akan mengedit jawaban saya sesuai.
Ruud N.
0

Untuk Magento 2.3.3 saya menemukan bahwa Anda dapat mengambil pendekatan Magento DevTeam.

  • Tambahkan Patch
bin/magento setup:db-declaration:generate-patch Vendor_Module PatchName
  • Tambahkan CategorySetupFactory ke konstruktor
public function __construct(
        ModuleDataSetupInterface $moduleDataSetup,
        Factory $configFactory
        CategorySetupFactory $categorySetupFactory
    ) {
        $this->moduleDataSetup = $moduleDataSetup;
        $this->configFactory = $configFactory;
        $this->categorySetupFactory = $categorySetupFactory;
}
  • Tambahkan atribut dalam fungsi 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
sumber
uhmm saya baru tahu bahwa saya ingin menambahkan jawaban ini ke pertanyaan lain. Saya hanya akan tinggal di sini dan menambahkan referensi untuk jawaban ini di sana. Saya harap tidak apa-apa. Ini juga sebagian jawaban untuk pertanyaan ini :)
embed0
-4

Ini BUKAN jawaban. Hanya solusinya.

Ini mengasumsikan bahwa Anda memiliki akses ke Magento Backend menggunakan browser dan Anda berada di halaman edit atribut (url seperti admin / katalog / product_attribute / edit / attribute_id / XXX / key ..)

Buka Konsol browser (CTRL + SHIFT + J on chrome) dan rekatkan kode berikut setelah mengubah array mimim .

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

- diuji pada Magento 2.2.2

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

th3pirat3
sumber
1
Ini adalah solusi jangka panjang yang mengerikan. Anda tidak bisa berharap para pemilih itu tetap sama. Ini adalah solusi terbaik, jika itu benar-benar berfungsi seperti yang diharapkan.
domdambrogia
@domdambrogia setuju. Ini adalah solusi.
th3pirat3