08. April 2020, Benjamin Rosenberger

mein-handel.at ‚Äď ein Magento2 Multishop in unter 7 Tagen

mein-handel.at ‚Äď ein Magento2 Multishop in unter 7 Tagen

‚Äď oder wie ein Aprilscherz Wirklichkeit wurde

Magento2 ist nun bereits in das Erwachsenen-Alter eingetreten mit der Version 2.3. Ich bzw. wir haben etliche sehr große Shops bereits umgesetzt und doch konnte ich noch nie die wahre Power des Magento-Untergrunds bis zur Gänze ausreizen.

Am 1. April war es soweit, Bernhard kam mit der Idee zur Umsetzung eines Multiwebsite-Shops f√ľr potenziell alle H√§ndlerinnen und H√§ndler O√Ė. Zun√§chst dachten andere und ich an einen Aprilscherz durch die knappe Deadline (nicht einmal eine ganze Woche!). Nach kurzer √úberlegung bin ich zum Schluss gekommen, dass Magento 2 Community Edition nun seine schier unendlichen M√∂glichkeiten ausspielen kann und wir im Team es schaffen werden.

Die aktuelle Home-Office Herausforderung kam f√ľr mich dann noch zus√§tzlich zum Tragen ‚Äď mit vier Kindern, Hunden und Pferd musste ich mir einen Ort der ‚ÄěRuhe‚Äú gestalten. Bekanntlich entstehen die besten Ideen und Projekte ja in Garagen. Hier ein Eindruck von meinem ‚ÄěMein-Handel-Home-Office-Fokus-Arbeitsplatz‚Äú:

Die Anforderungen

Unsere Anforderungen klingen sehr einfach, haben jedoch jeweils T√ľcken im Detail:

  • Infrastruktuelle Anforderungen wie
    • Ausfallsicherheit
    • Lastverteilung
    • Schnelligkeit
    • ein lokales Entwickeln der gesamten Umgebung muss m√∂glich sein

  • Es soll einen eigenen Shop geben, indem man einen Shop bestellen kann:
    • dieser soll ein gesondertes Men√ľ haben, welches durchgemischt CMS- und Produktseiten beinhaltet
    • eine √úbersicht √ľber alle vorhandenen Shops inkl. Details (Name, Kategoriebranche, Logo,‚Ķ) sollen angezeigt und durchsuchbar sein
    • eine Produktsuche √ľber alle vorhandenen H√§ndlershops soll implementiert werden

  • Es soll viele H√§ndlershops geben, welche
    • einfachst aufgesetzt werden k√∂nnen
    • mit minimalen Einstellungen eine gr√∂√ütm√∂gliche Variation f√ľr die H√§ndlerin und den H√§ndler schaffen (Kategorien, Produkte,‚Ķ)
    • eine einfache Wartungsoberfl√§che f√ľr Produkte und andere Dinge haben
    • als Unterpfad zur Hauptdomain gehostet werden
    • Daten weitgehend begrenzt auf eine H√§ndlerin oder einen H√§ndler haben sollte (Kategoriebaum, Produkte,‚Ķ)

Die Infrastruktur

Um die Infrastruktur abbilden zu können, welche Lasten der einzelnen Shops auf mehrere Server verteilen kann, haben wir diese nach folgendem Schema aufgebaut:

Ein Proxy verteilt entsprechend - der Anfrage - URLs an die entsprechenden Magento Server. Um Bottlenecks zu vermeiden, haben wir uns entschlossen, keine zentralen Instanzen von der Datenbank, Redis und Varnish zu erstellen, sondern diese auf jeden Server f√ľr sich laufen zu lassen. Dies entspricht dem vorgeschlagenen Standard-Server-Setup (siehe dazu auch die DevDocs unter https://devdocs.magento.com/guides/v2.3/config-guide/bk-config-guide.html).

Als Konsequenz dessen ist f√ľr jede Magento2 Instanz eine andere Konfiguration vonn√∂ten. Dies kann bereits durch die Konfigurationsm√∂glichkeiten von Magento2 zum Teil abgebildet werden (siehe dazu die entsprechenden DevDocs: https://devdocs.magento.com/guides/v2.3/config-guide/config/config-php.html).

Configuration as deployable code

Nur leider, wenn man sich unser Szenario weiterdenkt, sind diese 2 Dateien nicht ausreichend, um schnell und zuverlässig Konfiguration auf den Servern zu verteilen, ohne sich per SSH am Server Zugriff zu verschaffen.

Ich habe dazu zwei weitere Konfigurationsdateien eingef√ľhrt, welche sich mit den 2 bestehenden folgenderma√üen definiert sind:

  • conf.php ‚Äď Initialisierung der Magento Module, siehe https://devdocs.magento.com/guides/v2.3/extension-dev-guide/build/enable-module.html, Definition der Themes und eine gewisse quasi unverr√ľckbare Grundeinstellung
  • conf.shared.php ‚Äď Systemeinstellungen, welche f√ľr alle Server gleich sind, inklusive Websites, Stores und Store Views (diese sind hier immer als disabled gekennzeichnet)
  • conf.local.php ‚Äď Aktivierung einzelner Store Views
  • env.php ‚Äď Servereinstellung wie Datenbankverbindungen, Cache-Definitionen,‚Ķ.

Wir nutzen schon immer Gitlab f√ľr die Source-Verwaltung dessen CI/CD M√∂glichkeiten f√ľr den Deploy. Die Build-Pipelines sind entsprechend dem m√∂glichen Magento2 Zero-Downtime-Deployment (siehe dazu die DevDocs unter https://devdocs.magento.com/cloud/deploy/reduce-downtime.html) eingerichtet und erm√∂glichen einen quasi nahtlosen √úbergang einer Version zur n√§chsten (sofern keine Datenbank√§nderungen vorhanden sind).

Wer Magento2 kennt, der wei√ü, dass sich dieser Prozess am Server jedoch abh√§ngig von der Anzahl der Themes, Sprachen, Module,‚Ķ √ľber einen relativ langen Zeitraum ziehen kann (wenige Minuten bis zu fast einer halben Stunde).

Somit sind nun die Konfigurationen auf 3 Stellen aufgeteilt:

  • config.php im Magento2 Repository
  • config.shared.php und config.local.php als eigenes Repository
  • env.php hinterlegt auf den einzelnen Servern

Dadurch ist eine Trennung von Code-Updates (10+ Minuten) und Konfigurations-Updates (< 1 Minute) möglich.

Composer them all

Zus√§tzlich musste beachtet werden, dass man nach wie vor lokal mit allen m√∂glichen Stores weiterentwickeln k√∂nnen muss. Dies sollte automatisch passieren, um viele Fehler des manuellen Kopierens, √Ąnderns etc. zu vermeiden.

Um dieses Problem zu l√∂sen, setzen wir auf M√∂glichkeit √ľber Composer einfache Befehle ausf√ľhren zu k√∂nnen (siehe dazu auch die Composer-Dokumentation unter https://getcomposer.org/doc/articles/scripts.md#what-is-a-script-)

Folgende Dinge können nun automatisiert gesteuert werden:

  • Unterscheidung zwischen Entwicklerumgebungen (composer update) und Serverumgebungen (composer install)
  • Installieren notwendiger Sicherheitspatches (siehe dazu https://magento.com/security/patches) sobald diese erscheinen oder ein Upgrade auf die n√§chste Minor-Version noch nicht m√∂glich ist
  • Einspielen manueller Patches des Cores, welche noch nicht in das aktuelle Magento2 Release aufgenommen worden sind (etwa offene Pull Request, oder bereits f√ľr neuere Magento2 Versionen vorgesehene Pull Requests)
  • Kopieren der n√∂tigen Konfigurationen aus den installierten Modulen

Testen aller Schritte

Gitlab‚Äôs CI/CD Funktionalit√§ten bieten die M√∂glichkeit mehrere Umgebungen und deren Abh√§ngigkeiten zu definieren (n√§here Informationen unter https://docs.gitlab.com/ee/ci/introduction/). So k√∂nnen alle √Ąnderungen (sowohl Code, als auch Konfiguration) zuerst am Testsystem deployed und getestet und in weiterer Folge am Live-System ausgerollt werden.

Der Einstiegsshop

Ein Shop um einen Shop bestellen ‚Äď wie sieht sowas aus? Diese Frage stellte sich unser Frontend-Team, w√§hrend die Infrastruktur im entstehen war. Ebenso stellten sich viele Fragen technischer Natur:

  • Wie kann man ein Men√ľ definieren, welches nicht auf Kategorien basierend ist? 
  • Wie kann ich mit m√∂glichst Magento2 Funktionen ein Formular erzeugen, welches als Bestellung abgelegt und bezahlt wird? 
  • Wie findet man alle erstellten Shops, wenn diese doch auf verschiedenen Server liegen?

Das Ergebnis lässt sich sehen und ist einfach zu bedienen.

Magento2 und das liebe Topmenu

Die Anforderung scheint so leicht:

  • Ich will direkt auf Seiten im Men√ľ verlinken
  • Ich will direkt auf ein Produkt im Men√ľ verlinken
  • Ich will pro Store View konfigurieren, was als Quelle f√ľr das Men√ľ genommen werden soll

Men√ľs in Magento2 werden wie auch in Magento 1 Versionen von der Topmenu-Klasse (aktuelle Version ist hier zu sehen: https://github.com/magento/magento2/blob/9544fb243d5848a497d4ea7b88e08609376ac39e/app/code/Magento/Catalog/Plugin/Block/Topmenu.php) erstellt. Diese ist sehr starr (generiertes HTML in PHP und nicht der entsprechenden Template-Datei) und basierend auf dem Kategoriebaum. Dinge, die zu tun waren:

  • Herausl√∂sen der HTML Struktur in Template-Dateien
  • Herausl√∂sen der Men√ľbaumgenerierung und Bereitstellung mehrerer Quellen (Kategorien, etwaige andere Men√ľstruktur)

Magento2 und die Customizable Options

Ziel ist es mittels eines Bestellvorgangs alle notwendigen Daten zu erheben, um einen neuen Shop bereitstellen zu können. Hier bieten sich die Customizable Options an, welche eine Vielzahl von Möglichkeiten bieten:

  • ein einziges Produkt im Shop ist ausreichend, auf welches auch direkt vom Men√ľ verlinkt werden kann
  • verschiedene Typen von Eingabefeldern wie Text, Selects, Checkboxen, File-Uploads,‚Ķ 
  • speichern der Optionen und nachtr√§gliches √§ndern √ľber den Warenkorb
  • Dokumentation der ben√∂tigten Informationen zur weiteren Shop-Erstellung und Ausrollung

Einzig und allein was fehlt: Wie kann man den Kundinnen und Kunden Hilfestellungen geben, damit man korrekte Daten erhält? Und so wurde eine Tooltip-Option eingebaut, um Erklärungen zu den einzelnen Feldern bereit zu stellen.

Magento2 und die Systemkonfiguration

Was bringt mir ein Portal, wenn ich Kundinnen und Kunden nicht zu den einzelnen Shops f√ľhren kann? Somit musste eine M√∂glichkeit geschaffen werden, alle Rumpfdaten inklusive so mancher Mediendaten im Einstiegsshop und ebenso in den einzelnen H√§ndlershops zugreifbar zu machen.

Der erste Punkt der Daten ist aufgrund des bereits oben erw√§hnten ‚ÄěConfiguration as deployable code‚Äú leicht zu l√∂sen. Man kennt auf allen Servern alle Rumpfdaten eines Shops um diesen darstellen und suchen zu k√∂nnen.

Nun fehlen nur noch die Mediendaten (wie zB Logos). Das Prinzip hier ist jedoch das gleiche wie bei Konfigurationen, welche √ľber die Datei config.shared.php auf allen Server verteilt werden. Somit musste nur das Deploy-Skript um diese neuen Dateien erweitert werden und schon hat jeder Server alle n√∂tigen Daten.

Die Händlershops

Nun, da neue Shops bestellt werden k√∂nnen, m√ľssen diese auch entsprechende Grundfunktionen beinhalte, welche in folgenden Anforderungen definiert sind:

  • jeder Shop kann unterschiedliche Kategorien haben (jede √Ąnderung einer Kategorie darf sich nur auf diesen einen Shop auswirken)
  • jeder Shop kann unterschiedliche Versand- und Bezahlmethoden haben
  • alle Shops sollen als Pfade hinter der Hauptdomain erreichbar sein
  • es sollen standardisierte Seiten und Bl√∂cke genutzt werden, um die Setup-Zeit zu verringern ‚Äď jegliche Anpassungen k√∂nnen in weiteren Schritten vorgenommen werden
  • die H√§ndlerin oder der H√§ndler muss m√∂glichst einfach seine Produkte einpflegen und warten k√∂nnen
  • wir als Magento2 Betreiber m√ľssen zus√§tzlich zur Konfiguration Einstellungen f√ľr die Kundin oder den Kunden im Original-Backend machen k√∂nnen

Magento2 und die seine Root-Kategorien

Dadurch, dass alle Shops unterschiedliche Kategorieb√§ume haben k√∂nnen und sich gegenseitig nicht beeinflussen d√ľrfen, wird man in Magento2 bereits deutlich eingeschr√§nkt. Somit ist uns klar geworden, wir brauchen pro Shop eine Root-Kategorie, welche dann auf Store Ebene gesetzt werden kann.

Mit der zusätzlichen Anforderung unterschiedlicher Versand- und Bezahlmethoden blieb nur noch die Möglichkeit, jeden Shop als eigene Website in Magento2 abzubilden.

Magento2 und mehrere Websites als Subpfad

Warum die Seiten als Subpfad verwaltet werden sollen, hat vorrangig zwei Gr√ľnde:

  • ohne √Ąnderung der Routinginformationen Proxy kann ein neuer Shop erstellt werden (ginge auch mit Subdomains)
  • man kann die Subpfade so gestalten, dass diese auch SEO-technisch vorteilhaft sind.

Um den SEO-Teil zu erledigen, wurde die Struktur der Pfade mit dem Bezirksk√ľrzel und dem Unternehmensnamen aufgebaut.

Soweit so gut, grunds√§tzlich einstellbar √ľber die jeweilige Base-URL der Website, doch dann fingen die Probleme an:

  • Subpfade werden an die entsprechende index.php als Seite weitergegeben und resultieren in 404-Seiten des Default-Stores
  • erstellt man die Subpfade, muss man auch die entsprechenden Dateien und Ordner verlinken, sodass Magento2 als solches wieder aufgerufen wird
  • setzen der entsprechenden Website-Codes, damit Magento2 die korrekte Seite ausliefert.

Zusätzlich musste bei einem Code- bzw. Konfigurations-Deploy diese Ordnerstruktur erstellt und getestet werden.

F√ľr die Konfiguration habe ich im Konfigurations-Repository eine neue Datei hinterlegt, welche die Information von Subpfad zu Website-Code beinh√§lt. Ein einfaches zus√§tzliches Script liest diese Information aus und erstellt alle ben√∂tigten Ordner und Links im entsprechenden pub/-Verzeichnis.

Magento2 und WYSIWYG-Variablen

Magento2 hat bereits viele vorgefertigte Variablen, die in CMS-Pages und CMS-Bl√∂cke eingef√ľgt werden k√∂nnen. Diese mit allen n√∂tigen Informationen zu erweitern, ist ein leichtes, wenn man das richtige Core Modul kennt.

Hier ist es besonders vorteilhaft, dass die gesamte Shop-Information als Konfiguration gespeichert ist.

Diese k√∂nnen leicht nutzbar gemacht werden, wie es in folgenden DevDoc-Artikel beschrieben ist: https://devdocs.magento.com/guides/v2.3/extension-dev-guide/variable-pool/

Magento2 und die Produktwartung

Man darf ruhig ehrlich sein: das Magento2 Backend ist f√ľr Einsteigerinnen und Einsteiger kompliziert und wenig intuitiv. Die Anforderung hier ist nun folgende:

  • Alle nicht ben√∂tigten Funktionen m√ľssen entfernt werden
  • Jede Benutzerin und jeder Benutzer darf nur die ihr oder ihm zugewiesene Website bearbeiten k√∂nnen.
  • Die H√§ndlerin oder der H√§ndler darf sich bei der Produktpflege nicht von den vielen M√∂glichkeiten ablenken lassen, die wir noch gar keine Beachtung im Frontend geschenkt haben.

Dies sind nun 3 große Problemfelder, die alle in der kurzen Zeit gelöst werden wollten.

Magento2 und die Rechteverwaltung

Das war der leichteste Punkt. Magento2 bietet mit seinem Rollenmanagement, eine einfache und sehr m√§chtige M√∂glichkeit Benutzerinnen und Benutzer auf die Funktionsbereiche einzuschr√§nken, die sie ben√∂tigen. Problem 1 ist gel√∂st. Diese sind aktuell sehr eingeschr√§nkt auf:

  • Produkte erstellen, √§ndern und l√∂schen
  • Kategorien ansehen, √§ndern und l√∂schen

Magento2 und Mehrmandatenfähigkeit

Aktuell ist dies ein Feature der Enterprise-Edition und trotzdem wollte ich es nicht nicht glauben, dass dies in der Community Version nicht mit geringen Aufwand nachzubauen wäre und so habe ich mich entsprechend aufgemacht, um diese Funktion auch mittels Modul bereitzustellen.

Folgende Erweiterungen sind davon betroffen:

  • Hinzuf√ľgen einer Website-Zuordnung bei einer Backend-Benutzerin oder einem Backend-Benutzer
  • Einschr√§nkung aller Website-Aufrufe, um nur die zugewiesene Website zu erhalten
  • Filterung aller Produktanfragen (Listen, Detailansicht,‚Ķ)
  • Filterung aller Kategorieanfragen (Listen, Detailansicht, B√§ume,‚Ķ)

Magento2 und die unwissenden Editoren

Um die Produktwartung so einfach wie nur m√∂glich zu gestalten, sahen wir uns nicht in der Lage das aktuelle Admin-Backend zu verschlanken, damit jeder alles findet. Zus√§tzlich m√ľssten wir alle nicht erlaubten Felder entfernen oder irgendwie √ľber Rechte verbieten. Dies schien uns zu aufwendig, um in der kurzen Zeit eine sinnvolle L√∂sung zu erreichen. Die Idee ein eigenes Backend-Light zu entwerfen war geboren:

  • Produktwartung rein √ľber API Aufrufe
  • Beschr√§nkung auf die aktuell unterst√ľtzten Elemente
  • modernes Frontend, welches √ľbersichtlich und leicht zu bedienen ist

Auch hier waren etliche Herrausforderungen zu meistern:

  • Bereitstellung einer modernen Web-Applikations-Infrastructure mittels NPM und Vue.js
  • Ausnutzung der extremen Vielfalt von Magento2 REST-Endpunkten (siehe auch https://devdocs.magento.com/redoc/2.3/)
  • Filterung der Endpunkte bzgl. der Website-Einschr√§nkung
  • Deployment einer Vue.js Anwendung als Composer-Modul und ausspielen √ľber Magento2 in unterschiedlichen Subpfaden

Magento2 adminhtml mal X

Vieles kann √ľber Konfigurationen abgedeckt werden, doch sp√§testens zum Erstellen neuer Seiten f√ľr die H√§ndlerin oder den H√§ndler wird es schwierig. Hier musste eine M√∂glichkeit geschaffen werden, damit sich eine jede Berechtigte und ein jeder Berechtigter auf allen Magento2-Server einloggen kann.

Eine SSO (Single-Sign-On) L√∂sung musste her, welche automatisch berechtigte Benutzerinnen und Benutzer √ľber einen weiteren Server authentifiziert und authorisiert, in Magento2 eine Benutzerin oder einen Benutzer mit der entsprechenden Rollenberechtigung anlegt und schlussendlich alle Magento2 Authentifizierungen setzt.

Noch gibt es viel zu tun

Innerhalb k√ľrzester Zeit ist es uns gelungen durch ausgezeichnete Teamarbeit und perfekte Aufteilung der anstehenden Aufgaben diesen Aprilscherz Wirklichkeit werden zu lassen und somit dem regionalen Handel eine M√∂glichkeit zu bieten, in dieser Krise und danach f√ľr ihre Kundinnen und Kunden Waren im Zeitalter des Internets √ľber einen Onlineshop anbieten zu k√∂nnen.

Alles Gute hat einmal ein Ende

mein-handel.at war ein Projekt der WKO√Ė f√ľr den Zeitraum der Corona-Pandemie und nun ist mit heute (5. Mai 2021) offiziell zu Ende gegangen. Alle bestehenden Shops sind nat√ľrlich weiter erreichbar, bis diese nicht mehr ben√∂tigt werden.