Magento API and C# – Part I

I was recently tasked with integrating a Magento eCommerce website and a Windows based point of sales (POS) system. The job required granting sales staff access to Magento’s customer and address information at each POS terminal. I decided to make use of Magento’s SOAP API Version 2 which provides a lot of functionality but is not without its shortcomings. It doesn’t, for example, have native support for pagination. Fortunately Magento is highly extensible and this series of articles explains how to extend Magento’s API and then access it via a C# MVC website.

Before we begin

This article assumes that you already have an instance of Magento up and running. I am using a Community Edition installation, with sample data, running on WAMPServer.

Disable the Cache

Magento is a sensational ecommerce solution, packed with a lot of great features, but despite that it is notoriously slow. I wouldn’t usually recommend turning off its built-in caching but as developers we know how frustrating it can be when you find yourself yelling at your monitors “why aren’t I seeing my latest changes??” So in development I always disable caching and you can do the same like this:

Under ‘General Settings’ make sure that ‘WS-I Compliance’ is set to ‘Yes’.

While we are here we should also disable the WSDL cache by setting the ‘Enable WSDL Cache’ option to ‘No’. Again, I wouldn’t recommend this in a production environment because it will seriously impede performance. Your ‘Magento Core API’ settings should now look like this:

Magento Core API

Create a Magento API User

Magento resources are restricted by Access Control Lists (ACLs). In order to start developing against the Magento APIs we need to create a role and a user account that has permissions to access the particular API methods we will be using.

Create a new user

On the ‘User Info’ tab, complete the ‘Account Information’ form. Keep a note of your ‘User Name’ and ‘API Key’, you will need this later when configuring the service in the C# MVC website.

On the ‘User Role’ tab, make sure your user is assigned to the role you created in the previous step.

Now that we have finished configuring our environment we can start to lay out our module.

Creating a basic Magento module

Understanding the file structure

Magento modules are stored in the app/code directory. The app/code directory has three subdirectories: core, community, and local.

Core

The app/code/core directory contains the modules which provide Magento’s fundamental functionality for customers, categories, products, and so on. The files in the core directory should not be modified because any changes could potentially be overridden in future Magento updates. This is not an issue however because Magento has been designed to allow changes to the core functionality without modifying these files directly.

Community

Modules provided by third parties are stored in the app/code/community directory. If you have installed a module through Magento Connect this is where it ends up.

Local

The remaining directory, app/code/local, is where we store our own modules. We will begin by creating a module that extends the existing Magento API here.

Create your module structure

Modules, regardless of whether they are core, community, or local, are created with the following naming convention: Namespace/ModuleName where Namespace is usually the author or company name and ModuleName is the descriptive name of your module. In this example the namespace is MezaIT and the module name is CustomerAddress. Be aware that the namespace and module name are case sensitive. Our basic module structure should now look like this:

Module Structure

Configure the module

Magento looks for configuration files inside of the etc directory. Create that directory and a new configuration xml file: app/code/local/MezaIT/CustomerAdress/etc/config.xml. The most basic version of this file notifies Magento of the location of the files in our module and the version number. Paste the following xml inside of the config.xml file:

Configure the module

XHTML

1

2

3

4

5

6

7

8

<?xml version="1.0"?>

<config>

<modules>

<MezaIT_CustomerAddress>

<version>0.0.1</version>

</MezaIT_CustomerAddress>

</modules>

</config>

Register the module

The next step is to tell Magento that our module exists. In order to do this we create another xml file: /app/etc/modules/MezaIT_CustomerAddress.xml. The name of the xml file is not critical, Magento scans every file in this directory regardless. However, in order to keep our installation nice and neat and conform to our previous naming conventions the xml file and the module should use the same name. Paste the following xml inside of the MezaIT_CustomerAddress.xml file:

Register the module

XHTML

1

2

3

4

5

6

7

8

9

<?xml version="1.0"?>

<config>

<modules>

<MezaIT_CustomerAddress>

<active>true</active>

<codePool>local</codePool>

</MezaIT_CustomerAddress>

</modules>

</config>

Confirm that the module exists

At this stage the module adds no new functionality to Magento, it should however be registered in the system. We can double check this by:

Under ‘Disable Modules Output’ look for our ‘MezaIT_CustomerAddress’ module to see that it is in fact registered and set to ‘Enable’.

Confirm that the module exists

Now that we have configured our basic module and confirmed that it is running we can expand upon it to add our API enhancements.

Extend the API

We already mentioned that the SOAP API doesn’t natively support pagination, so that is one thing that we will implement ourselves. Another issue I identified was that if I wanted to access to a customer and their default address I had to first query Magento for the customer and then make a second call to retrieve their address information. Imagine the number of calls required if you wanted to retrieve a large list of customers and then their addresses. In order to reduce round trips to Magento our new module will retrieve a customer and their default address with a single query.

Notice that the class names match the file location (with the ‘/’s replaced with ‘_’s). Also notice that our version 1 class extends Mage_Core_Model_Abstract and the version 2 class simply extends version 1. Now we need to notify the module that these classes exist by modifying the config.xml to include:

config.xml

XHTML

1

2

3

4

5

6

7

<global>

<models>

<mezait_customeraddress>

<class>MezaIT_CustomerAddress_Model</class>

</mezait_customeraddress>

</models>

</global>

Define methods

Finally, we get to write some code! Add the following methods to the version 1 class.

Define methods

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

/**

* Number of customers

*

* @param null|object|array $filters Filters that match the customers

*

* @return int The number of customers

*/

publicfunctioncount($filters=null){

...

}

/**

* Retrieve a list of customers and addresses

*

* @param null|object|array $filters Filters that match the customers

* @param int $pageNum Page number

* @param int $pageSize Page size

*

* @return array List of customers

*/

publicfunctionpagedList($filters=null,$pageNum=1,$pageSize=10){

...

}

Refer to the code on Github for the full implementation. Once our code has been written we complete the necessary sections in the api.xml file to define our methods, their faults, and their related ACLs.

Define methods XML

XHTML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<methods>

<count translate="title"module="mezait_customeraddress">

<title>Count the number of customers</title>

<acl>customer_address/count</acl>

</count>

<list translate="title"module="mezait_customeraddress">

<title>Get list of customers and their default billing address</title>

We will also include a v2 node in our xml file that states what prefix will be used when making calls to the version 2 API.

V2 resource prefix

XHTML

1

2

3

4

5

<v2>

<resources_function_prefix>

<customer_address>mezaitCustomerAddress</customer_address>

</resources_function_prefix>

</v2>

Update the WSDL

The methods and objects that are available via a web service are defined by a WSDL (Web Service Description Language). Browse to http://<site_url>/api/v2_soap/?wsdl where <site_url> is your domain in order to see the current Magento WSDL. In a previous step we set the WSDL to be WSI compliant so we need to create a etcwsi.xml file. Whatever we add to this file is merged with the rest of the API.

Basic structure

Creating the wsi.xml file can be quite complicated. It will be easier to learn the structure by taking a look at the finished example or by copying and modifying an existing wsi.xml from another module. A basic implementation will include the following:

Types

Describe the data.

Messages

Typically, a message corresponds to an operation and contains the information needed to perform the operation.

PortType

Defines a Web service, the operations that can be performed, and the messages that are used to perform the operations.

Binding

Specifies the interface and defines the SOAP binding style (RPC/Document) and transport (SOAP Protocol). The binding section also defines the operations.

Wrap Up

Finally, refresh the WSDL (http://<site_url>/api/v2_soap/?wsdl) and confirm that our additions exist and are ready to use. You can test by searching the WSDL for ‘mezaitCustomerAddress’ which is the V2 prefix we defined. Assuming that you can see the changes we are ready to interact with the API from another application. In the next post we discuss how to call our new SOAP methods from a C# MVC website.