Creating or updating products with Magento SOAP v2 API - a very challenging adventure

In one of our current projects we have to set up a Magento shop for a customer. This project includes the development of an import / export interface to his ERP system. First, I had to make the decision which of the offered Magento Core API's I'll use: XML-RPC, SOAP or SOAP v2. I personally dislike XML-RPC webservices, so I had a closer look at the older SOAP API and the newer v2 one.

Deciding between the two SOAP alternatives - or "start the fight with the SOAP v2 API"

The biggest advantage of the older version is that is far better documented than the v2 interface - which grade of documentation goes closely to zero. But nevertheless I don't like the architecture of the older SOAP interface. v2 is more document-oriented and better to use. But again: in fact it isn't documented at all. The only thing you can rely on, is the WSDL itself. And if you encounter a problem, you can either try to find a solution with Google's help in some forums or - if your problem still remains - take the trial & error approach and try to find a solution on your own. As there aren't too many search results on Magento Soap v2 problems, you'll might get into the same situation like me, where you have to find a solution on your own. This can be very frustrating, especially if you're used to call and create Webservices with Java and now are facing an undocumented SOAP Webservice with PHP the first time.

How to add the manufacturer or other additional attributes to a product creation or update call

After solving some basic problems, I was very soon able to create and update products and their basic attributes like sku, price, description, status, etc. But when I tried to set the manufacturer of the product, I began to struggle with this simple-looking demand. Every time I tried to update the product, the manufacturer wasn't set. When I logged the request I had sent, I saw, that the SoapClient didn't send the manufacturer information. That means, I didn't set the right types and I violated the WSDL, so the SoapClient wasn't able to translate it and stripped it out of the request.

The difference between basic and additional attributes of a Magento product

Why did the SoapClient strip out the manufacturer info? First, we should know, that a Magento product has some basic attributes, which can be directly set (like sku, name, price, description, etc) and additional attributes. All the self-defined attributes logically go there. The "manufacturer" attribute is pre-defined by Magento, but not included in the attribute set by default. If you include it, it is also part of the additional attributes of the product. Let's now have a short look at the WSDL and examine the "catalogProductCreateEntity" type, which is needed both for creations and upates of products:

<complexType name="catalogProductCreateEntity">
    <all>
        <element minOccurs="0" name="categories" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="websites" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="name" type="xsd:string"/>
        <element minOccurs="0" name="description" type="xsd:string"/>
        <element minOccurs="0" name="short_description" type="xsd:string"/>
        <element minOccurs="0" name="weight" type="xsd:string"/>
        <element minOccurs="0" name="status" type="xsd:string"/>
        <element minOccurs="0" name="url_key" type="xsd:string"/>
        <element minOccurs="0" name="url_path" type="xsd:string"/>
        <element minOccurs="0" name="visibility" type="xsd:string"/>
        <element minOccurs="0" name="category_ids" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="website_ids" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="has_options" type="xsd:string"/>
        <element minOccurs="0" name="gift_message_available" type="xsd:string"/>
        <element minOccurs="0" name="price" type="xsd:string"/>
        <element minOccurs="0" name="special_price" type="xsd:string"/>
        <element minOccurs="0" name="special_from_date" type="xsd:string"/>
        <element minOccurs="0" name="special_to_date" type="xsd:string"/>
        <element minOccurs="0" name="tax_class_id" type="xsd:string"/>
        <element minOccurs="0" name="tier_price" type="typens:catalogProductTierPriceEntityArray"/>
        <element minOccurs="0" name="meta_title" type="xsd:string"/>
        <element minOccurs="0" name="meta_keyword" type="xsd:string"/>
        <element minOccurs="0" name="meta_description" type="xsd:string"/>
        <element minOccurs="0" name="custom_design" type="xsd:string"/>
        <element minOccurs="0" name="custom_layout_update" type="xsd:string"/>
        <element minOccurs="0" name="options_container" type="xsd:string"/>
        <element minOccurs="0" name="additional_attributes" type="typens:associativeArray"/>
        <element minOccurs="0" name="stock_data" type="typens:catalogInventoryStockItemUpdateEntity"/>
    </all>
</complexType>

What we can see here, are all the product attributes that can be set directly because the have their own element defined, and an element called "additional_attributes", which stands next to last. This is the element where we have to place the manufacturer. Its type is "typens:associativeArray", which is also defined in the WSDL. This type is based on the standard "soapenc:Array" type. It's array type is defined as "typens:associativeEntity[]", which is again part of the Magento WSDL. This "associativeEntity" type consists of two string elements: key and value:

<complexType name="associativeEntity">
    <all>
        <element name="key" type="xsd:string"/>
        <element name="value" type="xsd:string"/>
    </all>
</complexType>
<complexType name="associativeArray">
    <complexContent>
        <restriction base="soapenc:Array">
            <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:associativeEntity[]"/>
        </restriction>
    </complexContent>
</complexType>

Building this structure in PHP

Taking the time to analyze and understand the structure of the WSDL was a very big step towards the solution but there were still some problems about where to create an object and where to create an array. Some experiments, coffees and lots of swear words later I finally reached the solution:

The catalogProductCreateEntity itself has to be an stdClass object of course (like all the other request entities in this Webservice). We directly add all the direct attributes as string properties. For the "additional_attributes" property we create an array and then populate it with an stdClass object for every additional attribute we want to add. And these objects have an property "key" and a "value" one. For the manufacturer, our key is "manufacturer" and the value is an ID. So we finally get this call:

$client = new SoapClient($pathToWsdl); // set path to your Magento WSDL
$session = $client->login($apiuser, $apikey); // specify username and password

$catalogProductCreateEntity = new stdClass();
$additionalAttrs = array();

$catalogProductCreateEntity->name = "product name";
$catalogProductCreateEntity->description = "description";
$catalogProductCreateEntity->short_description = "desc";
$catalogProductCreateEntity->status = "1";
$catalogProductCreateEntity->price = "99";
$catalogProductCreateEntity->tax_class_id = "2";
/* you can add other direct attributes here */

$manufacturer = new stdClass();
$manufacturer->key = "manufacturer";
$manufacturer->value = "3";
$additionalAttrs[] = $manufacturer;        
/* you can add other additional attributes here like $manufacturer */

// finally we link the additional attributes to the $catalogProductCreateEntity object
$catalogProductCreateEntity->additional_attributes = $additionalAttrs;

// send the request
$client->catalogProductUpdate($session, "your product id", $catalogProductCreateEntity, NULL, "id");

// end session and enjoy your updated products :)
$client->endSession($session);

I hope, this article can help anyone out there, who faces the same problems. Feel free to comment :-)

Kommentare

Hi guys,

I read your article and code where you're using the function "catalogProductUpdate" to add product to Magento
// send the request
$client->catalogProductUpdate($session, "your product id", $catalogProductCreateEntity, NULL, "id");

 

But I can't find anywhere the APIs with these functions. I'm using API I found at this page http://www.magentocommerce.com/support/magento_core_api

When I add product to Magento using APIs, they go into TheFind Feed extension and not in the Magento catalogue.

Could you help me?

Hi,

first you have to know that there 3 different ways how you can consume Magento Webservices:

  • XML-RPC
  • SOAP
  • and a different SOAP API, called "SOAP v2"

Here is an overview and some small examples how you can call the first 2 methods: http://www.magentocommerce.com/wiki/doc/webservices-api/introduction

As I have written in the article, the older version is better documented than SOAP v2, which isn't really documented at all. What I did, is to look at the API documentation of v1 (the link that you have provided in your comment) and then search for the equivalent method in the WSDL of the SOAP v2 API. You can find the WSDL of your Magento installation at YOURMAGENTOURL/api/v2_soap?wsdl=1.

In case of the product update, the v1 API call can be found here: http://www.magentocommerce.com/wiki/doc/webservices-api/api/catalog_product#catalog_product.update. So, for v1 I would have to call "catalog_product.update". When you look at the v2 WSDL, then you'll find this:

<operation name="catalogProductUpdate">

That's why I have called in the example: $client->catalogProductUpdate($session, "your product id", $catalogProductCreateEntity, NULL, "id");

The v2 equivalents are always (or at least almost always!?) written in the same pattern: remove the dots and underscores from the v1 method name and write it in camelcase: catalog_product.update -> catalogProductUpdate

But I personally think that you're problem is a different one. I don't understand what you mean with "When I add product to Magento using APIs, they go into TheFind Feed extension and not in the Magento catalogue." Maybe it would be better, if you provide a code example what you did. I will try my best to support you :-)

 

Thank you for this post. I found it helpful.

\r\n

I have a question.

\r\n

Whenever I update a product with either v1 or v2 API, the "Use Default Values", always get unchecked after. Is there anyway to avaoid this from happening? If they are checked, I want to leave them checked. If I'm updating an attribute value, then uncheck it.

Hi,

\r\n

first of all, sorry for my late answer. The blog suffers currently from too much work that's currently do do ;) So I'm not looking as regularly at the comments as I normally always did.

\r\n

And now to your question: honestly I never cared about whether this is checked or not. But maybe I can have a look at this in the next days. One of our current projects is a Magento project with product import, and it's possible that I have to do some changes / additions to the import functionality. If I have to dig a bit into the Magento code, I will also search for your default value issue and post the answer here

When I try to create anonymous class I can't pass it in catalogProductUpdate anymore since it requires catalogProductCreateEntity() as parameter. I am using C#. Can you help?

Hi,

\r\n

I only used Magento Webservices with PHP so far. And I haven't ever used Webservices with C#. As C# has a few things in common with Java, I only can tell how I would try to do it in Java. I would use Apache Axis (or Axis2) to generate stub classes out from the WSDL and then work with these classes. I'm not sure, if it would work with anonymous classes also...

check this for api v2 if having any problems:

\r\n

http://www.magentocommerce.com/boards/viewthread/267840/

Looking at app/code/core/Mage/Catalog/Model/Product/Api/V2.php #256 I think we need to use

\r\n

        $manufacturer = new stdClass();
\r\n         $manufacturer->key = "manufacturer";
\r\n         $manufacturer->value = "20";
\r\n         $additionalAttrs['single_data'][] = $manufacturer;

\r\n

or

\r\n

        $manufacturer = new stdClass();
\r\n         $manufacturer->key = "manufacturer";
\r\n         $manufacturer->value = "20";
\r\n         $additionalAttrs['multi_data'][] = $manufacturer;

I have a question for updating product sku,what should i do?

\r\n

Thanks.

Hi,

\r\n

Is it possble to make the same example to work, but with the "WSI-COMPLIANCE" mode on?

\r\n

I try several combinatinos of how the array/object should be for the additional_attributes, but it never works.

\r\n

 

\r\n

Thanks!

Thank you for sharing your experiences.  It is nice to know I wasn't the only one who thought the Magento API was very poorly documented.

This was the only place I could find an example of how to update the Manufacturer.  Once I read in your example it was a number not a string everything fell into place and made what I was trying to do so much easier.

Thanks again

I haven't tracked progress since 1.4 CE, but the main problem of the Magento Webservice API is/was still, that it is only usable for a small amount of products. Doing bulk update/creation is not possible. It's neither possible to send a createOrUpdate Request. You have to query first, if the product exists.

But that's just one of many weaknesses of Magento. That's why I can recommend to everyone to rather go for Drupal Commerce than for Magento!

magento version 1.9 you have to add : $additionalAttrs['single_data'] = $manufacturer;

I have created a product successfully in Magento, but when I try to update it I lose the category assignments. The attributes however are not affected. My apologies in advance for the amount of data posted. But can you please advise if you can see anything odd about this update ******PRODUCT CREATE fa329a38dfe0085a9797fed1bf0b0711 simple 4 15450130 EURON FLEX MAXI PLUS 27 - 60 Kg 3300ml (PKT x 28) 4 1 4 28 38 127 35 58 209 1 2 0 30.9 0 size 27 - 60 Kg measurement gender capacity 3300ml type composition length colour profile (PKT x 28) pdf_download usage_chart unit_type PKT unit_count 28 unit_per_carton 4 manufacturer 274 manufacturer_sku ONT11509280 0 11 1 1 1 1 1 0 1 1 4 AVAILABLE 1 1 1 1 1 1 0 1 1 1 AVAILABLE 0 1 1 1 1 1 0 1 1 5 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 6 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 7 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 8 OUT OF STOCK MORE ON ORDER ******PRODUCT UPDATE 3126187c9ffda8ce6d72b680ec3843b8 simple 4 1200045031 ASTRATECH WOUND DRAIN BAG one Provided with non-return valve, air filter, sealing cap, tear slit and volume scale. ASTRATECH WOUND DRAIN BAG one #3 700ml STERILE C175 0 1 4 31 55 32 75 293 306 1 2 0 11.3 0 Provided with non-return valve, air filter, sealing cap, tear slit and volume scale. size #3 measurement gender capacity 700ml type STERILE composition length colour profile C175 pdf_download usage_chart unit_type EA unit_count 1 unit_per_carton 50 manufacturer 479 manufacturer_sku 0 0 1 1 1 1 1 0 1 1 4 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 1 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 5 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 6 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 7 OUT OF STOCK MORE ON ORDER 0 1 1 1 1 1 0 1 1 8 OUT OF STOCK MORE ON ORDER

Hi Kerrie,

I'm sorry, I can't give you any advice on this. My blog post is a couple of years old, based on Magento 1.4.x I think... I haven't used the SOAP webservice any more since then, since it isn't suitable for production use at all. The lack of batch update functionality was slowing the data import processes down in an inacceptable manner

Neuen Kommentar schreiben