Shopware 6: Theme-Entwicklung

Shopware templating theming

Im vorherigen Artikel “Shopware 6: Erster Überblick und Installation” haben wir uns einen Überblick über Shopware 6 verschafft und gelernt, wie wir es lokal installieren. Nun möchten wir Schritt für Schritt zeigen, wie man am besten bei der Theme-Entwicklung vorgeht.

Wir gehen dabei von grundlegenden Kenntnissen mit der Kommandozeile, Sass/SCSS, Twig und Shopware 5 aus.

Theme erstellen

Beginnen wir damit, ein Theme anzulegen. Dafür gibt es ein praktisches Konsolen-Kommando:

bin/console theme:create

Man wird aufgefordert, einen Namen zu vergeben. Theme- bzw. Plugin-Namen werden in CamelCase geschrieben und mit einem Vendor-Prefix versehen, also zum Beispiel “DmfExampleTheme”, wobei “Dmf” das Prefix ist.

Das Skript legt daraufhin im Ordner /custom/plugins eine Verzeichnisstruktur für das neue Theme an. An dieser Stelle möchten wir auf den vorherigen Blogpost verweisen, in dem wir genauer auf die Verzeichnisstruktur eingehen: Shopware 6: Erster Überblick und Installation

Nachdem wir unser Theme erstellt haben, müssen wir es noch installieren und aktivieren. Das geht entweder über das Backend im Browser oder aber in der Konsole. Als Entwickler behandeln wir hier aber nur das Vorgehen in der Konsole.

Dazu führen wir den allgemeinen Befehl zum installieren und aktivieren von Plugins aus:

bin/console plugin:install --activate DmfExampleTheme

Nun müssen wir das Theme noch einem Sales Channel zuweisen, damit es im Frontend sichtbar ist:

bin/console theme:change

Das Skript fragt zunächst nach dem Sales Channel, dem das Theme zugewiesen werden soll. Wir wählen den Sales Channel “Storefront” indem wir die davor stehende Nummer eintippen (oder ihn mit den Pfeiltasten auswählen) und mit Enter bestätigen.

Danach werden wir nach dem Theme gefragt, das wir zuweisen möchten. Auch hier wählen wir wieder über die davor stehende Nummer (oder die Pfeiltasten) unser neu erstelltes Theme aus.

Danach löschen wir noch den Cache:

bin/console cache:clear

Nun können wir unser Frontend im Browser aufrufen. Das neu erstellte Theme ist nun aktiv, allerdings sehen wir keinen Unterschied zum Storefront-Theme. Das liegt daran, dass ein neues Theme standardmäßig alle Templates und Assets vom Storefront-Theme erbt und damit zunächst identisch ist.

Konfigurationen mit theme.json

Die grundlegende Konfiguration des Themes erfolgt über die Datei theme.json im src/Resources-Ordner des eigenen Themes.

Diese Konfigurationsmöglichkeiten zeigen wir am besten anhand eines Beispiels:

{
  "name": "DmfExampleTheme", //Name des Themes
  "author": "digital.manufaktur GmbH", //Autor
  "views": [
    "@Storefront",
    "@Plugins",
    "@DmfParentTheme", //Ggf. Parent Theme einstellen (Wichtig: NACH @Plugins!)
    "@DmfExampleTheme"
  ],
  "style": [
    "@Storefront", //Anweisung, alle Stylesheets vom Theme "Storefront" zu erben (kann auch weggelassen werden, dann keine Vererbung)
    "@DmfParentTheme", //Anweisung von DmfParentTheme zu erben (optional)
    "app/storefront/src/scss/base.scss" //Eigener Stylesheet Entrypoint
  ],
  "script": [
    "@Storefront", //Anweisung, alle Scripts vom Theme "Storefront" zu erben (kann auch weggelassen werden, dann keine Vererbung)
    "@DmfParentTheme", //Anweisung von DmfParentTheme zu erben (optional)
    "app/storefront/dist/storefront/js/main.js" //Eigener JavaScript Entrypoint (Achtung: all.js ist eine kompilierte JavaScript-Datei!)
  ],
  "asset": [
    "app/storefront/src/assets" //Ordner, wo Medien lagern
  ],
  "config": { //Theme-Konfiguration für das Backend
    "blocks": { //Abschnitte
      "themeColors": { //Identifier dieses Blocks
        "label": {
          "en-GB": "Theme colours", //Englisches Label
          "de-DE": "Theme-Farben" //Deutsches Label
        }
      }
    },
    "fields": { //Einzelne Felder
      "sw-color-brand-primary": { //Variablenname
        "label": {
          "en-GB": "Primary colour", //Englisches Label
          "de-DE": "Primärfarbe" //Deutsches Label
        },
        "type": "color", //color, fontFamily oder media
        "value": "#399", //Wert
        "editable": true, //Sichtbar im Backend
        "block": "themeColors", //Zu welchem Block gehört dieses Feld?
        "order": 100 //Reihenfolge
      },
    }
  }
}
            

Tipp: Standard-Variablen aus dem Storefront-Theme können in der eigenen theme.json einfach überschrieben werden, um eigene Default-Werte zu definieren.

Mehr Informationen zur theme.json findet man in der offiziellen Dokumentation.

Theme kompilieren

Es gibt mehrere Möglichkeiten, ein Theme zu kompilieren. Die einfachste ist der Befehl

./psh.phar storefront:build

Dieser Befehl kompiliert einmalig das Theme, sodass wir die Änderungen sehen, sobald wir die Seite im Browser neu laden. Das kann allerdings, je nach Hardware und Komplexität des Themes, schon mal ein paar Minuten dauern. Da man das natürlich nicht bei jeder Änderung am Theme erneut ausführen möchte, gibt es den sogenannten “Hot Mode”, der das Konzept des Hot Module Replacements von Webpack nutzt. Dabei werden Änderungen in Stylesheets und JavaScripts direkt im Browser aktiv, ohne die Seite neu laden und den Kompilierungs-Prozess von vorne starten zu müssen.

Diesen Modus startet man folgendermaßen:

./psh.phar storefront:hot-proxy

Nach einem kurzen Moment sollte sich automatisch ein Browserfenster mit der Adresse https://localhost:9998 öffnen. Hier ist nun der Hot Mode aktiv. Nehmen wir nun Änderungen an den Quell-SCSS-Stylesheets vor, so können wir diese direkt im Browser sehen. Das erleichtert und beschleunigt die Frontend-Entwicklung enorm.

Mehr zum PHP Shell Helper (PSH), der eine vielzahl an Konsolen-Kommandos bereitstellt, findet man in den Shopware Docs oder auf GitHub.

Arbeiten mit Stylesheets

Im Ordner /custom/plugins/DmfExampleTheme/src/Resources/app/storefront/src/scss finden wir eine Datei namens base.scss. Dies ist der Einstiegspunkt für all unsere eigenen Stylesheets. Zu Demonstrationszwecken können wir in diese Datei eine einfache SCSS-Regel schreiben, z.B.:

body { color: red; }

Nun müssen wir die Datei speichern und unser Theme kompilieren (siehe oben). Wenn wir den Hot Mode nutzen, passiert das automatisch.

Wir sehen nun die veränderte Schriftfarbe im Frontend. Jetzt wissen wir im Grunde alles, was wir brauchen, um “echte” Regeln zu erstellen. Natürlich schreibt man nicht alle Regeln in diese eine Datei, sondern arbeitet mit Imports, analog zum Storefront-Theme.

Wir haben festgestellt, dass das am einfachsten geht, wenn man den gesamten Inhalt der base.scss des Storefront-Themes kopiert, in der eigenen base.scss einfügt und alle Imports mithilfe der IDE bzw. des Editors auskommentiert (suche nach @import und ersetze durch //@import). So haben wir stets im Blick, welche Dateien (hier sog. Partials) sich wo zu befinden haben und welche wir bereits bearbeitet haben.

Möchten wir nun ein Element anpassen (z.B. die Schriftfarbe der Produkt-Boxen), so suchen wir mit den Entwickler-Tools des Browsers im Frontend die entsprechende(n) CSS-Klasse(n) heraus, die das Element am besten für unsere Zwecke identifizieren und suchen sie im Storefront-Theme:

Wir haben nun herausgefunden, dass sich der gesuchte Selektor in der Datei /vendor/shopware/platform/src/Storefront/Resources/app/storefront/src/scss/component/_product-box.scss befindet. Wir legen in unserem Theme also eine Datei namens /custom/plugins/DmfExampleTheme/src/Resources/app/storefront/src/scss/component/_product-box.scss an. Dort schreiben wir nun unsere eigenen Regeln für die Produkt-Boxen hinein, z.B.:

.product-box { color: red; }

Jetzt müssen wir den Import dieser Datei in unserer base.scss wieder aktivieren. Dazu suchen wir dort diese Zeile:

//@import 'component/product-box';

und entfernen die vorhin erstellte Kommentierung.

Als nächstes speichern wir die beiden Dateien und kompilieren das Theme wie oben beschrieben. Nachdem wir die Seite im Browser aktualisiert haben, sollte die Schrift in den Produktboxen nun rot sein. Nach diesem Prinzip können wir nun sämtliche Elemente beliebig anpassen.

Möchten wir komplett neue Regeln anlegen, so überlegen wir zunächst, wo unsere neuen Regeln gemäß des 7:1-Patterns am ehesten hineinpassen. Dort legen wir dann eine neue Datei an und ergänzen einen entsprechenden @import-Befeh in der base.scss.

Achtung: Da Shopware 6 nur im Hot Mode einen LibSass-Compiler verwendet, ansonsten aber eine unabhängige Sass-Implementierung (scssphp) nutzt, kann nur der SCSS- nicht aber der einrückungsbasierte Sass-Syntax verwendet werden, da nur der SCSS-Syntax von diesem Compiler unterstützt wird. Auch Imports von Dateien oder Libraries mit Sass-Syntax sind möglich.

Theme-Variablen definieren

In Shopware 6 ist es einfach, eigene Variablen zu definieren, die über das Backend gepflegt werden können. Dazu werfen wir erneut einen Blick in die oben bereits genannte Datei:

/custom/plugins/ThemeName/src/Resources/theme.json

Wir legen folgendermaßen eine Beispiel-Variable an:

"config": {
   "fields": {
     "meine-variable": {
       "label": {
         "en-GB": "Beispiel",
         "de-DE": "Example"
       },
       "type": "color",
       "value": "#f00"
     }
  }
}
            

Wir können nun in unseren Stylesheets auf die neue Variable zugreifen:

body { color: $meine-variable; }

Bei der Kompilierung des Themes wird, neben der direkten Injektion in den Compiler, eine Datei /var/theme-variables.scss generiert, in der alle Variablen aus dem Backend abgespeichert werden. Das ist zum Beispiel nützlich, wenn man die Backend-Variablen eigenen/externen Tools auslesen möchte.

Hinweis: Aktuell ist es beim Überschreiben von Storefront-Standardvariablen wichtig, ein label und einen type anzugeben. Aufgrund eines derzeit noch vorhandenen Bugs lässt sich das Theme ansonsten nicht aktivieren. In Zukunft soll es aber ausreichen, nur die auch wirklich benötigten Felder anzugeben.

Variablen aus dem übergeordneten Theme anpassen

Nicht alle Variablen im Theme sind durch den Benutzer im Backend verwaltbar. Das Storefront-Theme bringt eine vielzahl an SCSS-Variablen mit. Oft kommt es vor, dass man diese Variablen mit eigenen Werten überschreiben möchte.

Dabei helfen uns Default Values. Variablen aus dem Storefront sind in der Regel als !default deklariert, sodass wir die Variablen in unseren eigenen Stylesheets einfach erneut deklarieren und sie damit überschreiben.

Dabei sollte die Verzeichnis- und Dateistruktur aus dem Storefront-Theme übernommen werden. Die Variablen befinden sich dort im Verzeichnis

/vendor/shopware/platform/src/Storefront/Resources/app/storefront/src/scss/abstract/variables

Möchte man eine Variable überschreiben, so sucht man sie in diesem Verzeichnis, legt eine Datei mit gleichem Namen und Pfad im eigenen Theme an und deklariert die gewünschte Variable dort erneut, um sie zu überschreiben. Dabei muss natürlich darauf geachtet werden, dass entsprechende Imports dieser Datei vorhanden sind. Dabei kann man sich wieder am Storefront-Theme orientieren.

Arbeiten mit JavaScript

Nach der Erstellung eines neuen Themes findet man eine leere JavaScript-Datei namens /custom/plugins/[ThemeName]/src/Resources/app/storefront/src/main.js. Dies ist der Einstiegspunkt für ein eigenes JavaScript. In dieser Datei kann ECMAScript verwendet werden. Bei der Theme-Kompilierung wird diese Datei mithilfe von Webpack mit den Storefront-JavaScript-Dateien gebundled.

Bei Shopware 6 wurden alle JavaScript-Komponenten als ES-Module umgesetzt. Dabei verzichtet Shopware 6 auf die Verwendung von jQuery. Lediglich für Bootstrap-Komponenten, die die jQuery benötigen, wird die Library noch mitgeliefert. Ansonsten ist alles JavaScript von Shopware reines Vanilla JavaScript, was die Entwicklung vereinfacht und Bloat verringert.

Generell kann man sagen, dass JavaScript im Frontend von Shopware 6 eine kleinere Rolle spielt als noch bei Shopware 5. Beispielsweise wird der Checkout und die Erlebniswelten nun ausschließlich mit PHP abgewickelt. Fast alles, was man im Frontend im Bezug auf JavaScript findet, stammt von Bootstrap. Shopware 6 selbst bringt nur noch relativ wenig eigenes JavaScript mit.

Arbeiten mit Twig-Templates

Twig ist Smarty, der in Shopware 5 verwendeten Template Engine, sehr ähnlich, daher sollte der Umstieg recht leicht fallen. Die meisten Unterschiede sind von syntaktischer und vokabularer Natur. Wir empfehlen einen Blick in die offizielle Twig-Dokumentation für einen ersten Überblick.

Die Template-Dateien befinden sich im Verzeichnis /custom/plugins/DmfAbeleTheme/src/Resources/views/storefront. Dieses existiert nach der Erstellung eines neuen Themes nicht, muss also manuell angelegt werden.

Das Vorgehen beim Arbeiten mit Twig-Templates ist dabei dem bei Stylesheets sehr ähnlich. Wir suchen im Browser mit den Entwickler-Tools ein Element heraus, das wir bearbeiten möchten und suchen dieses dann innerhalb der Storefront-Templates, die sich in /vendor/shopware/platform/src/Storefront/Resources/views/storefront befinden.

Haben wir die passende Datei ausfindig gemacht, so erstellen wir eine gleichnamige Datei mit gleichem Pfad innerhalb von unserem Theme. In dieser Datei können wir nun beliebig Anpassungen vornehmen. Die Datei wird, im Gegensatz zu Stylesheets, automatisch anhand des Dateinamens von Shopware geladen. So können wir vorhandene Templates überschreiben und verändern.

Blöcke, Extends und Includes

Bei Twig gibt es, wie bei Smarty, das Konzept der Blöcke. Ein Block wird über einen eindeutigen Namen identifiziert und dient der Template-Vererbung. Alles, was sich in Blöcken befindet, kann von uns modifiziert werden.

Haben wir eine Template-Datei in unserem Theme angelegt, mit der wir eine bereits vorhandene erweitern oder überschreiben wollen, so setzen wir an den Anfang der Datei den folgenden Tag:

{% sw_extends '@Storefront/storefront/base.html.twig' %}

Der Pfad muss dabei immer der der originalen Template-Datei sein. Dieses Tag sagt Twig, dass es sämtliche Blöcke in dieser Datei für uns verfügbar machen soll. Das sw_extends-Tag ist dabei nur ein Wrapper für das Twig-Tag extends. Es erweitert die originale Funktionalität um das Auflösen der @-Platzhalter im Pfad, die für den Pfad zu einem bestimmten Theme bzw. Plugin stehen.

Um einen Block zu überschreiben, definieren wir ihn in unserer Datei einfach erneut, z.B.:

{% block base_header %} <h1>Das hier ist jetzt der neue Header!</h1> {% endblock %}

Damit haben wir nun den Header ersetzt. Möchten wir einen Block erweitern, so können wir innerhalb unseres Blocks den Inhalt des originalen Blocks folgendermaßen laden:

{% block base_header %} { { parent() } } <h1>Das hier ist steht unter dem originalen Header!</h1> {% endblock %}

Möchten wir eine andere Template-Datei einfach nur in unsere einfügen, so können wir das folgendermaßen tun:

{% sw_include '@DmfExampleTheme/storefront/example.html.twig' %}

Wie auch bei sw_extends ist sw_include nur ein Wrapper für include, der @-Platzhalter auflöst.

Wir verweisen an dieser Stelle nochmals auf die offizielle Dokumentation von Twig, um sich mit den grundlegenden Konzepten und der Syntax vertraut zu machen. Darüber hinaus dient das Storefront-Theme als praktisches Beispiel, an dem man sich gut orientieren kann. Wir empfehlen also, einen Blick dort hinein zu werfen.

Eigene Icons mit Shopware 6 und Twig verwenden

Zu guter Letzt möchten wir noch auf ein Problem eingehen, auf das wir bei unseren bisherigen Arbeiten mit Shopware 6 gestoßen sind. Die Rede ist von der Verwendung von eigenen Icons.

Shopware 6 liefert in seinem Storefront-Theme eine Reihe an Icons mit, die man mithilfe des praktischen Tags twig {% sw_icon 'icon-name' %} ganz leicht in seinen Templates einbauen kann. Es kommt jedoch oft vor, dass man die Icon-Sammlung durch eigene Icons ergänzen oder ersetzen möchte, was zum Verfassungszeitpunkt dieses Artikels (noch) nicht möglich ist.

Vor dem Weiterlesen empfehlen wir, sich zu informieren, ob für dieses Problem inzwischen von Shopware 6 eine Lösung angeboten wurde, da diese dem folgenden Ansatz vorzuziehen wäre.

Um eigene Icons zu laden, haben wir eine kleine Erweiterung für das Template geschrieben, das für die Einbindung der Icons verantwortlich ist. Der folgende Code muss in die Datei /custom/plugins/[ThemeName]/src/Resources/views/storefront/utilities/icon.html.twig geschrieben werden:

Dieser Code erweitert den Block so, dass zunächst geprüft wird, ob im Verzeichnis /custom/plugins/[ThemeName]/src/Resources/app/storefront/src/assets/icon ein Icon mit dem gegebenen Namen liegt. Ist das der Fall, wird dieses verwendet. Ist kein solches Icon vorhanden, wird im normalen Storefront-Verzeichnis nach dem Icon gesucht. So kann man im oben genannten Verzeichnis eigene Icons ablegen, die dann mithilfe von sw_icon eingebunden werden können.

Cheat Sheet für Konsolen-Kommandos

Hier haben wir nochmal die wichtigsten Konsolen-Befehle für die Theme-Entwicklung zusammengestellt:

bin/console theme:create
bin/console theme:change
bin/console cache:clear oder ./psh.phar cache
bin/console plugin:install PluginName
bin/console plugin:activate PluginName
bin/console plugin:uninstall PluginName
bin/console plugin:deactivate PluginName
./psh.phar storefront:build
./psh.phar storefront:hot-proxy
            

Fazit

In diesem Artikel haben wir alle nötigen Werkzeuge an die Hand bekommen, mit denen wir unsere eigenen Shopware 6 Themes erstellen können. Wir haben gelernt, wie wir Themes erstellen und kompilieren, den Hot Mode nutzen und haben uns die Erstellung und Erweiterung von SCSS-Stylesheets, JavaScript und Twig-Templates angeschaut. Außerdem haben wir eine Möglichkeit gezeigt, eigene Icons in Templates zu verwenden.