Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Control the number of results from a Magento API call

Tags:

php

api

magento

I have a program that we use to connect our Magento store to our back end Inventory Control system via the API. Currently what it does is query the Magento API for all orders in Pending status, inserts them into the back end system, then sets their status to Processing in Magento. Due to limitations on our Inventory system, we can only insert a limited number of orders at a time. We control this by running the whole process through an if loop as shown below (FYI, code has been edited down to show only the key parts of this problem):

//The maximum number of orders to download
$iMaxCount = 10 ;

try {
  $proxy = new SoapClient($wsdl_url);
} catch (Exception $e) {
  echo 'Caught exception: ',  $e->getMessage(), "\n";
}

$sessionId = $proxy->login($login, $password);

//fetch the pending orders from the site
$field = array(array('status'=>array( 'pending') ));
$arr = $proxy->call($sessionId, 'sales_order.list', $field);
$iCnt = 0;

foreach($arr as $key => $value){
//only down up to the max count
if ($iCnt < $iMaxCount) {

[... Do the updating and insertion part of the program ...]

   $iCnt++;
} //End of looping through orders

The obvious downfall of doing it this way is I still have to pull all Pending orders even though I am going to be working with only 10 of them. i.e. If I have 200 pending orders, the API returns all 200 of them, processes 10, then skips the rest. What I want to do is modify the filter of the API call to pull only 10 orders at a time of status Processing. This would allow me to remove the if loop overhead and make the program run more efficiently because it only gets the data it needs.

Does anyone know how to apply that type of filter? Everything I have seen suggest you can only do this if you know the order numbers and set limits based on that. Thanks for your help!!!

like image 602
Greg Demetrick Avatar asked Oct 17 '11 16:10

Greg Demetrick


2 Answers

All API calls are, eventually, just executed PHP code. There will be a single PHP method somewhere that accepts the arguments passed in via the API call, so your best bet is to track down where that PHP code is executed.

Step 1 is to find the API call's configuration. In modern versions of Magento, API configurations are kept in files named api.xml

$ find app/code/core/Mage/ -name 'api.xml'
app/code/core/Mage/Api/etc/api.xml
app/code/core/Mage/Catalog/etc/api.xml
app/code/core/Mage/CatalogInventory/etc/api.xml
app/code/core/Mage/Checkout/etc/api.xml
app/code/core/Mage/Customer/etc/api.xml
app/code/core/Mage/Directory/etc/api.xml
app/code/core/Mage/GiftMessage/etc/api.xml
app/code/core/Mage/Sales/etc/api.xml

Once you've found all the api.xml files, search through them to fine which one configures your "top level api namespace" (unsure what this is really called by the internal developers)

$ find app/code/core/Mage/ -name 'api.xml' | xargs grep sales_order
app/code/core/Mage/Sales/etc/api.xml:            <sales_order translate="title" module="sales">
app/code/core/Mage/Sales/etc/api.xml:            </sales_order>
app/code/core/Mage/Sales/etc/api.xml:            <sales_order_shipment>
app/code/core/Mage/Sales/etc/api.xml:            </sales_order_shipment>
app/code/core/Mage/Sales/etc/api.xml:            <sales_order_invoice>
app/code/core/Mage/Sales/etc/api.xml:            </sales_order_invoice>
app/code/core/Mage/Sales/etc/api.xml:            <order>sales_order</order>
app/code/core/Mage/Sales/etc/api.xml:            <order_shipment>sales_order_shipment</order_shipment>
app/code/core/Mage/Sales/etc/api.xml:            <order_invoice>sales_order_invoice</order_invoice>

It looks like app/code/core/Mage/Sales/etc/api.xml is the file we want, since it has a <sales_order /> tag. Next, open the file and look at the <sales_order /> node.

<sales_order translate="title" module="sales">
    <model>sales/order_api</model>
    <title>Order API</title>
    <acl>sales/order</acl>
    <methods>
        <list translate="title" module="sales">
            <title>Retrieve list of orders by filters</title>
            <method>items</method>
            <acl>sales/order/info</acl>
        </list>
        <info translate="title" module="sales">
            <title>Retrieve order information</title>
            <acl>sales/order/info</acl>
        </info>

The first node we're interested in is <model>sales/order_api</model>. This specifies the object that will be instantiated to handle any API call in the sales_order namespace.

Next, we're going to look for the method list in the <methods/> node.

<list translate="title" module="sales">
    <title>Retrieve list of orders by filters</title>
    <method>items</method>
    <acl>sales/order/info</acl>
</list>

This node tells us that a call to sales_order.list corresponds to the method items. Combining that with the information found above, we now know the API call sales_order.list will run PHP code equivalent to the following

$m = Mage::getModel('sales/order_api');
$results = $m->items($args);

Next, open your model file and look at the items method

#File: app/code/core/Mage/Sales/Model/Order/Api.php
public function items($filters = null)
{
    //..a bunch of code to instantiate a collection object..
    if (is_array($filters)) {
        try {
            foreach ($filters as $field => $value) {
                if (isset($this->_attributesMap['order'][$field])) {
                    $field = $this->_attributesMap['order'][$field];
                }

                $collection->addFieldToFilter($field, $value);
            }
        } catch (Mage_Core_Exception $e) {
            $this->_fault('filters_invalid', $e->getMessage());
        }
    }        
}

At the end of this method, you can see that the method will run through each argument and attempt to use it as a filter on the collection. The key is the field, the value is the value search for. If you inspect the rest of the method you'll see there's no other way the paramaters interact with the collection to add any sort of paging or limits.

So, this leaves you with three options. The first is to find a set of values to pass into

$collection->addFieldToFilter($field, $value);

that will limit your collection. My suggestion would be some sort of date filter using the array('from'=>'10','to'=>'20') syntax.

Your second option would be to create a class rewrite for Mage_Sales_Model_Order_Api::items that does some extra filtering.

Your third option would be to investigate creating a module that adds a custom API method for you to call.

like image 111
Alan Storm Avatar answered Oct 22 '22 17:10

Alan Storm


A quick solution to set a limit is to locate app/code/core/Mage/Sales/Model/Order/Api.php (and do an overwrite of the class) then:

Change the method signature to accept another parameter $limit so the method signature looks like:

public function items($filters = null, $limit = null)

Then, add the following line before "foreach ($orderCollection as $order) {":

if( $limit ) $orderCollection->setPageSize( $limit );

Then simply pass the limit as an extra argument to the sales_order.list api call.

Booyah!

like image 1
MikeNatty Avatar answered Oct 22 '22 18:10

MikeNatty