Friday, November 2, 2012

Product attribute with custom options

Magento gives you the possibility to create dropdown product attributes. The problem I have sometimes is that the options for all the dropdown attributes are all in one single table. So for each option I get a (somehow) random ID. There are cases that I really need to know the id of one specific option and I don't want to hard code it. The solution for this is to have a custom source model for your dropdown attributes. The following example creates an attribute called 'Provider' with custom options. For the purpose of the example I'm going to use the namespace 'Easylife' and my extension is going to be names 'Provider'.
Here are the files you need to create:
app/code/local/Easylife/Provider/etc/config.xml - the extension config file
<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Provider>
            <version>0.0.1</version>
        </Easylife_Provider>
    </modules>
    <global>
        <models>
            <provider>
                <class>Easylife_Provider_Model</class>
            </provider>
        </models>
        <resources>
            <easylife_provider_setup>
                <setup>
                    <module>Easylife_Provider</module>
                    <class>Mage_Catalog_Model_Resource_Eav_Mysql4_Setup</class>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </easylife_provider_setup>
        </resources>
    </global>
</config>
app/code/local/Easylife/Provider/sql/easylife_provider_setup/mysql4-install-0.0.1.php - this will add an attribute named 'Provider' to your product
<?php
$this->startSetup();
//if you want to add the attribute to the category instead of the product change on the line below 'catalog_product' to 'catalog_category'
$this->addAttribute('catalog_product', 'provider', array(
        'group'                => 'General',
        'type'              => 'int',
        'backend'           => '',
        'frontend_input'    => '',
        'frontend'          => '',
        'label'             => 'Provider',
        'input'             => 'select',
        'class'             => '',
        'source'            => 'provider/attribute_source_provider',
        'global'             => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,//can be SCOPE_WEBSITE or SCOPE_STORE
        'visible'           => true,
        'used_in_product_listing' =>true,//can also be false
        'frontend_class'     => '',
        'required'          => false,//can be true
        'user_defined'      => true,
        'default'           => '',
        'searchable'        => false,//can be true
        'filterable'        => false,//can be true
        'comparable'        => false,//can be true
        'visible_on_front'  => false,//can be true
        'unique'            => false,
        'position'            => 60,//put any number here
    ));
$this->endSetup();
app/code/local/Easylife/Provider/Model/Attribute/Source/Provider.php - this is the source of you attribute
<?php
class Easylife_Provider_Model_Attribute_Source_Provider extends Mage_Eav_Model_Entity_Attribute_Source_Abstract{
    protected $_options = null;
    public function getAllOptions($withEmpty = false){
        if (is_null($this->_options)){
            $this->_options = array();
                       //$this->_options[] = array('label'=>'HERE GOES THE LABEL', 'value'=>'HERE GOES THE VALUE');
            //as example
            $this->_options[] = array('label'=> $this->__('Provider 1'), value=>1);
            $this->_options[] = array('label'=> $this->__('Provider 2'), value=>2);
            $this->_options[] = array('label'=> $this->__('Provider 3'), value=>3);
        }
        $options = $this->_options;
        if ($withEmpty) {
            array_unshift($options, array('value'=>'', 'label'=>''));
        }
        return $options;
    }
    public function getOptionText($value)
    {
        $options = $this->getAllOptions(false);

        foreach ($options as $item) {
            if ($item['value'] == $value) {
                return $item['label'];
            }
        }
        return false;
    }
    public function getFlatColums()
    {
        $attributeCode = $this->getAttribute()->getAttributeCode();
        $column = array(
            'unsigned'  => false,
            'default'   => null,
            'extra'     => null
        );

        if (Mage::helper('core')->useDbCompatibleMode()) {
            $column['type']     = 'int(10)';
            $column['is_null']  = true;
        } else {
            $column['type']     = Varien_Db_Ddl_Table::TYPE_SMALLINT;
            $column['length']   = 10;
            $column['nullable'] = true;
            $column['comment']  = $attributeCode . ' column';
        }

        return array($attributeCode => $column);
    }
    public function getFlatUpdateSelect($store)
    {
        return Mage::getResourceModel('eav/entity_attribute')
            ->getFlatUpdateSelect($this->getAttribute(), $store);
    }
}
app/etc/modules/Easylife_Provider.xml - the module declaration
<?xml version="1.0"?>
<config>
    <modules>
        <Easylife_Provider>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Catalog />
            </depends>
        </Easylife_Provider>
    </modules>
</config>
That's it. Clear the cache and try again. If you choose to include the attribute in the product listing or if you made it filtrable
'used_in_product_listing' =>true,
...
'filterable'        => false
then you need to rebuild your indexes. Now if you need to get all the products that belong to 'Provider 1' all you need to do is this:
$products = Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('provider', 1);
Enjoy, Marius.