Türchen 01: Magento-Webservices

Ich habe mir die Ehre nicht nehmen lassen das erste Türchen unseres Adventskalenders 2011 zu schreiben. Auch dieses Jahr möchten wir Euch die Wartezeit bis Weihnachten mit einigen Magento-Tipps und Tricks etwas verkürzen. Dennoch ist dieser Adventskalender etwas ganz besonderes. Uns ist es gelungen für die kommende Adventsreihe viele viele Gastautoren zu gewinnen. Alle sind dabei absolute Experten in Umgang mit Magento und zudem auch noch sehr hilfsbereit und Community orientiert. Menschen die ein Projekt wie Magento einfach braucht. Ich freue mich sehr auf die kommenden Beiträge! Ihr hoffentlich ebenso :)

Webservices mit Magento

Das bauen eines eigenen Magento-Webservice hat sich, wie ich feststellen musste, als alles andere als einfach erwiesen. Zu Beginn ist es wichtig zu verstehen das es vier unterschiedliche Webservices gibt:

SOAP-Webservice v1

Der SOAP-Webservice in der ersten Version arbeitet die Aufrufe alle über eine zentrale Call bzw. Multicall-Methode ab. Das erzeugen eines Produktes ist in diesem Fall wie folgt ausgebaut:

$proxy = new SoapClient('http://magentohost/api/soap/?wsdl');
$sessionId = $proxy->login('apiUser', 'apiKey');
$proxy->call($sessionId, 'product.create', array('simple',..));

Wie man erkennt wird auf Basis der aktuellen Session-ID ($sessionId) eine Anfrage an Magento geschickt. Dazu wird 'product.create' mit unterschiedlichen Parametern aufgerufen. Der Vorteil an diesem Webservice ist das er sich schnell und einfach erweitern lässt. Ein großer Nachteil besteht jedoch darin das er nicht kompatibel zu Java oder .NET ist. Dies begründet sich durch die Tatsache das der Webservice keine definierten Typen zurückgibt sondern auschließlich ein ANYTYPE liefert. Damit kommt z.B. Java nicht zurecht und verweigert eine einfache Zusammenarbeit mit dem Webservice. Dazu auch ein Auszug aus dem WSDL:

<message name="call">
  <part name="sessionId" type="xsd:string"/>
  <part name="resourcePath" type="xsd:string"/>
  <part name="args" type="xsd:anyType"/>
</message>
<message name="callResponse">
  <part name="callReturn" type="xsd:anyType"/>
</message>

SOAP-Webservice v2

Der Soap-Webservice v2. geht einen Schritt weiter. Hier werden klare einzelne Methoden im Webservice angelegt welche ebenso eine klare Rückgabe inkl. Typen enthalten. Das bedeutet jedoch das die Methoden nicht mehr, wie im oberen Beispiel, einfach alles entgegen und zurückgeben können. War das WSDL in der Api-V1 noch eine eierlegende Wollmilchsau (die jedoch niemand verstanden hat) müssen nun möglichst atomare Methoden geschaffen werden.

Magento kennt ihr eine Basis-Entität welche die in der Regel notwendigen Attribute inkl. Typdefiniton wiederspiegelt. Im WSDL sieht das dann wie folgt aus:

<complexType name="catalogProductCreateEntity">
    <all>
        <element minOccurs="0" name="categories" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="websites" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="name" type="xsd:string"/>
        <element minOccurs="0" name="description" type="xsd:string"/>
        <element minOccurs="0" name="short_description" type="xsd:string"/>
        <element minOccurs="0" name="weight" type="xsd:string"/>
        <element minOccurs="0" name="status" type="xsd:string"/>
        <element minOccurs="0" name="url_key" type="xsd:string"/>
        <element minOccurs="0" name="url_path" type="xsd:string"/>
        <element minOccurs="0" name="visibility" type="xsd:string"/>
        <element minOccurs="0" name="category_ids" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="website_ids" type="typens:ArrayOfString"/>
        <element minOccurs="0" name="has_options" type="xsd:string"/>
        <element minOccurs="0" name="gift_message_available" type="xsd:string"/>
        <element minOccurs="0" name="price" type="xsd:string"/>
        <element minOccurs="0" name="special_price" type="xsd:string"/>
        <element minOccurs="0" name="special_from_date" type="xsd:string"/>
        <element minOccurs="0" name="special_to_date" type="xsd:string"/>
        <element minOccurs="0" name="tax_class_id" type="xsd:string"/>
        <element minOccurs="0" name="tier_price" type="typens:catalogProductTierPriceEntityArray"/>
        <element minOccurs="0" name="meta_title" type="xsd:string"/>
        <element minOccurs="0" name="meta_keyword" type="xsd:string"/>
        <element minOccurs="0" name="meta_description" type="xsd:string"/>
        <element minOccurs="0" name="custom_design" type="xsd:string"/>
        <element minOccurs="0" name="custom_layout_update" type="xsd:string"/>
        <element minOccurs="0" name="options_container" type="xsd:string"/>
        <element minOccurs="0" name="additional_attributes" type="typens:associativeArray"/>
        <element minOccurs="0" name="stock_data" type="typens:catalogInventoryStockItemUpdateEntity"/>
    </all>
</complexType>

Zu der Basis-Entität ist noch eine weitere Entität verfügbar. Diese kümmert sich darum das eigene Attribute ebenfalls über den SOAP-Webservice gefüttert werden können. Dazu auch hier ein Ausschnitt aus dem WSDL:

<complexType name="associativeEntity">
    <all>
        <element name="key" type="xsd:string"/>
        <element name="value" type="xsd:string"/>
    </all>
</complexType>
<complexType name="associativeArray">
    <complexContent>
        <restriction base="soapenc:Array">
            <attribute ref="soapenc:arrayType" wsdl:arrayType="typens:associativeEntity[]"/>
        </restriction>
    </complexContent>
</complexType>

agoradesign.at liefert dazu folgendes Beispiel für einen Produkt-Import über die Api in der Version 2 mittels PHP:

$client = new SoapClient($pathToWsdl); // set path to your Magento WSDL
$session = $client->login($apiuser, $apikey); // specify username and password

$catalogProductCreateEntity = new stdClass();
$additionalAttrs = array();

$catalogProductCreateEntity->name = "product name";
$catalogProductCreateEntity->description = "description";
$catalogProductCreateEntity->short_description = "desc";
$catalogProductCreateEntity->status = "1";
$catalogProductCreateEntity->price = "99";
$catalogProductCreateEntity->tax_class_id = "2";
/* you can add other direct attributes here */

$manufacturer = new stdClass();
$manufacturer->key = "manufacturer";
$manufacturer->value = "3";
$additionalAttrs[] = $manufacturer;
/* you can add other additional attributes here like $manufacturer */

// finally we link the additional attributes to the $catalogProductCreateEntity object
$catalogProductCreateEntity->additional_attributes = $additionalAttrs;

// send the request
$client->catalogProductUpdate($session, "your product id", $catalogProductCreateEntity, NULL, "id");

// end session and enjoy your updated products :)
$client->endSession($session);

SOAP-Webservice v2 WS-I Compliance

Zusätzlich gibt es noch einen weiteren Webservice. Dieser teilt sich zwar die eigentliche Implementierung mit dem Webservice v2 besitzt jedoch eigene WSDL-Dateien. Diese sind bzw. sollen WS-I konform sein und somit auch mit .NET und Java problemlos laufen.

XML-RPC - Remote Procudure Calls

Zu meiner Schande muss ich gestehen das ich mich mit XML-RPC bis dato nicht beschäftigt hatte. Zu erwähnen sei das sie den selben Funktionsumfang wie die Magento API v1 bieten und auch ähnlich funktioniert. Dazu ein kleines Beispiel in PHP:

$client = new Zend_XmlRpc_Client('http://youmagentohost/api/xmlrpc/');

// If somestuff requires api authentification,
// we should get session token
$session = $client->call('login', array('apiUser', 'apiKey'));

$client->call('call', array($session, 'somestuff.method', array('arg1', 'arg2', 'arg3')));

Einen eigenen Webservice erstellen?

Um einen eigenen Webservice zu erstellen muss zuerst geklärt werden welcher Webservice angesprochen werden soll. Am einfachsten ist dabei sicherlich der SOAP-v1-Webservice oder  XML-RPC zu realisieren. Der einzelne Prozess ist dabei im Magento Wiki bereits recht gut beschrieben.

Um eine SOAPv2 oder SOAP-WS-I Anbindung zu realisieren ist schon etwas mehr notwendig. Zuerst muss die API-v1 erweitert werden.Der Funktionsumfang kann dabei identisch sein - wichtig ist das eine Klasse existiert die auf _V2 endet. Dazu ein Beispiel: In der Produkt-API werden die Request  zur API-Version 1 über die Klasse Mage_Catalog_Model_Product_Api abgearbeitet. Die Request der Version v2 dagegen über Mage_Catalog_Model_Product_Api_V2.

Zusätzlich muss in der api.xml noch ein Namensmapping angegeben werden. So kann der Syntax der APIv1 auf die APIv2 gelenkt werden. In der Praxis sieht das wie folgt aus:

<v2>
    <resources_function_prefix>
        <category>catalogCategory</category>
        <category_attribute>catalogCategoryAttribute</category_attribute>
    </resources_function_prefix>
</v2>

Danach muss im etc/ des Modules noch eine wsdl.xml bzw. wsi.xml angelegt werden. Erste für die allgemeine WSDL-V2-Api, die zweite für die V2-WSI-Api. Wie der Aufbau genau aussieht würde den Rahmen sprengen. Da verweise ich jedoch gerne auf Amazon: Service-orientierte Architekturen mit Web Services: Konzepte - Standards - Praxis (Link über Partnerprogramm bei Amazon)

Beispiele dazu finden sich z.B. im Modul app/code/core/Catalog. Die Implementierung der Api ist dabei z.B. unter app/code/core/Catalog/Model/Product/Api* zu finden.

Persönliche Erfahrungen

Persönlich schätze ich den Aufwand eines Magento-Webservice in Betrieb zu nehmen für relativ hoch ein. Dazu gehört einmal die Kompatibilität der verschiedenen Systeme: Wenn der Webservice in PHP einwandfrei läuft tut er das noch lange lange nicht in .NET oder Java. Somit ist es unter Umständen sehr aufwendig die Entwicklung hinsichtlich Fehler zu prüfen und natürlich auch diese zu beheben.

Im weiteren muss natürlich ebenso auf die Performance des Webservices geachtet werden: Wie verhält sich das System wenn die andere Software x-Request gleichzeitig absetzt? Welcher Request wird zuerst behandelt und hat das Auswirkungen auf darauf folgende?



Ein Beitrag von Tobias Vogt
Tobias's avatar

Tobias Vogt arbeitet seit 2008 mit Magento und ist seit 2011 durch Magento zertifizierter Entwickler. Seit 2016 ist er Mitgründer und CTO bei der connect-io GmbH, einer Magento-Agentur mit Sitz im idyllischen Paderborn-Salzkotten. Er gehört zum Gründer-Team der Webguys und ist seit November 2011 Bachelor of Science (Wirtschaftsinformatik). Sie erreichen Ihn per E-Mail unter tobi@webguys.de.

Alle Beiträge von Tobias

Kommentare
Matthias Zeis am

Achja, die SOAP-Schnittstelle...seufz. Gerade eben bin ich einem für mich neuen Phänomen auf die Schliche gekommen.

Szenario: der ERP-Dienstleister stellt fest, dass er plötzlich einige Bestellungen nicht mehr abrufen kann (SOAP v2 WS-I Compliance). Eine Systematik ist vorerst nicht zu erkennen, der Großteil geht normal durch. Seit wann das Problem auftritt, kann nicht eruiert werden.

Nach Analyse der SOAP-Response stellt sich heraus: bei den betroffenen Bestellungen wird das XML einfach mittendrin abgeschnitten. Nicht am Ende einer Node, sondern wirklich mittendrin (z.B. "

Dima am

TOP, weiter so.

Eine Frage zu der Produkt WebService API.

Hast du eine Idee warum alle Hacken "Use Default Value" beim Speichern für einen StoreView entfernt werden? Es wird der Name übergeben, aber trotzdem werden alle Hacken im StoreView entfernt. Dieses Verhalten tritt erst ab der Version 1.5 auf.

$client->call($sessionId, 'catalog_product.update', array($productId, array('name'=>'blub'), 1));

Grüße

Tobias Vogt am

.. ui du hattest schon was was WSDL baut? Da hab ich mich letzte Woche auch ewig mit beschäftigt .. :(

Vinai am

Klasse Post! Hier noch ein Link zu einer Prezi von mir zum Thema API v2 erweitern (März 2010): http://prezi.com/5rqtkyol238f/magento-soap-api/

Nicht vergessen Prezi-Mässig schön rein zu zoomen. Ab und zu gibts extrra infos, wie ein zusammen gehacktes Script zum automatischen generieren der wsdl.xml aus einer Magento Klasse.

daim am

Hach es ist doch eigentlich meist genau umgekehrt. PHP hat Probleme die WSDL zu parsen und in Java/.NET läuft es, ganz zu schweigen von solchen Dingen wie WSA

Tobias Vogt am

Das mit den klaren Definitionen ist im PHP-Umfeld leider nicht so wirklich gut. Was mit PHP läuft funktioniert noch lange nicht mit JAVA oder .NET - dementsprechend sind die Definitionen eigentlich zu Fuzzy ... :-/

Michael am

Ja was denn is denn schon Weihnachten?

Sicherlich ist der SOAP Overhead etwas grösser, wiegt aber durch eine klare Schnittstelle vieles wieder auf. Gerade wenn mehrere Seiten (Firmen) an einem Projekt arbeiten, kann man sich auf beiden Seiten auf eine klare Definition verlassen.

Dein Kommentar