API SOAP e Cron: integrare Magento con servizi esterni

Post on 11-May-2015

942 views 0 download

description

Magento offre due funzionalità normalmente trascurate da sviluppatori e commercianti nonostante le loro potenzialità: le API SOAP e il sistema Cron. Le API SOAP di Magento permettono di gestire i negozi eCommerce e integrarli con servizi esterni, fornendo accesso a risorse quali clienti, categorie, prodotti e ordini. Consentono inoltre la gestione dei carrelli e dell’inventario. Molto spesso, però, le API offerte non bastano a soddisfare le esigenze di un commerciante. Grazie alla sua estensibilità, Magento permette di ampliare le funzionalità native delle API, ad esempio per restituire più informazioni riguardanti un prodotto, per aggiungere la possibilità di importare un ordine, o addirittura creare nuove operazioni per manipolare le RMA. Magento, inoltre, offre alcuni task, come l’aggiornamento automatico delle valute e la generazione di report, e soprattutto consente di programmarne a piacere con una configurazione XML, in modo simile al crontab di UNIX. L’intervento di Andrea De Pirro è volto a spiegare come creare un semplice modulo per estendere le API Magento esistenti e aggiungerne di nuove, sfruttando al tempo stesso il sistema di Cron. Userà esempi reali corredati da consigli utili per sviluppare estensioni per esportare e importare prodotti, ordini e RMA.

Transcript of API SOAP e Cron: integrare Magento con servizi esterni

@akira28

Chi Sono• Co-fondatore di Yameveo

• 9 anni sviluppando in PHP

• Più di 3 anni di esperienza con Magento

• Lead developer di progetti Magento per Privalia, Reckitt Benckiser e Groupalia

• Zend Framework Certified Engineer

@akira28

Yameveo

Fondata nel 2012 a Barcellona, Yameveo è una società giovane, dinamica ed internazionale, specializzata

nell’e-commerce e nello sviluppo di applicazioni web. !

!

www.yameveo.com @Yameveo

@akira28

Yameveo StoreDecine di moduli per Magento e Prestashop

store.yameveo.com

@akira28

Di cosa parleremo

• Intro alle API SOAP di Magento

• Estensione delle API Magento

• Estensione 3rd party

• Creare una nuova chiamata API

• Il sistema Cron di Magento

@akira28

Magento SOAP

“Le API SOAP di Magento offrono la possibilità di gestire i negozi e-commerce, fornendo chiamate per lavorare con

risorse quali clienti, categorie, prodotti e ordini. Consentono inoltre di gestire i carrelli e l'inventario.”

Documentazione Magento

@akira28

Applicazioni• Integrazione con operatori logistici

• Integrazione con Warehouse

• Integrazione con CRM ed ERP

• Integrazione con altri servizi via middleware

• App mobile

• …

@akira28

API MagentoQuando viene effettuata una richiesta HTTP il sistema di

routing standard di Magento invia la richiesta ad una action del controller del modulo Mage_Api

!

Questa action istanzia un oggetto "Server API", lo inizializza con il tipo di API (SOAP, XML-RPC, etc.), ed in seguito

chiama il suo metodo “run”

@akira28

Come iniziare• Assicurati che l’endpoint sia accessibile:

www.example.com/api/v2_soap www.example.com/api/soap

• Definisci Utenti API e le risorse associate: System -> Web Services

• Dai un’occhiata al WSDLwww.example.com/api/v2_soap?wsdl=1 www.example.com/api/soap?wsdl=1

@akira28

Magento API flow

• Crea un Session ID effettuando una richiesta “login” con username e API key

• Salva il Session ID per effettuare altre chiamate

• Effettua le altre chiamate passando il Session ID

• Termina la sessione con endSession

@akira28

Testing API V1// connect to soap server $client = new SoapClient('http://apimagento.dev/api/soap?wsdl=1', array('cache_wsdl' => WSDL_CACHE_NONE, 'trace' => 1)); !// log in $session = $client->login('user', ‘password’); !$info = $client->call($session, 'catalog_product.info', '4'); !var_export($client->__getLastRequest());

http://www.php.net/manual/en/class.soapclient.php

@akira28

Esempio request V1

<?xml version="1.0" encoding="utf-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:Magento" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <ns1:call> <sessionId xsi:type="xsd:string">24eafa404acbd904f3c0978669102496</sessionId> <resourcePath xsi:type="xsd:string">catalog_product.info</resourcePath> <args xsi:type="xsd:int">4</args> </ns1:call> </SOAP-ENV:Body> </SOAP-ENV:Envelope>

@akira28

Esempio response V1array(6) { 'product_id' => string(3) "4" 'sku' => string(3) “sku-1“ 'set' => string(2) "41" 'type' => string(12) "configurable" 'categories' => array(1) { [0] => string(1) "4" } 'websites' => array(1) { [0] => string(1) "1" } }

@akira28

Testing API V2// connect to soap server $client = new SoapClient('http://apimagento.dev/api/v2_soap?wsdl=1', array('cache_wsdl' => WSDL_CACHE_NONE, 'trace' => 1)); !// log in $session = $client->login('user', ‘password'); !$complexFilter = array( 'complex_filter' => array( array( 'key' => 'type', 'value' => array('key' => 'eq', 'value' => 'configurable') ) ) ); $list = $client->catalogProductList($session, $complexFilter); !var_export($client->__getLastRequest());

@akira28

Esempio request V2<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="urn:Magento" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <ns1:catalogProductList> <sessionId xsi:type="xsd:string">c84d5edf23f22913066f4246d0949908</sessionId> <filters xsi:type="ns1:filters"> <complex_filter SOAP-ENC:arrayType="ns1:complexFilter[1]" xsi:type="ns1:complexFilterArray"> <item xsi:type="ns1:complexFilter"> <key xsi:type="xsd:string">type</key> <value xsi:type="ns1:associativeEntity"> <key xsi:type="xsd:string">eq</key> <value xsi:type="xsd:string">configurable</value> </value> </item> </complex_filter> </filters> <storeView xsi:nil="true" /> </ns1:catalogProductList> </SOAP-ENV:Body> </SOAP-ENV:Envelope>

@akira28

Esempio response V2array(118) { [0] => class stdClass#2 (9) { public $product_id => string(2) "16" public $sku => string(5) "n2610" public $name => string(16) "Nokia 2610 Phone" public $set => string(2) "38" public $type => string(6) "configurable" public $category_ids => array(1) { [0] => string(1) "8" } public $website_ids => array(1) { [0] => string(1) "1" } public $price => double(149.99) public $available_quantity => int(996) } […]

@akira28

abstract class Mage_Api_Model_Server_Handler_Abstract { public function startSession(){[…]} ! public function endSession($sessionId){[…]} ! public function login($username, $apiKey){[…]} ! public function call($sessionId, $apiPath, $args = array()){[…]} public function multiCall($sessionId, array $calls = array(), $options = array()){[…]} public function resources($sessionId){[…]} public function globalFaults($sessionId){[…]} public function resourceFaults($sessionId, $resourceName){[…]} !} !

app/code/core/Mage/Api/Model/Server/Handler/Abstract.php

API SOAP Handler

@akira28

Differenze API V1 e V2

• V2 è adatta ad applicazioni scritte in linguaggi tipizzati come Java o .NET

• Il WSDL V2 è molto più grande

• Nella V2 esiste un metodo per ogni chiamata: V1 - $client->call($sid, ’catalog_product.info’, …);V2 - $client->catalogProductInfo($sid, …);

@akira28

@akira28

Problemi

• Le chiamate native non contengono abbastanza informazioni

• Le estensioni di terze parti quasi mai offrono API

• Non esistono call per alcune risorse

@akira28

Soluzioni

• Estensione delle API native

• Inclusione delle estensioni di terze parti

• Nuove call API

Estensione delle API native

@akira28

Estensione ProductInfo

• Configurazione del modulo

• Codice PHP

• WSDL

@akira28

Estensione ProductInfo - config

[…] <global> <models> […] <catalog> <rewrite> <product_api>Yameveo_Productinfo_Model_Catalog_Product_Api</product_api> <product_api_v2>Yameveo_Productinfo_Model_Catalog_Product_Api_V2</product_api_v2> </rewrite> </catalog> […] !

Yameveo/Productinfo/etc/config.xml

@akira28

class Yameveo_Productinfo_Model_Catalog_Product_Api extends Mage_Catalog_Model_Product_Api { […] public function info($productId, $store = null, $attributes = array(), $identifierType = null) { $product = $this->_getProduct($productId, $store, $identifierType); […] return $this->infoResult($result, $product, $attributes, $store); } […]

Yameveo/Productinfo/Model/Catalog/Product/Api.php

ProductInfo - codice

@akira28

ProductInfo - codice

class Yameveo_Productinfo_Model_Catalog_Product_Api extends Mage_Catalog_Model_Product_Api { […] protected function infoResult($result, $product, $attributes, $store) { […] } elseif ($product->isConfigurable()) { $childProducts = Mage::getModel('catalog/product_type_configurable')->getUsedProducts(null, $product); $skus = array(); foreach ($childProducts as $childProduct) { $skus[$childProduct->getId()][‘sku’] = $childProduct->getSku(); […] } $result['configurable_products_data'] = $skus; […] return $result; } }

Yameveo/Productinfo/Model/Catalog/Product/Api.php

@akira28

Estensione ProductInfo - wsdl[…] <complexType name="catalogProductReturnEntity"> <all> […] <element name="configurable_products_data" type="typens:childrenEntityArray" minOccurs="0"/> […] </all> </complexType> !<complexType name="childrenEntityArray"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:childrenEntity[]"/> </restriction> </complexContent> </complexType> <complexType name="childrenEntity"> <all> <element name="sku" type="xsd:string" minOccurs=“0"/> […] </all> </complexType> […] !

Yameveo/Productinfo/etc/wsdl.xml

Inclusione delle estensioni di terze parti

@akira28

Estensione OrderApi

• Configurazione del modulo

• Codice PHP

• WSDL

@akira28

M2E Pro

@akira28

Estensione OrderApi - config

![…] <models> <yameveo_orderapi> <class>Yameveo_OrderApi_Model</class> </yameveo_orderapi> <sales> <rewrite> <order_api>Yameveo_OrderApi_Model_Order_Api</order_api> <order_api_v2>Yameveo_OrderApi_Model_Order_Api_V2</order_api_v2> </rewrite> </sales> </models> […]

Yameveo/OrderApi/etc/config.xml

@akira28

Estensione M2ePro - codiceclass Yameveo_OrderApi_Model_Order_Api extends Mage_Sales_Model_Order_Api { public function info($orderIncrementId) { $result = parent::info($orderIncrementId); $ebayOrder = Mage::helper('M2ePro/Component_Ebay') ->getCollection('Order') ->addFieldToFilter('magento_order_id', $result['order_id']) ->getFirstItem(); ! if ($ebayOrder->getData('ebay_order_id')) { $result['ebay'] = array(); $result['ebay']['ebay_order_id'] = $ebayOrder->getData('ebay_order_id'); $result['ebay']['selling_manager_record_number'] = $ebayOrder->getData('selling_manager_record_number'); […] foreach ($ebayOrder->getItemsCollection() as $item) { $itemArray = array( […] 'buy_it_now_price' => $item->getData('buy_it_now_price'), […] ); ! $result['ebay']['items'][] = $itemArray; } } return $result; } […]

Yameveo/OrderApi/Model/Order/Api.php

@akira28

Estensione M2ePro - wsdl[…] <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:Magento"> <complexType name="salesOrderEntity"> <all> <element name="ebay" type="typens:ebayOrder" minOccurs="0" maxOccurs="1"/> </all> </complexType> <complexType name="ebayOrder"> <all> <element name="ebay_order_id" type="xsd:string" minOccurs="0" maxOccurs="1"/> <element name="items" type="typens:ebayOrderItems" minOccurs="0" /> […] </all> </complexType> <complexType name="ebayOrderItems"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:ebayOrderItem[]" /> </restriction> </complexContent> </complexType> <complexType name=“ebayOrderItem"> […] <element name="buy_it_now_price" type="xsd:string" minOccurs="0" maxOccurs=“1"/> […] </complexType> […]

Yameveo/OrderApi/etc/wsdl.xml

Nuova call API

@akira28

Estensione RmaApi

• Configurazione del modulo

• Codice PHP

• WSDL

@akira28

RmaApi - config<config> <modules> <Yameveo_RmaApi> <version>1.0</version> </Yameveo_RmaApi> </modules> <global> <models> <rma> <class>Yameveo_RmaApi_Model</class> </rma> </models> <helpers> <rma> <class>Yameveo_RmaApi_Helper</class> </rma> </helpers> </global> </config>

Yameveo/RmaApi/etc/config.xml

@akira28

RMA API api.xml<config> <api> <resources> <rma translate="title" module="rmaapi"> <model>rma/api</model> <title>Rma Resource</title> <acl>rma</acl> <methods> <info translate="title" module="rmaapi"> <title>Retrieve RMA information</title> <method>info</method> <acl>rma/info</acl> </info> </methods> <faults module="rmaapi"> <invalid_protocol> <code>106</code> <message>This operation available through SOAP v2 only.</message> </invalid_protocol> </faults> </rma> </resources> <resources_alias> <rma>rma</rma> </resources_alias> <v2> <resources_function_prefix> <rma>rma</rma> </resources_function_prefix> </v2> <acl> <resources> <rma translate="title" module="rmaapi"> <info translate="title" module="rmaapi"> <title>Retrieve RMA information</title> </info> </rma> </resources> </acl> </api> </config>

Yameveo/RmaApi/etc/api.xml

@akira28

RmaApi - codiceclass Yameveo_RmaApi_Model_Api extends Mage_Api_Model_Resource_Abstract { /** * Info on RMA * * @param string $incrementId * @return info */ public function info($incrementId) { $rma = Mage::getModel('enterprise_rma/rma') ->getCollection() ->addFieldToFilter('increment_id', $incrementId) ->getFirstItem(); $rmaData = $rma->getData(); foreach ($rma->getItemsForDisplay() as $item) { $rmaData['items'][] = $item->getData(); } $comments = Mage::getResourceModel('enterprise_rma/rma_status_history_collection') ->addFilter('rma_entity_id', $rma->getId()); foreach ($comments as $comment) { $rmaData['comments'][] = $comment->getData(); } $rmaData['shipping_label'] = $rma->getShippingLabel()->getData(); ! return $rmaData; }

Yameveo/RmaApi/Model/Api.php

@akira28

RmaApi - wsdl[…] <message name="rmaInfoRequest"> <part name="sessionId" type="xsd:string"/> <part name="rmaIncrementId" type="xsd:string"/> </message> <message name="rmaInfoResponse"> <part name="result" type="typens:rmaExportEntity"/> </message> [...] <portType name="{{var wsdl.handler}}PortType"> <operation name="rmaInfo"> <documentation>Info on RMA</documentation> <input message="typens:rmaInfoRequest"/> <output message="typens:rmaInfoResponse"/> </operation> </portType> <binding name="{{var wsdl.handler}}Binding" type="typens:{{var wsdl.handler}}PortType"> <operation name="rmaInfo"> <soap:operation soapAction="urn:{{var wsdl.handler}}Action"/> <input> <soap:body namespace="urn:{{var wsdl.name}}" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body namespace="urn:{{var wsdl.name}}" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding>

@akira28

RmaApi - wsdl<complexType name="rmaExportEntity"> <all> [...] <element name="customer_custom_email" type="xsd:string" minOccurs="0" maxOccurs="1"/> <element name="items" type="typens:rmaExportEntityItemsArray" minOccurs="1" maxOccurs="1"/> [...] </all> </complexType> <complexType name="rmaExportEntityItemsArray"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:rmaExportEntityItem[]"/> </restriction> </complexContent> </complexType> <complexType name="rmaExportEntityItem"> <all> [...] <element name="product_sku" type="xsd:string" minOccurs="0"/> [...] </all> </complexType>

Yameveo/RmaApi/etc/wsdl.xml

@akira28

@akira28

Cos’altro si può fare con le API

• Esportare ed importare ordini, prodotti, clienti

• Checkout via SOAP per applicazioni esterne

• Creare promozioni

• Gestire il CMS

• ….

• Le possibilità sono infinite!

@akira28

Magento Cron

• Cos’è

• Come funziona

• Come può risultare utile

• Come si crea un task

@akira28

Magento Cron!

• Il meccanismo Cron di Magento viene attivato periodicamente utilizzando un cronjob del sistema

• La chiamata viene avviata nel file di cron.php

• Il codice in cron.php invoca Mage_Cron_Model_Observer → dispatch(), che a sua volta provvederà a:

• eseguire le operazioni pianificate

• generare, se necessario, task da eseguire in futuro

• ripulire la history delle operazioni eseguite

@akira28

Utilizzi del cron• Export automatico

• Report ordini giornaliero

• Aggiornamento statistiche

• Invio mail per cart abbandonati

• …

@akira28

Creare un task cron - config

[…] <crontab> <jobs> <yameveo_export> <schedule> <cron_expr>0 2 * * *</cron_expr> </schedule> <run> <model>yameveo/observer::inventoryExport</model> </run> </yameveo_export> </jobs> </crontab> […]

Yameveo/Export/etc/config.xml

@akira28

Creare un task cron - codiceclass Yameveo_Export_Model_Observer extends Mage_Core_Model_Observer { /** * Cron method to export the complete product inventory to the configured ftp server */ public function inventoryExport() { $products = Mage::getModel(‘catalog/product’)->getCollection(); // build data to export foreach ($products as $product) { […] } $this->sendToFtp($data); } […] }

Yameveo/Export/Model/Observer.php

@akira28

Configurare cron da backend

[…] <settings translate="label" module=“yameveo_export"> […] <fields> <cron_settings translate="label comment" module="yameveo_export"> <label>How often do you want the cron to run?</label> <frontend_type>text</frontend_type> <sort_order>70</sort_order> <comment>Use Crontab Format (Eg. "0 4 * * *" for every day at 4 a.m.)</comment> <show_in_default>1</show_in_default> </cron_settings> </fields> </settings> […]

Yameveo/etc/system.xml

@akira28

Configurare cron da backend

[…] <crontab> <jobs> <yameveo_export> <schedule> <config_path>export_config/settings/cron_settings</config_path> </schedule> <run> <model>yameveo_export/observer::inventoryExport</model> </run> </yameveo_export> </jobs> </crontab> […]

Yameveo/Export/etc/config.xml

Volete vedere altro codice?

@akira28

@akira28

Risorse• www.yameveo.com

• github.com/Yameveo

• twitter.com/Yameveo

• alanstorm.com

• www.magentocommerce.com/api

Domande?

Grazie!www.yameveo.com !

@akira28 @Yameveo !

http://bit.ly/andreadepirro

@akira28

yameveo@yameveo.com

STIAMO ASSUMENDO!