С Вики: REST (сокр. от англ. Representational State Transfer — «передача репрезентативного состояния») — в более употребительном узком смысле под REST понимается метод взаимодействия компонентов распределённого приложения в сети Интернет, при котором вызов удаленной процедуры представляет собой обычный HTTP-запрос (обычно GET или POST; такой запрос называют REST-запрос), а необходимые данные передаются в качестве параметров запроса. Этот способ является альтернативой более сложным методам, таким как SOAP, CORBA и RPC.
По стандартному htaccess Magento, все запросы поданные на /api/ должны отправляться на api.php:
RewriteRule ^api/([a-z][0-9a-z_]+)/?$ api.php?type=$1 [QSA,L]
Почему нужно стоить использовать именно стандартный API? Помимо того, что для него уже все готово, API-запросы всегда запускаются в режиме администратора (Mage::app()->getStore()->isAdmin() === true), что подразумевает как языковую независимость для EAV-аттрибутов, так и отсутствие каких-либо событий из области frontend.
За конфигурацию REST отвечает конфигурационный файл api2.xml в директории etc модуля, нода api2.
Итак, попробуем расширить предыдущий функционал еще и импортом.
Допустим, формат вводных данных с внешней системы будет таков:
<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>
В указанном запросе один заказ будет реальным, второй — нет.
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>
Нода resource_groups отвечает за ACL в System > Web Services > REST Roles. В самих же ресурсах REST API мы указываем группу принадлежности easy_interfacing и модель api2_order, которая будет отвечать за функционал.
Путь к интерфейсу укаазн в route_collection, мы также указываем, что обработка будет вестись на множественном количестве элементов (action_type = route_collection). В нашем REST API мы все будем делать на гостевом доступе, чтоб не мучаться с паролями на этапе обучения: в коде разница будет только в названии класса.
Нода attributes отвечает за ACL фильтр импортируемых данных (Mage_Api2_Model_Acl_Filter). Так же можно форсировать аттрибуты в массив импортируемых данных добавлением списка в ноду forced_attributes. Все возможные варианты можно найти в api2.xml любого модуля, позволяющего делать импорт/экспорт данных, например, Mage_Sales или Mage_Catalog.
По умолчанию все аттрибуты не должны быть массивами, что не подходит под наш формат (shipment — массив), поэтому создадим собственный фильтр, где исправим эту проблему в методе Mage_Api2_Model_Acl_Filter::collectionIn:
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]; } }
Не забудьте проставить ACL доступы в System > Web services > REST — Roles:
и в System > Web services > REST — Attributes:
И, наконец создадим сам класс API:
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; } }
В нем нам особо ничего не нужно, только _addResult и пара констант кодов ошибок. Так же создадим REST-класс, унаследованный от этого:
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()); } } } }
В нем переопределим метод dispatch, чтобы подменить фильтр на наш и сменить немного рендеринг, так как по умолчанию Magento выдаст немного кривой ответ, основанный на коллекции сообщений из getResponse().
Так как мы указывали action_type=collection, мы реализуем метод _multiUpdate. В $filteredData всегда будет находиться уже отфильтрованный массив (если из ACL аттрибутов убрать ID, то Easy_Interfacing_Model_Order::import бросит исключение, или даже крэшнется).
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 { }
Guest-класс API так же нужно создать, т.к. именно он будет вызван при гостевом доступе к REST.
В конечном итоге, если Вы все сделали правильно, при запросе на http:///api/rest/easy_interfacing/order/ методом PUT через любой REST-клиент вводных данных указанных выше, придет ответ вида:
<?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>
ссылка на оригинал статьи http://habrahabr.ru/post/232603/
Добавить комментарий