7

I've created an attribute in Magento 2 of the type multiselect and now I want to have it populated by a custom source model.

Now I remember from Magento 1 that when you want to do this, you had to manually edit the attribute in the database and set the source_model to the path of the source model.

However, when I do this in Magento 2, I get an error. I changed source_model in eav_attribute to Vendor\Module\Model\Config\Source\Product\Attributes, but when I want to edit the product in the admin, I get the following error:

Fatal error: Uncaught Error: Call to undefined method Vendor\Module\Model\Config\Source\Product\Attributes::setAttribute() in .../vendor/magento/module-eav/Model/Entity/Attribute/AbstractAttribute.php:547

Stack trace: #0 .../var/generation/Magento/Catalog/Model/ResourceModel/Eav/Attribute/Interceptor.php(1129): Magento\Eav\Model\Entity\Attribute\AbstractAttribute->getSource()
#1 .../vendor/magento/module-backend/Block/Widget/Form.php(232): Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor->getSource()
#2 .../vendor/magento/module-backend/Block/Widget/Form.php(201): Magento\Backend\Block\Widget\Form->_applyTypeSpecificConfig('multiselect', Object(Magento\Framework\Data\Form\Element\Multiselect), Object(Magento\Catalog\Model\ResourceModel\Eav\Attribute\Interceptor))
#3 .../vendor/magento/module-catalog/Block/Adminhtml/Product/Edit/Tab/Attributes.php(51): Magento\Backend\Block\Widget\Form->_setFieldset(Array, Object(Magento\Framework\Data\Form\El in .../vendor/magento/module-eav/Model/Entity/Attribute/AbstractAttribute.php on line 547

Does anyone know how I can create a product attribute with a custom source model?

Edit:

Current Source Model:

use Vendor\Module\Model\Config\Source\AbstractSource;
use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory;
use Magento\Eav\Model\Entity\Attribute;
use Magento\Eav\Model\Entity\TypeFactory;

/**
 * Class Attributes
 */
class Attributes extends AbstractSource
{
    /**
     * @var AttributeFactory
     */
    protected $attributeFactory;

    /**
     * @var TypeFactory
     */
    protected $eavTypeFactory;

    /**
     * Attributes constructor.
     * @param AttributeFactory $attributeFactory
      @param TypeFactory $typeFactory
     */
    public function __construct(
        AttributeFactory $attributeFactory,
        TypeFactory $typeFactory
    )
    {
        $this->attributeFactory = $attributeFactory;
        $this->eavTypeFactory = $typeFactory;
    }

    /**
     * @return array
     */
    public function toArray()
    {
        $arr = [];

        $entityType = $this->eavTypeFactory->create()->loadByCode('catalog_product');        
        $collection = $this->attributeFactory->create()->getCollection();
        $collection->addFieldToFilter('entity_type_id', $entityType->getId());
        $collection->setOrder('attribute_code');

        /** @var Attribute $attribute */
        foreach ($collection as $attribute) {
            $arr[$attribute->getAttributeId()] = $attribute->getFrontendLabel();
        }

        return $arr;
    }
}

Class Vendor\Module\Model\Config\Source\AbstractSource:

namespace Vendor\Module\Model\Config\Source;

abstract class AbstractSource implements \Magento\Framework\Option\ArrayInterface
{
    /**
     * Options getter
     * @return array
     */
    final public function toOptionArray()
    {
        $arr = $this->toArray();
        $ret = [];

        foreach ($arr as $key => $value) {
            $ret[] = [
                'value' => $key,
                'label' => $value
            ];
        }

        return $ret;
    }

    /**
     * Get options in "key-value" format
     * @return array
     */
    public function toArray()
    {
        return [];
    }
}
  • Post your current source model code. – Ryan Hoerr May 31 '16 at 11:26
  • I've added my source model and the abstract class it extends. The abstract class is nothing more than a method that generates the toOptionArray() output. – Giel Berkers May 31 '16 at 11:33
6

I got it! It turns out that multiselect uses the backend model Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend. For this reason, the source model, must also take EAV into account. How can we do this? Simple: just extend your source model from Attribute\Source\AbstractSource and implement the getAllOptions()-method (which returns a 2-dimensional array with value and label-keys:

/**
 * @return array
 */
public function getAllOptions()
{
    $arr = $this->toArray();
    $ret = [];

    foreach ($arr as $key => $value) {
        $ret[] = [
            'value' => $key,
            'label' => $value
        ];
    }

    return $ret;
}

That's it! Enjoy using your custom source model for multiselect attributes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.