This page is also available in English. Change to English page

How to: Wie entwickle ich Apps für die Shopware Cloud?

Shopware 6

Jetzt Shopware testen

Onlineshop erstellen
SW6-DEV-Blog_Apps_DE-860x325px-Blog

To the English article

Warum ein neues App-System?

Die kurze Antwort: Durch die Cloud verändern sich die Dinge.

Ein neues Verfahren zur Entwickelung von Shopware-Erweiterungen bedeutet nicht, dass Plugins der Vergangenheit angehören. Ganz im Gegenteil: Plugins sind eine sehr gute Möglichkeit, den Shopware-Core zu erweitern und direkt mit diesem zu interagieren.

Aber in Cloud-Umgebungen ist jede Zeile Code eine Belastung für die Instanzen der Benutzer. Deshalb ist notwendig das Coupling der Schnittstelle zwischen Shopware-Instanzen und Erweiterungen gering zu halten.

Aber was meine ich mit diesem Coupling?

Ein Plugin ist abhängig von individuellen Fakten. Einige sind offensichtlich, etwa die Shopware-Version, die euer Plugin benötigt. Andere sind transitiv, etwa die Verwendung von Symfony 4 und PHP 7, die für den Betrieb der Shopware-Plattform benötigt werden. Die letzte Form von Abhängigkeiten ist die gefährlichste: die impliziten Abhängigkeiten.

Diese sind leicht zu übersehen, da sie nicht sofort auffallen. Nehmen wir beispielsweise die Reihenfolge einer Query, ohne dass explizit eine Sortierung angegeben ist.

Die Ergebnisse können zwar nach einer Standardreihenfolge sortiert sein, wenn sich eure Logik aber auf diese Reihenfolge verlässt und sie nicht explizit hinzugefügt wird, kann man diese Abhängigkeit nur schwer erkennen. Deshalb kann jemand anderes leicht, und meist unabsichtlich, gegen diese Annahme verstoßen.

Coupling bedeutet in diesem Kontext die Anzahl der Dependencies in eurem Code, was nicht heißen muss, dass diese automatisch schlecht sind. In den meisten Fällen sind sie notwendig, damit überhaupt etwas geschieht. Aber sie binden eure Software auch eng an fremden Code, von dem sie wortwörtlich abhängig ist. Eine neue Version einer dieser Dependencies kann einen Break verursachen, ganz zu schweigen von den vielen Kombinationsmöglichkeiten von Versionen mit unterschiedlichen Abhängigkeiten.

Es gibt jedoch Möglichkeiten, dieses Coupling zu verringern und Systeme weniger anfällig zu machen.

Ein üblicher Weg ist die Reduzierung des Bereichs möglicher Breaks. Das bedeutet, möglichst wenige Annahmen zu treffen, beispielsweise gibt es keinen Check, dass ein eingehendes Array mit genau zwei Elementen 'names' und 'ids' gefüllt ist. Stattdessen prüft das System, dass diese beiden Felder vorhanden sind, ignoriert aber alle zusätzlichen Felder. Das bedeutet, dass zukünftige Änderungen, die Felder wie 'timestamp' hinzufügen, euren Code nicht beeinträchtigen. Die andere übliche Methode ist die Formalisierung von Abhängigkeiten. Das bedeutet, auf Sprach- oder zumindest auf Konfigurationsebene deutlich zu machen, was der Code als gegeben voraussetzt.

Das ist im Wesentlichen das, was Interfaces in PHP und anderen Sprachen tun.

Das waren einige unserer Grundüberlegungen bei der Entwicklung des neuen App-Systems. Das App-System basiert vollständig auf HTTP. Das ermöglicht uns die Reduzierung der Kontaktflächen zwischen eurer App und dem Shopware-Core.

Eine netzwerkbasierte Schnittstelle ermöglicht nicht nur die Verteilung der Komplexität über Geräte und Domänen, was im Wesentlichen das Prinzip der Microservice-Architektur ist, sondern fördert auch die Stabilität in beide Richtungen. Die App wird stabiler gegenüber Codeänderungen, da sie den Shopware-Code gar nicht mehr kennen muss. Shopware-Instanzen werden weniger von der Implementierung eurer App beeinträchtigt, da Apps nicht mehr mit deren Quellcode interagieren.

Es gibt auch eine explizite Definition der Schnittstelle zwischen eurer App und der Shopware-Instanz. Sie wird durch eine XML-Datei definiert, die die Events bestimmt, über die eure App benachrichtigt werden muss. Diese 'manifest.xml' macht deutlich, was eure App braucht und wie sich das Verhalten der Shopware-Instanz auswirken könnte.

Um die Schnittstelle noch stabiler zu machen, haben wir versucht, sie möglichst schlank zu halten. In einer HTTP-Schnittstelle lässt sich zum Beispiel leichter feststellen, wie sich Änderungen des Plugin-Systems auf Apps auswirken. Das verringert das Risiko, sie unbeabsichtigt zu beeinträchtigen.

Nur noch Apps?

All das ermöglicht die Entwicklung von Apps, die mit demselben Code in selbst gehosteten Umgebungen und in der Shopware Cloud laufen. Dennoch gibt es Fälle, in denen ein Plugin vielleicht die bessere Wahl ist.

Deshalb ersetzen wir das Plugin-System nicht. Plugins sind eine wertvolle Möglichkeit, in der Tiefe mit dem Shopware-Core zu interagieren, und wir wollen diese Form der Erweiterung unbedingt beibehalten.

Nicht zu vergessen: Du kannst mit Apps eine größere Zahl von Händlern erreichen, da diese in der Cloud betrieben werden können.

Zeit für eine Demonstration

Aber kommen wir zu den Apps selbst. Eine App muss mithilfe eines eventbasierten Workflows modelliert werden. Nehmen wir zum Beispiel an, wir wollen eine Kommissionierliste für eingehende Bestellungen generieren.

Wie setzen wir das um? Viele Programme arbeiten in etwa mit dem gleichen folgenden Interaktionsmuster:

1.)  Daten lesen
2.) Ergebnis berechnen
3.) Ergebnis ausgeben

Und Apps folgen demselben Muster. Schließlich sind sie Webanwendungen, sie erhalten also einen Request, bearbeiten ihn und geben eine Response zurück.

Lesen wir zunächst die relevanten Daten für unsere App. Das App-System kann unsere App über Änderungen in den Shops informieren, in denen sie installiert ist. Die App muss also nicht die Shops abfragen. Um über alle neuen Bestellungen benachrichtigt zu werden, fügen wir ein paar Zeilen zur manifest.xml hinzu:

Das WebHook-Element beschreibt alle Ereignisse, über die die App benachrichtigt werden muss – in diesem Fall jedes Mal, wenn eine Bestellung aufgegeben wird. Dann wird ein Request an unseren App-Server unter der angegebenen URL gesendet.

Als Nächstes muss unsere App die Events verarbeiten, die sie abonniert hat. Dafür könnte man einen beliebigen Technologie-Stack verwenden, aber da ich in erster Linie PHP-Entwickler bin, mache ich es mir hier einfach und gehe von einem normalen Symfony-Skelett aus. So könnte der Controller aussehen:

Hier werden die Ereignisdaten entpackt und die Bestell-ID wird abgerufen. Da unsere App jetzt weiß, welche Bestellung erstellt wurde, kann sie einfach alle weiteren benötigten Daten abrufen, etwa so:

Unser Code erstellt ein Criteria für das Lesen der aufgegebenen Bestellung und das Hinzufügen aller Artikel dieser Bestellung. Die 'searchOrders'-Methode sendet dieses Criteria dann JSON-serialisiert an die API des Shops und gibt die Bestellung als Array aus.

Nachdem wir den ersten Teil unseres Workflows abgeschlossen haben, fügen wir jetzt den Code hinzu, der die eigentliche Kommissionierliste erstellt:

Diese Schleife iteriert über alle zuvor abgerufenen Artikel und erstellt aus jedem Artikel eine Zeile unserer Kommissionierliste.

Damit haben wir Teil Zwei unserer App abgeschlossen. Als Letztes muss unsere Kommissionierliste noch in die Datenbank des Shops geschrieben werden.

Wir können sie nicht einfach von unserer Abfrage zurücksenden, denn das App-System weiß dann nicht, wohin damit.

Beim Zurücksenden von Daten an den Shop haben wir die Wahl. Das App-System kennt derzeit zwei Wege, benutzerdefinierte Daten an den Shop zurückzusenden:

1.)  Ein benutzerdefiniertes Admin-Modul, das eure eigene Web-UI über ein iFrame einbindet
2.) Das Hinzufügen benutzerdefinierter Felder zu vorhandenen Entitys

Die erste Option ist zweifellos verlockend, aber auch etwas überdimensioniert für unsere kleine Kommissionierliste.

Bei der zweiten Option müssen wir unsere Kommissionierliste zur Bestell-Entity hinzufügen. Das können wir tun, indem wir erneut die manifest.xml erweitern:

Wie man sieht, ist das eine vollständig deklarative Art der Erweiterung von Entitys auf Basis der bereits etablierten Zusatzfelder.

Außerdem sieht man an den Label-Elementen im obigen Beispiel, dass das App-System über eine integrierte Internationalisierung verfügt.

Mit dem benutzerdefinierten Feld in jeder Bestellung gibt es jetzt eine Möglichkeit, die Kommissionierliste anzuzeigen. Aber wie werden diese Felder befüllt?

Hier kommen wir zurück zum Controller für unseren WebHook:

Wir können die API verwenden, um die Bestellung über einen kleinen PATCH Request mit unserer Kommissionierliste zu aktualisieren.

Wie man sieht, greifen die Apps überwiegend auf die bereits im Core von Shopware 6 enthaltene Funktionalität zurück. Neu ist lediglich, wie eure App ihre Schnittstelle mit der Shopware-Instanz und mit deren API und UI definieren kann.

Ich bräuchte noch einige Blogbeiträge mehr, um alles zu erläutern, was mich an den Möglichkeiten des Systems begeistert – lasst mich also einfach nur einige weitere Fähigkeiten des App-Systems auflisten, ohne zu sehr ins Detail zu gehen:

  • Apps müssen Berechtigungen festlegen, um die Schnittstelle mit dem angesprochenen Shop weiter zu festigen.
  • Apps können jetzt ganze Themes auf dieselbe Weise wie Plugins bereitstellen.
  • In der Administration können Buttons zu Listen- und Detailseiten hinzugefügt werden.
  • Die manifest.xml wird vollständig durch ein XSD-Schema definiert, dadurch ist auch IDE-Integration leichter.

Wo stehen wir jetzt?

Du kannst ab sofort mit dem App-System experimentieren. Wir haben es als Plugin veröffentlicht.

Mit diesem Plugin kannst Du einen Shop mit einer Bleeding-Edge-Version des App-Systems patchen. Wir arbeiten derzeit an einer produktionsreifen Version. Aber dieses System ist nicht nur für Dich neu, sondern auch für uns. Deshalb freuen wir uns auf Dein Feedback.

Tatsächlich haben wir seit der ersten Ankündigung des App-Systems beim diesjährigen Community Day bereits wertvolles Feedback erhalten. Das hat uns dabei geholfen, Funktionen zu priorisieren, um Apps zu einer echten Alternative zur Entwicklung von neuen Plugins zu machen.

Was die Zukunft bringen wird

Wir arbeiten derzeit an zwei Dingen. Erstens wollen wir in den kommenden Monaten das App-System mit seinem aktuellen Funktionsumfang verfeinern und es in den Shopware-Core integrieren. Zweitens bauen wir einen direkt in die Administration integrierten App-Store auf, damit Shopbetreiber ihre Shopware-Instanz direkt aus der Administration heraus erweitern können.

Außerdem sind wir uns im Klaren darüber, dass Erstellung und Pflege einer App eine ziemliche Umstellung gegenüber der Entwicklung von Plugins ist. Deshalb haben wir uns mit platform.sh zusammengetan, um euch ein Template für die Entwicklung von Apps bereitzustellen.

Dazu wirst Du in Kürze mehr erfahren. Ebenso planen wir umfangreiche Materialien und Ressourcen, die euch unterstützen werden.

Wie bei allen iterativen Entwicklungen evaluieren wir auch kontinuierlich neue mögliche Funktionen zur Erweiterung des Systems.

Zum Abschluss habe ich unten noch einige weitere Informationen aufgelistet, um Dir im Hinblick auf das App-System auf den neuesten Stand zu bringen.

Ressourcen