I've been doing some work with the open source Magento web store software, mostly to do with their XMLRPC API for our ReadyToShip software. Inevitably, the requirement arised that couldn't be solved by the existing Magento API calls, so it was time to take a look at writing some code and building my own custom api call (as suggested by the Magento documentation).
Unfortunately, the documentation on doing so is kind of sketchy, so it was a slightly frustrating experience, not least because of the complexity of Magento's data structures. I've got a 'Hello World' API call working now though, and this post will have a dual purpose: to help others who have been wrestling with the same problem, and to actually get it straight what I did to get it working!
Before I start, can I just say that Mage::Log is your friend! I figured out a lot of what is going on here by throwing Mage::Log("Bla bla") throughout the existing Magento code.
OK, first steps, here is the directory structure of my custom files:
magento/
|-- app/
|-- code/
| |-- local/
| |-- CompanyName/
| |-- ModuleName/
| |-- etc/
| | |-- api.xml
| | |-- config.xml
| |-- Model/
| |-- ObjectModel/
| |-- Api.php
|-- modules/
|-- etc/
|-- CompanyName_ModuleName.xml
A lot of directories for only 4 new files! But the directory structure is important, as Magento makes use of strict naming conventions to find where it's class and config files are.
OK, let's tell Magento that we're writing a new module, and in doing so, where to find it. Here are the contents of magento/app/etc/modules/CompanyName_ModuleName.xml :
<?xml version="1.0"?>
<config>
<modules>
<CompanyName_ModuleName>
<active>true</active>
<codePool>local</codePool>
</CompanyName_ModuleName>
</modules>
</config>
Pretty simple, huh? From this piece of XML, Magento knows to look in the magento/app/code/local/CompanyName/ModuleName directory for your module.
Next, let's take a look at the config file for our module. Here are the contents of magento/app/code/local/CompanyName/ModuleName/etc/config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<CompanyName_ModuleName>
<version>0.1.0</version>
</CompanyName_ModuleName>
</modules>
<global>
<models>
<test>
<class>CompanyName_ModuleName_Model</class>
</test>
</models>
</global>
</config>
In this file we're defining an extra variable for our module (I suspect the <modules> section could go in the CompanyName_ModuleName.xml file, actually, as all the XML config is merged together), as well as telling Magento the base class name for the module (this is important to those constantly having Magento look under the Mage/ directory for your class files instead of your company name, a problem I struggled with for quite a while).
Next is the definition of our API call, here are the contents of magento/app/code/local/CompanyName/ModuleName/etc/api.xml:
<?xml version="1.0"?>
<config>
<api>
<resources>
<resource_name translate="title" module="companyname_modulename">
<title>Title Of My Resource</title>
<model>modulename/objectmodel_api</model>
<methods>
<methodName translate="title" module="companyname_modulename">
<title>Title Of My Method</title>
</methodName>
</methods>
</resource_name>
</resources>
</api>
</config>
I'm still a bit fuzzy on exactly how this section works, but what I do know is that the <model> variable is what tells Magento where your class is. I think the resource name can be whatever you like, and I'm not sure where the module attribute is used as I seem to be able to set it to anything I like and it still works.
Finally, let's look at the API method itself, here are the contents of magento/app/code/local/CompanyName/ModuleName/Model/ObjectModel/Api.php:
<?php
class CompanyName_ModuleName_Model_ObjectModel_Api extends Mage_Api_Model_Resource_Abstract
{
public function methodName($arg)
{
return "Hello World, here is my argument: " . $arg;
}
}
?>
And there you have it! You can call your method using resource_name.methodName like you would any other API call.
Note: if you have Magento caching turned on, you will need to refresh the cache under System -> Cache Management before the changes to your model will take effect. This caught me out a few times as changes I'd made were not being reflected in the debug output, very frustrating! For debugging purposes I would suggest turning the caching off temporarily.