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.

7 comments:

  1. Always Helpful and Easy to understand post by Marius.
    Thank you Marius.!!! Thanks A lot...

    ReplyDelete
  2. Hi,

    I have used your code but for source I used product attribute that I created called "special_departments"

    so I have this code:
    public function getAllOptions($withEmpty = false){
    if (is_null($this->_options)){
    $this->_options = array();
    //here you call your extenral API and build an array with possible values like this
    //$this->_options[] = array('label'=>'HERE GOES THE LABEL', 'value'=>'HERE GOES THE VALUE');
    $attributeCode = 'special_department';

    $attribute = Mage::getModel('eav/config')->getAttribute('catalog_product', $attributeCode);
    $allOptions = $attribute->getSource()->getAllOptions(true, true);
    foreach ($allOptions as $instance) {
    $this->_options[] = array('label'=>$instance['label'], 'value'=> $instance['value']);
    }
    }
    $options = $this->_options;
    if ($withEmpty) {
    array_unshift($options, array('value'=>'', 'label'=>''));
    }
    return $options;
    }


    i can see dropdown in admin but after i select it andclick save it refresh page and nothing is saved or selected!?

    any suggestion?

    thank you

    ReplyDelete
  3. hi,

    all working fine, it was not saving because I am using multiselect and I forget to define backend, so it was not getting array. I added this:

    'backend' => 'eav/entity_attribute_backend_array',

    all working perfect.

    Thank you

    ReplyDelete
  4. I say amen to that ! Thanks a lot Marius really spared me some time.

    ReplyDelete
  5. I Seem to be getting the following message:

    Fatal error: Call to undefined method Easylife_Provider_Model_Attribute_Source_Provider::__() in /home/fuxopei/public_html/app/code/local/Easylife/Provider/Model/Attribute/Source/Provider.php on line 10.

    I am unsure what the problem could be. any help please?

    ReplyDelete
    Replies
    1. You are right. This $this->__('Provider 1') should actually be Mage::helper('[module]')->__('Provider 1') or simply Mage::helper('core')->__('Provider 1')

      Delete