Run methods that are specified in module’s xml file using XPath and Varien_Object

screen

For one client last year we needed to develop some custom ERP. In documentation client specified that they want to run some methods that are “defined” in config.xml using method’s code as a id in time of exporting their orders. Shortly, depending on payment code from order they wanted to add some custom calculation when they are exporting orders. If you are interested how we implemented this keep reading!

By example everything will be much easier to explain. Let’s start with creating config.xml and then at the end we’ll echo to Magento about our module.

Step 2: Creating module’s configuration

Here is the sample code that’s simplified a bit. You can create file app/code/local/Inchoo/XmlMethodCall/etc/config.xml with following content:

< ?xml version="1.0"?>
 
<config>
 
    <modules>
       <inchoo_xmlmethodcall>
           <version>1.0.0.0</version>
       </inchoo_xmlmethodcall>
    </modules>
 
    <global>
       <xml_method_call>
           <payment_checkmo>
               <name>Check / Money order </name>
               <code>checkmo</code>
               <add>
                   <model>inchoo_xmlmethodcall/checkmoAddVal</model>
                   <method>addValue</method>
               </add>
               <return>
                   <model>inchoo_xmlmethodcall/checkmoGetCod</model>
                   <method>getCode</method>
               </return>
           </payment_checkmo>
       </xml_method_call>
       <xml_method_call>
           <payment_ccsave>
               <name>Credit Card (saved) </name>
               <code>ccsave</code>
               <add>
                   <model>inchoo_xmlmethodcall/ccsaveAddVal</model>
                   <method>addValue</method>
               </add>
               <return>
                   <model>inchoo_xmlmethodcall/ccsaveGetCod</model>
                   <method>getCode</method>
               </return>
           </payment_ccsave>
       </xml_method_call>
       <models>
           <inchoo_xmlmethodcall>
               <class>Inchoo_XmlMethodCall_Model</class>
               <resourcemodel>inchoo_xmlmethodcall_mysql4</resourcemodel>
           </inchoo_xmlmethodcall>
       </models>
    </global>
 
    <frontend>
       <routers>
           <inchoo_xmlmethodcall>
               <use>standard</use>
               <args>
                   <module>Inchoo_XmlMethodCall</module>
                   <frontname>xmlmethodcall</frontname>
               </args>
           </inchoo_xmlmethodcall>
       </routers>
    </frontend>
 
</config>

Take a deeper look into xml_method_call node, first child of global node. There you can see two siblings with the same name, xml_method_call node.

Let’s describe little bit next xml block:

           <payment_checkmo>
               <name>Check / Money order </name>
               <code>checkmo</code>
               <add>
                   <model>inchoo_xmlmethodcall/checkmoAddVal</model>
                   <method>addValue</method>
               </add>
               <return>
                   <model>inchoo_xmlmethodcall/checkmoGetCod</model>
                   <method>getCode</method>
               </return>
           </payment_checkmo>
  • payment_checkmo – unique node that contains some data
    • name – name of what this unique name represent, not relevant for code
    • code – code (id) of a payment method
    • add, return – two methods definition about what they should call (with module/model and method call definition)

I hope that now you have much more clear picture about what we needed to implement. In order export time we needed to call some methods that will perform some calculations and return result. Requirement was that methods needed to be configurable so non developers could change them from configuration file.
Sure that you’ll do it in system.xml file.

Step 3: Creating module’s models, described in config.xml

Create files:

app/code/local/Inchoo/XmlMethodCall/Model/CheckmoAddVal.php
app/code/local/Inchoo/XmlMethodCall/Model/CheckmoGetCod.php
app/code/local/Inchoo/XmlMethodCall/Model/CcsaveAddVal.php
app/code/local/Inchoo/XmlMethodCall/Model/CcsaveGetCod.php

with following content in it (source code will be added respectively):

< ?php
 
/**
*
* @author      Inchoo <ivan .galambos@inchoo.net>
*/
class Inchoo_XmlMethodCall_Model_CheckmoAddVal extends Mage_Core_Model_Abstract
{
 
   public function addValue()
   {
       return 1;
   }
}
< ?php
 
/**
*
* @author      Inchoo <ivan .galambos@inchoo.net>
*/
class Inchoo_XmlMethodCall_Model_CheckmoGetCod extends Mage_Core_Model_Abstract
{
 
   public function getCode()
   {
       return 'a';
   }
}
< ?php
 
/**
*
* @author      Inchoo <ivan .galambos@inchoo.net>
*/
class Inchoo_XmlMethodCall_Model_CcsaveAddVal extends Mage_Core_Model_Abstract
{
 
   public function addValue()
   {
       return 2;
   }
}
< ?php
 
/**
*
* @author      Inchoo <ivan .galambos@inchoo.net>
*/
class Inchoo_XmlMethodCall_Model_CcsaveGetCod extends Mage_Core_Model_Abstract
{
 
   public function getCode()
   {
       return 'b';
   }
}

Because of simplicity and educational example we’ll just create controller and inside of controller some orders in array with a few columns in it.

This is the most significant and interesting part…

Step 4: Creating controller

< ?php
 
/**
*
* @author      Inchoo <ivan .galambos@inchoo.net>
*/
class Inchoo_XmlMethodCall_IndexController extends Mage_Core_Controller_Front_Action
{
 
   public function indexAction()
   {
       $orders = array();
       $orders[] = array (
           'order_no'    => 101,
           'method'    => 'checkmo',
           'amount'    => 99
       );
       $orders[] = array (
               'order_no'    => 102,
               'method'    => 'ccsave',
               'amount'    => 100
       );
       $orders[] = array (
               'order_no'    => 101,
               'method'    => 'checkmo',
               'amount'    => 199
       );
 
       foreach ($orders as $row) {
           $orderNo = $row['order_no'];
           $conf = Mage::getSingleton('core/config')->init()
               ->getXpath('global/xml_method_call//code[.="' . $row['method'] . '"]/..');
 
           if ($conf) {
               $conf = new Varien_Object(current($conf)->asArray());
               $add = new Varien_Object($conf->getAdd());
               $code = new Varien_Object($conf->getReturn());
 
               if (method_exists(Mage::getModel($add->getModel()), $add->getMethod())) {
                   $amountValue = $row['amount'] + Mage::getModel($add->getModel())->{$add->getMethod()}();
               }
               if (method_exists(Mage::getModel($code->getModel()), $code->getMethod())) {
                   $codeValue = Mage::getModel($code->getModel())->{$code->getMethod()}();
               }
           } else {
               //throw an error
               die("or exit... or something goes wrong...");
           }
           echo "<pre>"                     . PHP_EOL;
           echo 'ID: '     . $orderNo         . PHP_EOL;
           echo 'VALUE: '     . $amountValue     . PHP_EOL;
           echo 'CODE: '     . $codeValue     . PHP_EOL;
       }
       print_r($orders);
   }
}

Firstly we created “collection of orders” with some data in it – $orders. This could be real collection but because of simplicity now, I decided to put it in more simplified and cleaner way – simple 2-dimensional array.

Next significant part is:

$conf = Mage::getSingleton('core/config')->init()
               ->getXpath('global/xml_method_call//code[.="' . $row['method'] . '"]/..');

As you can see we are calling XPath on code/config model. If you don’t know what this ->getXpath(‘global/xml_method_call//code[.=”‘ . $row[‘method’] . ‘”]/..’) mean then it’s a really great time to visit at least w3schools/XPath and learn something new!

We want to instantiate object $conf that will have node xml_method_call as root node. We don’t know which one is it but we know method’s code and in config.xml we can search for it.
Two // means go one lvl. deeper from temp. node and check for all “code” nodes that you can find there and pick up those that has desired value in it. Because it’s only one (code-id) we’ll get one object as result.
“/..” means go one node back from where are you now – grab our desired xml_method_call node. From there everything is much easier.

Next interesting part:

               $conf = new Varien_Object(current($conf)->asArray());
               $add = new Varien_Object($conf->getAdd());
               $code = new Varien_Object($conf->getReturn());

is to set our $conf, $add and $code as Varien_Object instances (we’ll have our getter and setter prepared and objects that we know how they look like, right?)

Last thing is to check if our methods exists or not. If so, print our result on screen.

So let’s echo to Magento config object about our module and then we’ll visit our controller.

Step 1: Magento, we are here!

Create file app/etc/modules/Inchoo_Xmlmethodcall.xml with a following content

<?xml version="1.0"?>
 
<config>
	<modules>
		<Inchoo_XmlMethodCall>
			<active>true</active>
			<codePool>local</codePool>
		</Inchoo_XmlMethodCall>
	</modules>
</config>

Step 5: Visit something like: http://magento1510.loc/index.php/xmlmethodcall

You shall see something like:

ID: 101
VALUE: 100
CODE: a
 
ID: 102
VALUE: 102
CODE: b
 
ID: 101
VALUE: 200
CODE: a
Array
(
    [0] => Array
        (
            [order_no] => 101
            [method] => checkmo
            [amount] => 99
        )
 
    [1] => Array
        (
            [order_no] => 102
            [method] => ccsave
            [amount] => 100
        )
 
    [2] => Array
        (
            [order_no] => 101
            [method] => checkmo
            [amount] => 199
        )
 
)

I hope that some of you now see how you could use Varien_Object and especially how things can be interesting with XPath expressions.

In one of my next articles I’ll show you how can you export and iterate through collections not only by $_exportPageSize property but with additional _exportCollectionLimit (we’ll set: export 1000 records total by one cycle of cron, but take 100 by 100 of them – keeping our memory safe).

So stay tuned!


Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.