Magento – Get Custom attribute in API

apimagento-1.9soap-api-v2

<?php  require_once "app/Mage.php";

    Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
    $installer = new Mage_Sales_Model_Mysql4_Setup;
    // change details below:
    $attribute  = array(
        'type' => 'int',
        'label'=> 'IceCatID',
        'input' => 'text',
        'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
        'visible' => true,
        'required' => false,
        'user_defined' => true,
        'default' => "",
        'group' => "General Information"
    );
    $installer->addAttribute('catalog_category', 'IceCatID', $attribute);
    $installer->endSetup();?>

I created Custom attribute for category it's working fine but how to get api in this custom attribute we are developing Android App.

Best Answer

If you're using the SOAP Api try this:

  • Create a basic custom module and ensure that has <Mage_Catalog/> as dependency

Then add an api config file for overwrite the "category" complex types definitions:

app/code/{pool}/{vendor}/{module}/etc/wsdl.xml

And add this contents:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:typens="urn:{{var wsdl.name}}" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             name="{{var wsdl.name}}" targetNamespace="urn:{{var wsdl.name}}">
    <types>
        <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:Magento">
        <complexType name="catalogCategoryEntity">
            <all>
                <element name="category_id" type="xsd:int"/>
                <element name="parent_id" type="xsd:int"/>
                <element name="name" type="xsd:string"/>
                <element name="is_active" type="xsd:int"/>
                <element name="position" type="xsd:int"/>
                <element name="level" type="xsd:int"/>
                <element name="children" type="typens:ArrayOfCatalogCategoryEntities"/>

                <!-- ADD YOUR ATTRIBUTE TO ANY DESIRED NODE -->
                <!-- CUSTOM ATTRIBUTE -->  
                <element name="ice_cat_id" type="xsd:int"/>
                <!-- CUSTOM ATTRIBUTE -->

            </all>
        </complexType>
        <complexType name="catalogCategoryEntityNoChildren">
            <all>
                <element name="category_id" type="xsd:int"/>
                <element name="parent_id" type="xsd:int"/>
                <element name="name" type="xsd:string"/>
                <element name="is_active" type="xsd:int"/>
                <element name="position" type="xsd:int"/>
                <element name="level" type="xsd:int"/>
            </all>
        </complexType>
        <complexType name="ArrayOfCatalogCategoryEntitiesNoChildren">
            <complexContent>
                <restriction base="soapenc:Array">
                    <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:catalogCategoryEntityNoChildren[]"/>
                </restriction>
            </complexContent>
        </complexType>
        <complexType name="catalogCategoryTree">
            <all>
                <element name="category_id" type="xsd:int"/>
                <element name="parent_id" type="xsd:int"/>
                <element name="name" type="xsd:string"/>
                <element name="position" type="xsd:int"/>
                <element name="level" type="xsd:int"/>
                <element name="children" type="typens:ArrayOfCatalogCategoryEntities"/>
            </all>
        </complexType>
        <complexType name="catalogCategoryEntityCreate">
            <all>
                <element name="name" type="xsd:string" minOccurs="0"/>
                <element name="is_active" type="xsd:int" minOccurs="0"/>
                <element name="position" type="xsd:int" minOccurs="0"/>
                <!-- position parameter is deprecated, category anyway will be positioned in the end of list
                    and you can not set position directly, use catalog_category.move instead -->
                <element name="available_sort_by" type="typens:ArrayOfString" minOccurs="0"/>
                <element name="custom_design" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_apply" type="xsd:int" minOccurs="0"/>
                <element name="custom_design_from" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_to" type="xsd:string" minOccurs="0"/>
                <element name="custom_layout_update" type="xsd:string" minOccurs="0"/>
                <element name="default_sort_by" type="xsd:string" minOccurs="0"/>
                <element name="description" type="xsd:string" minOccurs="0"/>
                <element name="display_mode" type="xsd:string" minOccurs="0"/>
                <element name="is_anchor" type="xsd:int" minOccurs="0"/>
                <element name="landing_page" type="xsd:int" minOccurs="0"/>
                <element name="meta_description" type="xsd:string" minOccurs="0"/>
                <element name="meta_keywords" type="xsd:string" minOccurs="0"/>
                <element name="meta_title" type="xsd:string" minOccurs="0"/>
                <element name="page_layout" type="xsd:string" minOccurs="0"/>
                <element name="url_key" type="xsd:string" minOccurs="0"/>
                <element name="include_in_menu" type="xsd:int" minOccurs="0"/>
            </all>
        </complexType>
        <complexType name="catalogCategoryInfo">
            <all>
                <element name="category_id" type="xsd:string"/>
                <element name="is_active" type="xsd:int"/>
                <element name="position" type="xsd:string"/>
                <element name="level" type="xsd:string"/>
                <element name="parent_id" type="xsd:string"/>
                <element name="all_children" type="xsd:string"/>
                <element name="children" type="xsd:string"/>
                <element name="created_at" type="xsd:string" minOccurs="0"/>
                <element name="updated_at" type="xsd:string" minOccurs="0"/>
                <element name="name" type="xsd:string" minOccurs="0"/>
                <element name="url_key" type="xsd:string" minOccurs="0"/>
                <element name="description" type="xsd:string" minOccurs="0"/>
                <element name="meta_title" type="xsd:string" minOccurs="0"/>
                <element name="meta_keywords" type="xsd:string" minOccurs="0"/>
                <element name="meta_description" type="xsd:string" minOccurs="0"/>
                <element name="path" type="xsd:string" minOccurs="0"/>
                <element name="url_path" type="xsd:string" minOccurs="0"/>
                <element name="children_count" type="xsd:int" minOccurs="0"/>
                <element name="display_mode" type="xsd:string" minOccurs="0"/>
                <element name="is_anchor" type="xsd:int" minOccurs="0"/>
                <element name="available_sort_by" type="typens:ArrayOfString" minOccurs="0"/>
                <element name="custom_design" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_apply" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_from" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_to" type="xsd:string" minOccurs="0"/>
                <element name="page_layout" type="xsd:string" minOccurs="0"/>
                <element name="custom_layout_update" type="xsd:string" minOccurs="0"/>
                <element name="default_sort_by" type="xsd:string" minOccurs="0"/>
                <element name="landing_page" type="xsd:int" minOccurs="0"/>
            </all>
        </complexType>
    </schema>
  </types>
</definitions>

This is the full list of all category related nodes/actions so add your attribute to any of them. (I just added it to catalogCategoryEntity)

NOTES

  • By convention your attribute code should be ice_cat_id instead IceCatID you can still using "IceCatID" for label.
  • Keep just the modified <complex type>nodes in your wsdl file.
  • Clean Config/WSDL Cache(s) and if you don't see the changes just restart php to flush php wsdl cache.

EDIT: About WSDL Definition in Magento To clarify some concept:

We are using the WSDL Configuration to update <complexType> definitions. Every <complexType> node defines a data type that isn't standard (like 'int')

Every SOAP call has it own definition, if you make a call to catalogCategoryInfo: you will get a response body with a node called 'info' as defined here:

app/code/core/Mage/Catalog/etc/wsdl.xml

    ...
    <message name="catalogCategoryInfoRequest">
        <part name="sessionId" type="xsd:string"/>
        <part name="categoryId" type="xsd:int"/>
        <part name="storeView" type="xsd:string"/>
        <part name="attributes" type="typens:ArrayOfString"/>
    </message>
    <message name="catalogCategoryInfoResponse">
        <part name="info" type="typens:catalogCategoryInfo"/>
    </message>
    ...

This Service definition is telling us that requests to catalogCategoryInfo will give us a node called "info" of the catalogCategoryInfo type. This means that if you need your custom attribute for that call (catalogCategoryInfo) you have to add your attribute to the type definition (in your module wsdl.xml file):

app/code/{pool}/{vendor}/{module}/etc/wsdl.xml (part)

        ...
        <complexType name="catalogCategoryInfo">
            <all>
                <!-- MY CUSTOM ATTRIBUTE -->
                <element name="ice_cat_id" type="xsd:int"/>
                <!-- MY CUSTOM ATTRIBUTE -->
                <element name="category_id" type="xsd:string"/>
                <element name="is_active" type="xsd:int"/>
                <element name="position" type="xsd:string"/>
                <element name="level" type="xsd:string"/>
                <element name="parent_id" type="xsd:string"/>
                <element name="all_children" type="xsd:string"/>
                <element name="children" type="xsd:string"/>
                <element name="created_at" type="xsd:string" minOccurs="0"/>
                <element name="updated_at" type="xsd:string" minOccurs="0"/>
                <element name="name" type="xsd:string" minOccurs="0"/>
                <element name="url_key" type="xsd:string" minOccurs="0"/>
                <element name="description" type="xsd:string" minOccurs="0"/>
                <element name="meta_title" type="xsd:string" minOccurs="0"/>
                <element name="meta_keywords" type="xsd:string" minOccurs="0"/>
                <element name="meta_description" type="xsd:string" minOccurs="0"/>
                <element name="path" type="xsd:string" minOccurs="0"/>
                <element name="url_path" type="xsd:string" minOccurs="0"/>
                <element name="children_count" type="xsd:int" minOccurs="0"/>
                <element name="display_mode" type="xsd:string" minOccurs="0"/>
                <element name="is_anchor" type="xsd:int" minOccurs="0"/>
                <element name="available_sort_by" type="typens:ArrayOfString" minOccurs="0"/>
                <element name="custom_design" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_apply" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_from" type="xsd:string" minOccurs="0"/>
                <element name="custom_design_to" type="xsd:string" minOccurs="0"/>
                <element name="page_layout" type="xsd:string" minOccurs="0"/>
                <element name="custom_layout_update" type="xsd:string" minOccurs="0"/>
                <element name="default_sort_by" type="xsd:string" minOccurs="0"/>
                <element name="landing_page" type="xsd:int" minOccurs="0"/>
            </all>
        </complexType>
        ...

Looking at the app/code/core/Mage/Catalog/etc/wsdl.xmlfile you will find which <compleType> definition node must edit for each message (When I say "add your attribute to every needed node" means that). So the workaround to do it would be something like that:

  1. Determine which SOAP service I need to consume.
  2. Find that service response definition in app/code/core/Mage/Catalog/etc/wsdl.xml
  3. Look at that definition and Find which node I need to modify.
  4. Use our app/code/{pool}/{vendor}/{module}/etc/wsdl.xml file to overwrite the definition (adding our custom attribute(s) there).
  5. Clean Magento Cache
  6. Clean Php Wsdl cache (or simply restart php service)

Note that the same workaround could be used if you want to remove some unused attributes to perform fastest requests.

EDIT 2: Missing Attributes for some methods

I forgot to mention that for some API methods the WSDL definition rewrite isn't enough. these methods are:

  • catalogCategoryTree

  • catalogCategoryLevel

In both cases the category data is actually loaded by collection so you need to rewrite these methods in order to get your custom attribute available in the response.

To add an attribute to the catalogCategoryTree API method

First we have to overwrite the Mage_Catalog_Model_Category_Api::tree method:

public function tree($parentId = null, $store = null)
{
    if (is_null($parentId) && !is_null($store)) {
        $parentId = Mage::app()->getStore($this->_getStoreId($store))->getRootCategoryId();
    } elseif (is_null($parentId)) {
        $parentId = 1;
    }

    /* @var $tree Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Tree */
    $tree = Mage::getResourceSingleton('catalog/category_tree')
        ->load();

    $root = $tree->getNodeById($parentId);

    if($root && $root->getId() == 1) {
        $root->setName(Mage::helper('catalog')->__('Root'));
    }

    $collection = Mage::getModel('catalog/category')->getCollection()
        ->setStoreId($this->_getStoreId($store))
        ->addAttributeToSelect('name')
        ->addAttributeToSelect('is_active')
        // Custom Attribute
        ->addAttributeToSelect('ice_cat_id');

    $tree->addCollectionData($collection, true);

    return $this->_nodeToArray($root);
}

Then we need to modify the Mage_Catalog_Model_Category_Api::_nodeToArray method too since return data is hardcoded:

protected function _nodeToArray(Varien_Data_Tree_Node $node)
{
    // Only basic category data
    $result = array();
    $result['category_id'] = $node->getId();
    $result['parent_id']   = $node->getParentId();
    $result['name']        = $node->getName();
    $result['is_active']   = $node->getIsActive();
    $result['position']    = $node->getPosition();
    $result['level']       = $node->getLevel();
    $result['children']    = array();

    // Custom Attribute
    $result['ice_cat_id']  = $node->getIceCatId();

    foreach ($node->getChildren() as $child) {
        $result['children'][] = $this->_nodeToArray($child);
    }

    return $result;
}

To add an attribute to the catalogCategoryLevel API method

We need to modify the Mage_Catalog_Model_Category_Api::level

public function level($website = null, $store = null, $categoryId = null)
{
    $ids = array();
    $storeId = Mage_Catalog_Model_Category::DEFAULT_STORE_ID;

    // load root categories of website
    if (null !== $website) {
        try {
            $website = Mage::app()->getWebsite($website);
            if (null === $store) {
                if (null === $categoryId) {
                    foreach ($website->getStores() as $store) {
                        /* @var $store Mage_Core_Model_Store */
                        $ids[] = $store->getRootCategoryId();
                    }
                } else {
                    $ids = $categoryId;
                }
            } elseif (in_array($store, $website->getStoreIds())) {
                $storeId = Mage::app()->getStore($store)->getId();
                $ids = (null === $categoryId)? $store->getRootCategoryId() : $categoryId;
            } else {
                $this->_fault('store_not_exists');
            }
        } catch (Mage_Core_Exception $e) {
            $this->_fault('website_not_exists', $e->getMessage());
        }
    }
    elseif (null !== $store) {
        // load children of root category of store
        if (null === $categoryId) {
            try {
                $store = Mage::app()->getStore($store);
                $storeId = $store->getId();
                $ids = $store->getRootCategoryId();
            } catch (Mage_Core_Model_Store_Exception $e) {
                $this->_fault('store_not_exists');
            }
        }
        // load children of specified category id
        else {
            $storeId = $this->_getStoreId($store);
            $ids = (int)$categoryId;
        }
    }
    // load all root categories
    else {
        $ids = (null === $categoryId)? Mage_Catalog_Model_Category::TREE_ROOT_ID : $categoryId;
    }

    $collection = Mage::getModel('catalog/category')->getCollection()
        ->setStoreId($storeId)
        ->addAttributeToSelect('name')
        ->addAttributeToSelect('is_active')
        // ---
        // CUSTOM ATTRIBUTE
        ->addAttributeToSelect('ice_cat_id');

    if (is_array($ids)) {
        $collection->addFieldToFilter('entity_id', array('in' => $ids));
    } else {
        $collection->addFieldToFilter('parent_id', $ids);
    }

    // Only basic category data
    $result = array();
    foreach ($collection as $category) {
        /* @var $category Mage_Catalog_Model_Category */
        $result[] = array(
            'category_id' => $category->getId(),
            'parent_id'   => $category->getParentId(),
            'name'        => $category->getName(),
            'is_active'   => $category->getIsActive(),
            'position'    => $category->getPosition(),
            'level'       => $category->getLevel()
            // ---
            // CUSTOM ATTRIBUTE
            'ice_cat_id'  => $category->getIceCatId()
        );
    }

    return $result;
}

Alternatively you can overwrite hook the event catalog_category_collection_save_after and add your attribute by default every time the collection is loaded but you will still need to change the _nodeToArray() and level methods.