📜 ⬆️ ⬇️

Magento step by step: REST API

In the previous article, we looked at creating a “skeleton” for exporting orders. In the same look at the creation of the same skeleton, but import through the REST API.

From the wiki: REST (abbr. From English. Representational State Transfer - “representative state transfer”) - in a more commonly used narrow sense, REST refers to the method of interaction of components of a distributed application on the Internet, in which a remote procedure call is a regular HTTP request ( usually GET or POST; such a request is called a REST request), and the necessary data is transmitted as request parameters. This method is an alternative to more complex methods such as SOAP, CORBA and RPC.


According to standard htaccess Magento, all requests submitted for / api / should be sent to api.php:
RewriteRule ^api/([az][0-9a-z_]+)/?$ api.php?type=$1 [QSA,L] 

')
Why should you use the standard API? In addition to the fact that everything is ready for it, API requests are always launched in administrator mode (Mage :: app () -> getStore () -> isAdmin () === true), which implies language independence for EAV attributes. , and the absence of any events from the frontend area.
The configuration file api2.xml in the etc directory of the module, api2 node, is responsible for the REST configuration.

So, we will try to expand the previous functionality also by import.
Suppose the format of input data from an external system is as follows:
 <orders> <order> <id>145000003</id> <shipment> <tracking>uiwq12889124</tracking> <items> <item> <sku>21</sku> <qty>1</qty> </item> </items> </shipment> </order> <order> <id>145000003ZZZ</id> <shipment> <tracking>uiwq128zzz89124</tracking> <items> <item> <sku>21</sku> <qty>1</qty> </item> </items> </shipment> </order> </orders> 


In the specified request, one order will be real, the second - no.

app / code / local / Easy / Interfacing / etc / api2.xml
 <?xml version="1.0"?> <config> <api2> <resource_groups> <easy_interfacing translate="title" module="api2"> <title>Easy Interfacing REST</title> <sort_order>30</sort_order> <children> <easy_interfacing_orders translate="title" module="api2"> <title>Orders</title> <sort_order>50</sort_order> </easy_interfacing_orders> </children> </easy_interfacing> </resource_groups> <resources> <easy_interfacing_orders translate="title" module="api2"> <group>easy_interfacing</group> <model>easy_interfacing/api2_order</model> <filter>easy_interfacing/api2_order_filter</filter> <title>Orders</title> <sort_order>10</sort_order> <versions>1</versions> <routes> <route_collection> <route>/easy_interfacing/order</route> <action_type>collection</action_type> </route_collection> </routes> <privileges> <guest> <update>1</update> </guest> </privileges> <attributes translate="id shipment" module="easy_interfacing"> <id>Order ID</id> <shipment>Shipment data</shipment> </attributes> </easy_interfacing_orders> </resources> </api2> </config> 


The resource_groups node is responsible for the ACLs in System> Web Services> REST Roles . In the very same resources of the REST API, we specify the easy_interfacing membership group and the api2_order model, which will be responsible for the functionality.
The path to the interface is specified in route_collection, we also indicate that the processing will be carried out on a plurality of elements (action_type = route_collection). In our REST API, we will do everything on guest access, so as not to suffer with passwords during the learning phase: in the code, the difference will be only in the class name.
The attributes node is responsible for the imported data ACL filter (Mage_Api2_Model_Acl_Filter). You can also force attributes to the array of imported data by adding a list to the node forced_attributes. All possible options can be found in api2.xml of any module that allows you to import / export data, for example, Mage_Sales or Mage_Catalog.

By default, all attributes should not be arrays, which is not suitable for our format (shipment - array), so we will create our own filter, where we fix this problem in the Mage_Api2_Model_Acl_Filter :: collectionIn method:

app / code / local / Easy / Interfacing / Model / Api2 / Order / Filter.php
 class Easy_Interfacing_Model_Api2_Order_Filter extends Mage_Api2_Model_Acl_Filter { public function collectionIn($items) { $nodeName = key($items); if (!is_numeric(key($items[$nodeName]))) { $items[$nodeName] = array($items[$nodeName]); } if (is_array($items[$nodeName])) { foreach ($items[$nodeName] as &$item) { $item = $this->in($item); } } return $items[$nodeName]; } } 

Do not forget to put the ACL access in System> Web services> REST - Roles :

and in System> Web services> REST - Attributes :


And finally, create the API class itself:
app / code / local / Easy / Interfacing / Model / Api2 / Order.php
 class Easy_Interfacing_Model_Api2_Order extends Mage_Api2_Model_Resource { const RESULT_ERROR_NOT_FOUND = 404; const RESULT_ERROR_IMPORT = 500; const RESULT_SUCCESS = 200; protected $_responseItems = array(); protected function _addResult(array $item, $errorCode, $errorMessage) { $result = array('result' => $errorCode, 'id' => $item['id']); if ($errorMessage) { $result['error'] = $errorMessage; } $this->_responseItems[] = $result; } } 

In it, we especially do not need anything, only _addResult and a pair of error code constants. Also create a REST class inherited from this:
app / code / local / Easy / Interfacing / Model / Api2 / Order / Rest.php
 class Easy_Interfacing_Model_Api2_Order_Rest extends Easy_Interfacing_Model_Api2_Order { public function dispatch() { $this->_filter = Mage::getModel('easy_interfacing/api2_order_filter', $this); parent::dispatch(); $this->_render($this->_responseItems); } protected function _multiUpdate(array $filteredData) { foreach ($filteredData as $item) { $order = Mage::getModel('sales/order')->loadByIncrementId($item['id']); /* @var $order Mage_Sales_Model_Order */ if (!$order->getId()) { $this->_addResult($item, self::RESULT_ERROR_NOT_FOUND); continue; } try { Mage::getSingleton('easy_interfacing/order')->import($order, $item); $this->_addResult($item, self::RESULT_SUCCESS); } catch (Exception $ex) { $order->addStatusHistoryComment('Failed importing order: ' . $ex->getMessage())->save(); $this->_addResult($item, self::RESULT_ERROR_IMPORT, $ex->getMessage()); } } } } 

In it, override the dispatch method to change the filter to ours and change the rendering a bit, since by default Magento will produce a slightly response curve based on the collection of messages from getResponse ().
Since we specified action_type = collection, we implement the _multiUpdate method. In $ filteredData there will always be an already filtered array (if the ID is removed from the ACL attributes, then Easy_Interfacing_Model_Order :: import will throw an exception, or even crash).

Add the import method to our Easy_Interfacing_Model_Order:
app / code / local / Easy / Interfacing / Model / Order.php
 class Easy_Interfacing_Model_Order { public function import(Mage_Sales_Model_Order $order, array $data) { Mage::throwException('Not implemented'); } public function export(Mage_Sales_Model_Order $order) { Mage::throwException('Not implemented'); } } 


Guest-class API also needs to be created, since It will be called when the guest access to REST.
app / code / local / Easy / Interfacing / Model / Api2 / Order / Rest / Guest / V1.php
 class Easy_Interfacing_Model_Api2_Order_Rest_Guest_V1 extends Easy_Interfacing_Model_Api2_Order_Rest { } 


Ultimately, if you did everything correctly, when you request a http: /// api / rest / easy_interfacing / order / method PUT via any REST client input data specified above, you will receive a response of the form:
 <?xml version="1.0" ?> <magento_api> <data_item> <result>500</result> <id>145000003</id> <error>Not implemented</error> </data_item> <data_item> <result>404</result> <id>14501100003</id> </data_item> </magento_api> 

Source: https://habr.com/ru/post/232603/


All Articles