credits: Done and not done via photopin (license). posted on June 8th, 2016.

Use custom Source Models for Multiselect Product Attributes in Magento 2

Source models in Magento 2 are very simple. They simply are classes that return a list of properties. A very basic example:

class ImportMethod implements \Magento\Framework\Option\ArrayInterface
{
    const METHOD_PUSH = 'push';
    const METHOD_PULL = 'pull';

    /**
     * @return array
     */
    public function toArray()
    {
        return [
            self::METHOD_PUSH => __('Push (remote server transfers to us)'),
            self::METHOD_PULL => __('Pull (transfer from remote server)')
        ];
    }

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

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

        return $ret;
    }
}

You can use this source model in configuration files or in the source_model-column of your EAV attribute.

But ...

When you want to use multiselect instead of select, you might encounter the following error:

Fatal error: Uncaught Error: Call to undefined method Vendor\Module\Model\Config\Source\YourSourceModel::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</pre>

What's happening here? and more importantly: how can we fix this?

EAV Models

It's all in the configuration of your model. If you've created a multiselect attribute in the admin, it's backend model is set to Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend. As you can see, this is an EAV model. 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 the sameĀ  2-dimensional array with value and label-keys):

class ImportMethod extends Attribute\Source\AbstractSource
{
    const METHOD_PUSH = 'push';
    const METHOD_PULL = 'pull';

    /**
     * @return array
     */
    public function toArray()
    {
        return [
            self::METHOD_PUSH => __('Push (remote server transfers to us)'),
            self::METHOD_PULL => __('Pull (transfer from remote server)')
        ];
    }

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

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

        return $ret;
    }

    /**
     * @return array
     */
    public function getAllOptions()
    {
        return $this->toOptionArray();
    }
}

That's it! The error will be gone and you'll be able to use your custom source model for multiselect product attributes in Magento 2!

Magento 2 Source Models Product Attributes