api soap e cron: integrare magento con servizi esterni
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
@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