Programmatically Create Attributes, Attribute Options & Logging Into the API

Data flexibility is a key component to any eCommerce platform.  Every client will have totally different needs, and expectations of how the site looks and functions.  Every client has completely different needs about what data shows on a website and how it is shown.  Magento uses attributes to define and store custom data, which is nothing new, or complicated.  It’s a simple structure that works extremely well.

Today I will be teaching you how to programmatically create attributes, and when applicable, attribute options programmatically for your Magento install using SOAP requests.

Step 1: Get a SessionKey

First thing to do.  Let’s get a SessionKey so we can gain access to Magento’s wonderful API.
Don’t know how to do that?  No problem! First thing you’ll need to do is log into your admin panel.

01-systemwebservices

System -> WebServices
There are two different pages we’ll have to visit.  You’ll have to make a role first. So click on SOAP/XML-RPC – Roles. Click ‘Add New Role’ in the next screen.

There are two panels in this screen:

  • Role Info
  • Role Resources
  • Role Name is just for display purposes, not necessarily important unless you plan on creating a lot of different roles. The Role Resources panel is a bit more important. This is where you set which functions are allowed to be run for the role you’re creating.  It’s powerful when you have many different people/resources accessing Magento’s API and you want to limit what each user can do. If you feel confident in your security you can simply set the Resource Access to All.  This allows any user assigned to this role to do everything with the API.

    Step 2: Create a User

    Next we need to create a user: System -> Webservices – SOAP/XML-RPC – Users. Click ‘Add New User’ The User info panel loads up first.  The two important fields here are:

  • User Name
  • API Key(and API Key Confirmation all the same)
  • These two fields are your login credentials. Remember them. After filling out the information, go to the User Role panel and assign your new user to the applicable role.

    Step 3: Load C#.NET Project

    In your project you’ll want to add a new service reference to gain access to the API methods. The URL you want to add is: http://{YourURL}/index.php/api/v2_soap?wsdl=1

    05-addwebservicereference1

    Note – Depending on your server configuration you may need to use https instead of http.

    Step 4: Time To Log In

    Once you’ve added the service reference it’s time to login!
    First add the namespace to your codefile

    using ProjectName.ServiceReferenceName;
    

    The object to access all the API endpoints is called Mage_Api_Model_Server_V2_HandlerPortTypeClient
    To create the object properly we’ll use this line of code.

    var proxy = new Mage_Api_Model_Server_V2_HandlerPortTypeClient("Mage_Api_Model_Server_V2_HandlerPort", "http://{YourURL}/index.php/api/v2_soap");
    

    Note – You may need to use https

    Logging in supplies us with a Session Key.  You may want to store this in an easily accessible variable unless you plan on logging in each time you want to perform one task (which you will soon see, we could be performing many tasks in a short time span).

    var sessionKey = Proxy.login(apiUsername, apiUserkey);
    

    Remember the username and apikey you used when you created your webservice user earlier on? That’s where these values come into play.

    Now that you’ve obtained your Session Key we can finally start learning how to create attributes and options.

    how to create attributes and options

    There a few very important pieces of data you’ll need for one attribute:

  • The name of the attribute as it should appear in the backend.
  • The “code” for the attribute – the code can only use the following characters; lowercase letters and “_”. 
  • The code is used for some attribute and attribute option functions so it’s important to use a code that makes sense for any logic you may need to use. The “type” of attribute.

    Magento uses many different attribute types. Some of them function mildly different, some of them very differently. A full list of the attribute types and the values you would need to pass in are shown here:

    10-attributetypes

    In this image the “value” is what you have to pass into Magento.

    Note: This article will be dealing with text, textarea, multiselect, and select types specifically.

    A description of each type

    “text” and “textarea” are basically the same thing, textarea just has a much higher character limit.  Use these when every product could have a different value for this attribute.

  • “select”– Use when there is a set amount of possible values and a product can only have one of these values.
  • “multiselect”– Use when there is a set amount of possible values and a product can have one or more of these values.
  • Check for Existing Attributes

    When looking to create attributes, it is good practice to grab a list of all currently existing attributes:

    var attributeList = GetAttributeList();
    

    This way we can check and make sure an attribute code you’re trying to create doesn’t already exist.  Very useful if you just lazily grab a full collection each run.

    Let’s check to see if it exists.

    var attributeMatch = attributeList.Where(x => x.code.ToLowerInvariant() == code.ToLowerInvariant()).FirstOrDefault();
    

    This will either return a filled object or null.  So the condition to check if this attribute code already exists is.

    if (attributeMatch == null)
    

    If this statement returns true we’re set to create the attribute.  If the statement returns false, you can grab the ID of the attribute from the object returned

    var attributeId = atttributeMatch.attribute_id;
    

    Creating A New Attribute

    So we’re ready to create a new attribute.  The object required to be passed in is a “catalogProductAttributeEntityToCreate” There are some confusing aspects to this object that I will walk through with you. The most confusing aspect is the frontend_label which takes an array of catalogProductAttributeFrontendLabelEntity and contains the following:

  • label
  • store_id
  • The label is how the attribute name looks on the frontend of your site.  The store_id corresponds to which storeview. This label should be shown on and is very useful for multi-language sites.  If you only have one storeview, each storeview should show the same label, you can just use store_id “0” (Default value).  Every FrontEndLabelEntity should have “0” defined.

    So you’ve created a list of FrontEndLabelEntities.  There is one other EXTREMELY important data point to talk about.  Setting this appropriately could save you a big amount of heartache in the future – Scope.

    scope

    Three values are applicable to scope:

    15-scopetypes

    The values here don’t actually correspond to what needs to be passed in.

    The three values that are allowed are:

  • “global”
  • “website”
  • “storeview”
  • Scope is a very important and powerful piece of data that needs to be handled cautiously.  What scope does is define at what level of your Magento install could a different value be defined for a same attribute on a same product.

    Example

    You have 2 websites and 4 storeviews; all storeviews will always show the same data – Make this “global”
    You have 2 websites and 4 storeviews; you want to show different values between the websites – Make this “website”.
    You have 2 websites and  4 storeviews; the different storeviews are for multi-lingual purposes (or each storeview can display a different value) – Make this “storeview”

    Creating the Object

    Now that we’ve got the hard stuff out of the way, let’s create the object required to tell Magento to create a new attribute.

    var attributeToCreate = new catalogProductAttributeEntityToCreate
    {
    attribute_code = code, //attribute code
    frontend_input = attribute.AttributeType.ToLowerInvariant(), //the attribute type - "text", "select" etc.
    frontend_label = frontEndLabelEntityList.ToArray(),
    apply_to = applyTo.ToArray(), //which product types can use this attribute - "simple", "configurable" etc.
    is_comparable = 1,
    is_comparableSpecified = true,
    is_configurable = attribute.IsConfigAttribute ? 1 : 0, //does this attribute define a selector for configurable products?
    is_configurableSpecified = attribute.IsConfigAttribute ? true : false,
    is_required = 0,
    is_requiredSpecified = false,
    is_searchable = 1,
    is_unique = 0,
    is_uniqueSpecified = false,
    is_used_for_promo_rules = 0,
    is_used_for_promo_rulesSpecified = false,
    is_visible_in_advanced_search = 1,
    is_visible_in_advanced_searchSpecified = true,
    is_visible_on_front = 1,
    is_visible_on_frontSpecified = true,
    used_in_product_listing = 1,
    used_in_product_listingSpecified = true,
    scope = "global",
    };
    

    Nothing too fishy here once we get past the the weirdness with Frontendlabels. Now, let’s create it!

    var attributeId = Proxy.catalogProductAttributeCreate(sessionKey, attributeToCreate);
    

    Remember the Session Key we generated when we logged in?  We have to pass this in when we call this api method (and every other single API method!)  This method returns the attributeId of the newly created attribute.

    All good done and dandy!  Let’s start using this attribute!  I’ve got so much cool stuff to show my customers and they’re going to love seeing all the cool stuff we h-NO! Not yet!

    AttributeSets

    Magento has this magical thing called AttributeSets.  I won’t get too into the inner workings of it.  Most people may not need anything more than the default. The default attribute set created seems to always have an Id of 4.  Don’t ask me why.  Just the way things work in Magento!

    Inside each Attribute set are attribute set groups. What’s an attribute set group you ask?  I can answer this by looking at a product information page in the backend!

    20-attributesetgroups

    Each of those panels on the left are an attribute group.  You get to decide which pane your attribute goes in!

    Typically we create a new group for all custom attributes clients need, I do this manually in the backend and it’s always “19” for Magento Enterprise builds.  If you want a list of all current groups in a set adn their Ids, you’re kinda out of luck.  Your only chance is to look at the database manually.  Table eav_attribute_group will have all the info you want. Let’s assign this attribute to an attribute set and an attribute set group.

    var groupingSetSuccessful = Proxy.catalogProductAttributeSetAttributeAdd(sessionKey, attributeId.ToString(), AttributeSetId.ToString(), AttributeSetGroupId.ToString(), sortOrder);
    

    Nothing unknown or mysterious here. sortOrder is kinda finicky and might not return the results expected.  You can sort these manually in the backend though!

    Okay, finally your attribute is ready to go!  If you chose “text”, or “textarea” (or a few others not covered such as “date”) you’re ready to go!  Fill in your products new attribute with data.

    If you chose a “select” or “multiselect” you’ve only just begun.  Most attributes just take a flat text value.  “select” and “multiselect” require you to define a set collection of options to choose from.

    Let’s create some options

     
    We’re going into this assuming you have a List<string> of options required.

    First get a list of already existing options belonging to this attribute

    var existingOptionsList = Proxy.catalogProductAttributeOptions(sessionKey, attributeId.ToString(), StoreView);
    

    You can even grab the labels returned based on storeview, typically I pass in null and compare off default values, but logic really depends on how your integration is set up to work. Let’s start off by setting up a foreach based on your List<string>

    foreach(var option in optionList)
    {
    
    }
    

    Inside this loop, first check and see if the option exists.

    var optionExists = existingOptionsList.Where(x => x.label.ToLowerInvariant() == option.ToLowerInvariant()).FirstOrDefault();
    

    if this returns null, we’ve got a new option to create.  If it returns an object, we can skip and move onto the next option.

    First, create the object Magento expects to create the option. catalogProductAttributeOptionEntityToAdd

    There is one weird point on this object. “label” requires an array of catalogProductAttributeOptionLabelEntity.
    One catalogProductAttributeOptionLabelEntity contains:

  • “store_id” – an array of store ids applicable
  • “value” – what is shown on the frontend
  • Very similar to the weirdness in creating the attribute, the difference is, we can pass in an array of storeids instead of creating an object for each separate storeid. Otherwise we got some pretty basic stuff going on.

    var newOption = new catalogProductAttributeOptionEntityToAdd{
    newOption.is_default = 0;
    newOption.label = labels.ToArray();
    newOption.order = 0;
    }
    

    Nothing out of the ordinary here. Now create that option!

    var optionCreateSuccessful = Proxy.catalogProductAttributeAddOption(sessionKey, attributeId.ToString(), newOption);
    

    That’s it!  We’re done.  We’ve created an attribute, and if options are required for the attribute, we’ve created all the known necessary options! I’ve only given you small snippets of our overall logic that we use to create attributes and options, but these small snippets are the backbones to creating your own logic required to maintain product attributes and options.