Installing Custom Attributes with Your Module

Last modified by Jona on Fri, June 25, 2010 09:06
Source|Old Revisions  

This is an old revision of the document!

This is a quick tutorial on how to install custom product attributes (eav attributes) with your module. It can easily be adapted for other entities’ attributes. It’s also a good starting point for general install scripts.


When the Mage application loads it looks for custom install scripts in the modules sql/mymodule_setup/ folder and checks them against the current module version and the _core_resource table to see whether they should be executed.

Custom attributes (EAV attributes) are available for products, categories, customers and other modules. They are organised into groups (loosely correspond to left-hand tabs in admin’s Edit Product, etc pages) which in turn can be organised into sets (eg Manage Attribute Sets in the backend). They can have options (such as for dropdowns) which are specified in the database for manually created attributes or via class definitions for code created ones. Additionally, attribute values are stored in separate tables according to their datatype (int,varchar,text,decimal,datetime...)

Installing Custom Product Attributes

The following steps can be taken before OR after the module has been installed into Mage.

Step 1: Create the Resource Setup Class

Create the folder MyModule/Model/Resource/Eav/Mysql4/ in your module folder. Create a file called Setup.php and make a class definition as follows:

  1. class MyModule_Model_Resource_Eav_Mysql4_Setup extends Mage_Eav_Model_Entity_Setup
  2. {
  3. }

...where MyModule_Model is what is defined as the base model in config.xml:

  1. <global>
  2.         <models>
  3.             <mymodname>
  4.                 <class>MyModule_Model</class>
  5.             </mymodname>
  6.         </models>
  7. ...
  8. </global>

Note, mymodname defines the “short” version of the modules name used in Mage::getModel(’mymodname/mymodel’).

We’ll come back to this file in just a minute...

Step 2: Define the installer resource in config.xml

Add a <resource> block under <global> in the module’s etc/config.xml file:

  1. <global>
  2.   <models>
  3.     <mymodname>
  4.       <class>MyModule_Model</class>
  5.     </mymodname>
  6.   </models>
  8.   <resources>
  9.     <mymodname_setup>
  10.       <setup>
  11.         <module>MyModule</module>
  12.         <class>MyModule_Model_Resource_Eav_Mysql4_Setup</class>
  13.       </setup>
  15.       <connection>
  16.         <use>core_setup</use>
  17.       </connection>
  18.     </mymodname_setup>
  20.     <mymodname_write>
  21.       <connection>
  22.         <use>core_write</use>
  23.       </connection>
  24.     </mymodname_write>
  25.     <mymodname_read>
  26.       <connection>
  27.         <use>core_read</use>
  28.       </connection>
  29.     </mymodname_read>
  30.   </resources>
  32. ...
  34. </global>

Not certain whether <mymodname> and <mymodname_setup> need to correlate. Nor am I certain what the MyModule in <setup> needs to correspond to. The value in <class>, however, needs to be the same as the class you create in step 1.

Important: <mymodname_setup> is used to register the resource you’ll be using, in the database and specifically the core_resource table. That is where all module resources, core, local and community are registered. Unfortunately there is no differentiation between core, local and community which means that if your module has the same name as an existing module, regardless of the namespace, and both modules use <mymodname_setup>, they’ll clash. I’m not aware whether this is the whole extent of the repercussions, but this will stop mysql4-install-x.x.x.php from running. To avoid this you can use <mynamespace_mymodname> instead of <mymodname_setup>.

Step 3: Create an install script

Create the folders/file MyModule/sql/mymodname_setup/mysql4-install-1.2.3.php where mymodname_setup corresponds to the XML entity you just added in config.xml.

Also ensure the 1.2.3 corresponds to the module version in config.xml

  1. <config>
  2.     <modules>
  3.         <MyModule>
  4.             <version>1.2.3</version>
  5.         </MyModule>
  6.     </modules>
  7. ...
  8. </config>

In the file add the following code:

  1. $installer = $this;
  3. $installer->installEntities();

$this is an instance of your MyModule_Model_Resource_Eav_Mysql4_Setup class created in the first step. Additionally, by extending Mage_Eav_Model_Entity_Setup you get access to the nifty installEntities() method which we’ll discuss below...

Step 4: Define your entities

Go back to your Setup.php file from the first step. By extending Mage_Eav_Model_Entity_Setup we get access to 2 important methods. The first is installEntities() which is called by our install script. The second is getDefaultEntities() which is called by installEntities() and defines our custom attributes. It returns a nested array defining entity types and their attributes.


From Magento 1.4, don’t forget to add these following lines. Between ‘table’ and ‘attributes’ elements. ;)

Without that, the General, Meta, Images, ... Tabs will not showing up in the product edit page.

  1. 'additional_attribute_table' => 'catalog/eav_attribute',
  2.                 'entity_attribute_collection' => 'catalog/category_attribute_collection',
  1. class MyModule_Model_Resource_Eav_Mysql4_Setup extends Mage_Eav_Model_Entity_Setup
  2. {
  4.     /**
  5.      * @return array
  6.      */
  7.     public function getDefaultEntities()
  8.     {
  9.         return array(
  10.             'catalog_product' => array(
  11.                 'entity_model'      => 'catalog/product',
  12.                 'attribute_model'   => 'catalog/resource_eav_attribute',
  13.                 'table'             => 'catalog/product',
  14.                 'attributes'        => array(
  15.                     'myattribcode' => array(
  16.                         'group'             => 'Group/Tab',
  17.                         'label'             => 'My Attrib Label',
  18.                         'type'              => 'int',
  19.                         'input'             => 'boolean',
  20.                         'default'           => '0',
  21.                         'class'             => '',
  22.                         'backend'           => '',
  23.                         'frontend'          => '',
  24.                         'source'            => '',
  25.                         'global'            => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
  26.                         'visible'           => true,
  27.                         'required'          => false,
  28.                         'user_defined'      => false,
  29.                         'searchable'        => false,
  30.                         'filterable'        => false,
  31.                         'comparable'        => false,
  32.                         'visible_on_front'  => false,
  33.                         'visible_in_advanced_search' => false,
  34.                         'unique'            => false
  35.                     ),
  37.                    ...
  38.                )
  39.            ),
  40.            ...  // define attributes for other model entities here
  41.       );

A good reference for all this is app/code/core/Mage/Catalog/Resource/Eav/Mysql4/Setup.php

entity_model and table can be changed to add attributes to other entities (eg Customers). attributes is an array of arrays - one for each attribute you wish to define. To get an idea about the values possible, create your custom attributes manually via Product > Manage Attributes and then have a peak _eav_attribute table and replicate the values in this array.

A few of the less obvious ones:

class Validation class for data. Example values: validate-digits, validate-number
backend Model for handling input in the backend (eg customer/customer_attribute_backend_password)
frontend Model for handling display of attribute on the frontend (eg catalog/product_attribute_frontend_image)
source Model defining values for select boxes/dropdowns (see below)

Step 5: Run (or re-run) the install script

Now be sure to disable or clear the EAV cache and reload the edit product page in the backend (or any page I believe). The new attributes should be available to edit in the default set!

If you mess up or wish to re-execute the install script, find the entry in the _core_resource table that corresponds to your module and remove it. Then reload the page.

The installEntities() method is self-cleaning so no need to remove the database entries manually.

Appendix A: Custom Dropdowns Options

Above we mentioned that the source parameter in an attribute could be assigned to a model whose values would then populate the drop down. To do so we create a class anywhere in the MyModule/Models/ folder, for example: MyModule/Models/Product/Attribute/Source/Unit.php:

  1. class MyModule_Model_Product_Attribute_Source_Unit extends Mage_Eav_Model_Entity_Attribute_Source_Abstract
  2. {
  3.     public function getAllOptions()
  4.     {
  5.         if (!$this->_options) {
  6.             $this->_options = array(
  7.                 array(
  8.                     'value' => '',
  9.                     'label' => '',
  10.                 ),
  11.                 array(
  12.                     'value' => '1',
  13.                     'label' => 'Day',
  14.                 ),
  15.                 array(
  16.                     'value' => '2',
  17.                     'label' => 'Week',
  18.                 ),
  19.                 array(
  20.                     'value' => '3',
  21.                     'label' => 'Month',
  22.                 ),
  23.                 array(
  24.                     'value' => '4',
  25.                     'label' => 'Year',
  26.                 )
  27.             );
  28.         }
  29.         return $this->_options;
  30.     }
  31. }

getAllOptions() is the only method that need be defined. It is also not necessary for this class to extend Mage_Eav_Model_Entity_Attribute_Source_Abstract (though you may have some more methods to define if not ??).

Now all you do is add:

  1.     'input'     => 'select',
  2.     'source'    => 'mymodname/product_attribute_source_unit',

to the appropriate attribute in the getDefaultEntities() above.

Note, the class name must correspond to the folder layout. Additionally, MyModule_Model and mymodname must correspond to the model definition in config.xml:

  1. <global>
  2.   <models>
  3.     <mymodname>
  4.       <class>MyModule_Model</class>
  5.     </mymodname>
  6.   </models>
  7. ...
  8. </global>

Appendix B: Upgrade scripts

In addition to executing install scripts for each version of your module, it is also possible to have “upgrade” scripts whose filenames are of the form **mysql4-upgrade-1.2.3-1.2.4.php. I’m guessing these work the same way...

Appendix C: Easier way to add options

Add the following array notation to your attribute :

'option' => array (
   'value' => array('notates_to_zero'=>array(0=>'Option Name'))

To have your model automagically add the appropriate options, there’s no need to create a model to define your options, you can do it via this script :)

Appendix D: quick func to pre-populate attribute set(s) + modification to addAttribute method

If you add this method, you can call $installer→createNewAttributeSet($name); from your script and it will pre-populate it from the default attribute set :

public function createNewAttributeSet($name) {
        $modelSet = Mage::getModel('eav/entity_attribute_set')
            ->setEntityTypeId(4) // 4 == "catalog/product"
        $modelSet->initFromSkeleton(4)->save(); // same thing

Note: ‘4’ is hard-coded as it’s the catalog/product entity_type_id, change to suit your script.

The following modification plus the addition of ‘attribute_set’ key in your setup script will make sure your custom groups don’t get added to the default or other groups:

1) Add “addAttribute” override to your Setup.php - copy the whole Method , just change the methods listed

if (!empty($attr['group'])) {
               $sets = $this->_conn->fetchAll('select * from '.$this->getTable('eav/attribute_set').' where entity_type_id=?', $entityTypeId);
            foreach ($sets as $set) {
                if (!empty($attr['attribute_set'])) {
                    if ($attr['attribute_set'] == $set['attribute_set_name']) {
                        $this->addAttributeGroup($entityTypeId, $set['attribute_set_id'], $attr['group']);
                        $this->addAttributeToSet($entityTypeId, $set['attribute_set_id'], $attr['group'], $code, $sortOrder);    
                } else {
                    $this->addAttributeGroup($entityTypeId, $set['attribute_set_id'], $attr['group']);
                    $this->addAttributeToSet($entityTypeId, $set['attribute_set_id'], $attr['group'], $code, $sortOrder);
        if (empty($attr['is_user_defined'])) {
               $sets = $this->_conn->fetchAll('select * from '.$this->getTable('eav/attribute_set').' where entity_type_id=?', $entityTypeId);
            foreach ($sets as $set) {
                if (!empty($attr['attribute_set'])) {
                    if ($attr['attribute_set'] == $set['attribute_set_name']) {
                        $this->addAttributeToSet($entityTypeId, $set['attribute_set_id'], $attr['group'], $code, $sortOrder);
                } else {
                    $this->addAttributeToSet($entityTypeId, $set['attribute_set_id'], $this->_generalGroupName, $code, $sortOrder);

Then an example snippet from the setup node :

'myAttrib' => array(
                                            'attribute_set'     => 'myAttribSet',
                                            'group'              => 'myAttribGroup',                                            
                                            'type'              => 'varchar',
                                            'input'             => 'multiselect',
                                            'class'             => 'validate-number',
                                            'backend'            => 'eav/entity_attribute_backend_array',
                                            'frontend'          => '',
                                            'source'            => '',
                                            'global'            => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
                                            'visible'           => true,
                                            'required'          => false,
                                            'user_defined'      => false,
                                            'searchable'        => true,
                                            'filterable'        => true,
                                            'comparable'        => true,
                                            'option'            => array (
                                                'value' => array('optionone'=>array(0=>'My First Option'))
                                            'visible_on_front'  => true,
                                            'visible_in_advanced_search' => true,
                                            'unique'            => false

Note: the above example shows also the proper functional notation for adding an option to the attribute. Then you call this method from your installer script:


Hope this helps someone....