Du möchtest mehr über die Basics der Shopware-Pluginentwicklung erfahren? Dann bist du hier genau richtig!
Willkommen zum zweiten Teil unseres Shopware 5.2 Plugin-Tutorials. Im letzten Teil haben wir ein Konzept aufgestellt und das Grundgerüst aufgebaut. Falls ihr diesen verpasst habt, solltet ihr ihn euch unbedingt hier durchlesen, weil wir im folgenden darauf aufbauen.
Lesezeit: ~18 Minuten
Am Ende stelle ich den aktuellen Stand des Plugins völlig kostenlos zur Verfügung. Ihr könnt es euch aber jetzt schon herunterladen und den Code beim Lesen des Blog-Artikels nachvollziehen.
Um den größten Lerneffekt zu erzielen, solltet ihr den Beitrag erst vollständig durchlesen und verstehen nebenbei könnt ihr euer eigenes Plugin erstellen. Danach schnappt ihr euch den Code und geht die einzelnen Funktionen nochmal durch, um zu schauen, ob ihr auch wirklich alles verstanden habt.
Wenn ihr letztes Mal gut aufgepasst habt, wisst ihr ganz genau, wo wir letztes Mal aufgehört haben und was euch heute erwartet! Als Erstes zeige ich euch, wie ihr über euer Plugin einen Cronjob erstellt. Danach richten wir für diesen einen Subscriber an. Abschließend werden wir unseren ersten Service einbauen und verwenden.
Da sich alles so spannend anhört und ihr es bestimmt genauso wenig erwarten könnt wie ich, fangen wir direkt mit dem ersten Punkt an:
Wir beginnen mit dem Cronjob. Falls du noch nicht weißt, was ein Cronjob ist bzw. wie sie genau funktionieren, kannst du dir diesen Blog-Beitrag durchlesen.
Mit dem neuen Plugin-System von Shopware 5.2 gibt es die Möglichkeit die Einstellungen des Cronjobs in der Datei cronjob.xml zu hinterlegen. Diese wird dann beim Installieren automatisch zu einem richtigen Cronjob verarbeitet.
Da es bis Shopware 5.3.5 allerdings einen Bug gibt, bei dem das Enddatum über diesen Weg nicht richtig gesetzt wird und wir den Cronjob daher im Backend nochmal anpassen müssten, zeige ich euch vorher noch einen anderen Weg, bei dem wir den Cronjob über die Datenbank hinzufügen.
Kleine Erinnerung: Die Basefile eines Plugins ist die Hauptdatei und trägt den selben Namen wie der Plugin-Ordner - in unserem Fall die EmzRenameImages.php
Dabei nehmen wir die Basefile aus dem ersten Teil und erweitern sie um eine uninstall() und eine install() Methode. Diese Methoden werden aufgerufen, wenn das Plugin de- oder installiert wird.
In der Datenbank des Shops werden alle Daten gespeichert. Unter anderem finden wir dort auch alle Cronjobs und ihre Konfiguration. Über die oben genannten Methoden können wir den Cronjob also bei der Installation über einen sogenannten Query in die Datenbank einfügen. Ein Query ist im Grunde ein Befehl, der auf Datenbank-Ebene ausgeführt wird, um z. B. einen Eintrag in ihr zu speichern.
Der Code dazu sieht folgendermaßen aus:
Zunächst sagen wir oben, dass wir die InstallContext und die UninstallContext-Klassen benutzen möchten. Das ist wichtig, weil wir sie bei den Parametern der un- und install-Methoden angeben.
Wir beginnen in der install() Methode, indem wir uns in der $connection-Variable über den sogenannten Service-Container einen Service holen. Ein Service ist im Grunde nur eine Ansammlung von verschiedenen Funktionen. In unserem Fall holen wir uns den DBAL-Service, mit dem wir Daten aus der Datenbank holen oder sie dort einfügen.
Shopware hat neben dem DBAL-Service eine ganze Menge anderer Services. Zum Beispiel gibt es einen Media-Service, mit dem man unter anderem Bilder löschen kann.
Der DBAL-Service hat eine insert() Funktion. Diese wird benutzt, um einen neuen Datenbank-Eintrag hinzuzufügen. Dabei geben wir verschiedene Parameter an, welche die Funktion in einen Query umwandelt und an die Datenbank schickt. In unserem Code fügen wir einen Eintrag in die s_crontab Tabelle (hier sind alle Cronjobs gespeichert) mit den vorliegenden Daten ein.
Für den Namen habe ich "Bilder umbenennen" gewählt. Die Aktion des Cronjobs ist "Shopware_CronJob_EmzRenameImages" und wird später nochmal relevant sein. Als Intervall habe ich hierbei 600 Sekunden bzw. 10 Minuten gewählt.
In der uninstall() Methode führe ich gegensätzlich einen Query aus, der den Datenbank-Eintrag der 's_crontab'-Tabelle löscht, welcher als Aktion "Shopware_CronJob_EmzRenameImages" hinterlegt hat. Da das nur auf unseren Cronjob zutrifft, wird dieser gelöscht.
Nun sollte beim Installieren der Cronjob hinzugefügt werden. Wenn ihr euer Plugin schon installiert habt, könnt ihr es über den Plugin-Manager im Backend einfach neu installieren.
Wenn ihr mit einer Version ab 5.3.5 arbeitet, könnt ihr auch den einfacheren Weg nehmen. Erstellt dazu den Ordner Resources und dort die Datei cronjob.xml. In diese könnt ihr folgenden Code einsetzen:
Diese Datei wird automatisch beim Installieren ausgelesen und zu einem Cronjob verarbeitet. Auch hier müsst ihr das Plugin einmal neu installieren.
Für den Cronjob haben wir als Aktion "Shopware_CronJob_EmzRenameImages" hinterlegt. Nun können wir einen sogenannten Subscriber anlegen. Ein Subscriber ist zunächst einmal nur eine weitere PHP-Datei. Die Besonderheit ist allerdings, dass ein Subscriber unseren Code zu bestimmten Ereignissen ausführen kann.
Für einen Subscriber müssen zwei Schritte umgesetzt werden:
Da wir gerade eben einen Cronjob erstellt haben, nutzten wir an dieser Stelle einen Subscriber, um eine Funktion zu definieren, welche beim Ausführen unseres Crons aufgerufen wird.
Für den Subscriber erstellen wir in unserem Plugin-Ordner den Ordner Subscriber. In diesem werden all unsere Subscriber-Dateien liegen. Dort erstellt ihr die Datei Cronjob.php. Der Name der Datei ist theoretisch egal, sollte aber thematisch zu dem Event passen. In dieser Datei befindet sich folgender Code:
Um ein Subscriber zu sein, muss die Klasse von dem SubscriberInterface erben. Außerdem ist es wichtig, dass wir eine getSubscribedEvents() Methode definieren. In dieser geben wir ein Array zurück, in dem wir Events und zugehörige Listener-Funktionen verbinden.
Ein Listener ist eine Funktion, die aufgerufen werden soll, wenn ein bestimmtes Event ausgelöst wird. Bei Cronjobs ist die Aktion auch gleichzeitig das Event. Wenn der Cronjob also ausgeführt wird, wird kurz danach die onCronjobExecute() Methode unseres Subscribers ausgeführt. In diese schreiben wir später unseren Code für das Umbenennen der Bilder.
Bis jetzt haben wir aber nur die Subscriber-Datei angelegt. Im nächsten Schritt müssen wir Shopware noch sagen, dass es sich hierbei um einen Subscriber handelt. Dafür erstellen wir im Resources-Ordner eine Datei mit dem Namen services.xml. In dieser registrieren wir alle Subscriber und Services, die wir in unserem Plugin verwenden möchten.
Folgenden Code packen wir in diese Datei, um den Cronjob-Subscriber zu registrieren:
Wichtig ist hierbei, dass wir die richtige Klasse angeben. Außerdem dürfen wir den "tag" nicht weglassen. Durch diesen kann Shopware später bei Installation die zuvor hinterlegte getSubscribedEvents() Methode unseres Subcribers aufrufen und die jeweiligen Events mit ihren Funktionen verbinden.
Nun müssen wir das Plugin wieder neu installieren. Dadurch wird die services.xml ausgelesen und verarbeitet. Das System weiß danach, dass in unserer Cronjob.php ein Subscriber liegt, der auf das Ausführen des Cronjobs wartet.
An dieser Stelle kommen wir und auf unser Konzept aus Teil 1 zurück. Ich habe mir überlegt, dass im Großen und Ganzen 3 Komponenten wichtig sind. In diesem Teil der Blog-Reiche kümmern wir uns erstmal nur um eine der drei.
Wir bräuchten eine Art Warteschlangen-System, welches uns eine bestimmte Anzahl an Artikel-Bildern liefert, die wir beim Ausführen des Cronjobs umbenennen können. Zusätzlich sollte man mit dieser Komponente auch die Möglichkeit haben, bereits umbenannte Bilder zu markieren, damit sie beim nächsten Durchlauf nicht wieder umbenannt werden.
Oben habt ihr bereits erfahren, dass es den DBAL-Service gibt, der sich um die Datenbank kümmert. Wir können aber auch selbst eigene Services definieren. Welche Funktionen in einem Service enthalten sind und wie dieser genau aussieht, entscheiden wir dabei selbst. Dabei ergibt es aber Sinn, nur Funktionen mit dem selben Thema in einem Service unterzubringen. In unseren Queue-Service werde ich also nur Funktionen einbauen, die sich um das Beschaffen und Markieren von Artikel-Bildern kümmert.
Damit der Service die richtigen Artikel holen kann, muss es eine Möglichkeit geben, bereits umbenannte Bilder zu markieren, damit sie nicht immer wieder umbenannt werden.
Jedes Artikelbild in Shopware hat einen zugehörigen Datensatz in der Datenbank. Dabei gibt es auch einen Identifikator (id genannt). Diese können wir nutzen und nach dem Umbenennen in eine eigene Datenbank-Tabelle eintragen. Beim Holen der nächsten Bilder kann man damit die bereits umbenannten Bilder aussortieren.
Aber wie erstellen wir nun eine Tabelle in der Datenbank? Am einfachsten wäre es natürlich, die Tabelle einfach manuell in der Datenbank zu erstellen. Aber wir wären natürlich keine Entwickler, wenn wir es einfach mögen ;)
Shopware arbeitet mit einem Framework namens Doctrine. Ein Vorteil von Doctrine ist, dass wir PHP-Klassen aufbauen können, in denen wir mit Variablen und Funktionen eine Art Konfiguration hinterlegen. Diese Klasse kann Doctrine später auslesen und zu einer Datenbank-Tabelle umwandeln.
Fun-Fact: diese PHP-Klassen werden "Models" genannt. Ab sofort dürft ihr also behaupten, eure Freitag-Abende mit Models zu verbringen.
Unsere Model-Dateien erstellen wir in dem Ordner Models. Hier fügen wir eine neue Datei mit dem Namen RenamedImage.php hinzu.
Ein Model besteht aus einer PHP-Klasse, mehreren Variablen und Funktionen. Auf die Funktionen werden wir erstmal nicht genauer eingehen, weil wir sie für dieses Plugin nicht brauchen.
In einem Model wird mit sogenannten Annotations gearbeitet. Eine Annotation ist im Grunde Text, der zwischen einem /** und */ steht. Er dient als Konfiguration und steht z. B. über einer Variable der Klasse.
Die erste Annotation steht über der Model-Klasse. Hier wird unter anderem der spätere Tabellenname - in unserem Fall emz_renamed_images - definiert. Die restlichen Annotations stehen über den Variablen. Sofern die Konfiguration in den Annotations stimmt, wird jede Variable später in eine eigene Spalte umgewandelt. In der Konfiguration werden wichtige Daten wie Spaltenname, Typ usw. hinterlegt. Alle Annotations und ihre Konfigurationen werden in der Dokumentation von Doctrine nochmal genauer erklärt.
Eine Besonderheit von Doctrine ist, dass man hier auch auf andere Models verweisen kann. Bei der $articleImage-Variable sage ich z. B. die Id des Image-Models, welches für die Tabelle der Artikelbilder steht, hinterlegt werden soll. Damit wird eine Verknüpfung zwischen unserem und dem Artikel-Bilder-Model von Shopware erstellt.
Wie das alles genau funktioniert würde genug Inhalt für einen weiteren Blogpost liefern. An dieser Stelle ist für euch wichtig zu wissen, dass das obige Model eine Tabelle mit zwei Spalten erstellt. Neben der gewöhnlichen id-Spalte gibt es noch die article_img_id-Spalte, in der wir auf die Tabelle der Artikelbilder verweisen. Damit können wir dann später ganz leicht herausfinden, welche Artikelbilder bereits umbenannt worden sind.
Die Model-Datei ist nun fertig, allerdings müssen wir Shopware noch irgendwie mitteilen, dass unser Model in eine Tabelle umgewandelt werden muss. Perfekt wäre es natürlich, wenn das am Anfang bei der Installation geschieht. Daher schauen wir uns die install()/uninstall() Methode nochmal an. Bevor ihr den Code einfügt, solltet ihr das Plugin erstmal deinstallieren. Den Grund erkläre ich gleich.
Hier haben wir in der jeweiligen Methode einen Block ergänzt, der das Model beim De- und Installieren hinzufügt oder löscht. Der Grund für das Deinstallieren war, dass der Code-Block der uninstall() Funktion versucht die Tabelle aus der Datenbank zu löschen. Wenn wir das Plugin neu installieren, findet er die Tabelle aber nicht, weil sie zu diesem Zeitpunkt noch nicht existiert. Daher wird eine Fehlermeldung ausgegeben.
Zur Lösung des Problems deinstallieren wir das Plugin vorher, fügen den neuen Code hinzu und installieren es wieder. Danach kann unsere Erweiterung allerdings auch ohne Probleme neu installiert werden, weil sich die Tabelle ab diesem Zeitpunkt in der Datenbank befindet.
Da wir nun unsere Tabelle haben, können wir uns an den Queue-Service setzen. Unsere Services werden wir in dem Ordner Components/Services ablegen.
Ich gehe dabei so vor, dass ich für jeden Service nochmal einen Unterordner mit dem Namen des Ordners anlege. In diesem Fall erstellen wir dort den Queue Ordner. Hier legen wir die Dateien Queue.php und QueueInterface.php ab. Letztere wird das Interface des Services sein. Ein Interface ist einfach gesagt eine Vorlage für unsere PHP-Klasse. Hier definieren wir kurz welche Funktionen wir in der Klasse benutzen werden und welche Parameter in diesen erlaubt sind.
Unser Interface ist mit zwei Funktionen recht simpel aufgebaut. Wie oben bereits gesprochen gibt es hier einmal eine Funktion, um an die nächsten 10 Bilder zu kommen und eine Funktion, um bereits umbenannte Bilder zu markieren.
Hier der Code für den gesamten Queue-Service:
Wir finden neben den beiden Funktionen, welche wir in unserem Interface definiert haben, auch noch weitere Funktionen. Diese sind aber keine Methoden, die später über den Service von außen aufgerufen werden soll und nur im Service selbst als Helfer benutzt wird. Daher ist die Funktion auch als private gekennzeichnet.
Wichtig ist für uns die getArticleImages() Methode, welche wir später in unserem Cronjob benutzen möchten. Diese Methode soll 10 Artikel-Bilder, welche bisher noch nicht umbenannt wurden, zurückgeben. Dabei bauen wir uns zunächst über die getArticleImagesQueryBuilder() Methode eine sogenannten QueryBuilder. Diesen kann man in Verbindung mit den Model Klassen aus Shopware ganz einfach Querys zu bauen. Falls euch dieses Thema interessiert, könnt ihr wieder in der Dokumentation von Doctrine mehr dazu lesen.
In diesem Beispiel sprechen wir das Image Model an, welche für die Artikelbilder zuständig ist. Über einen leftJoin() holen wir uns alle Einträge aus unserem RenamedImages Model, welche über die article_img_id zu einem Artikelbild zugeordnet werden können. Danach sagen wir in der where() Funktion, dass wir nur diejenigen Artikelbild-Einträge haben wollen, die keinen passenden "Partner" in unserem Model mit den markierten Artikel-Bildern haben. Dadurch stellen wir sicher, dass wir nur unmarkierte Bilder bekommen und kein Bild mehrfach umbenannt wird.
Die Bilder, welche uns dieser Query zurückgibt, geben wir wiederum das Ergebnis der Funktion zurück. Damit erhält derjenige, der die getArticleImages() Methode aufruft, die passenden 10 nächsten Bilder.
Die andere für uns interessante Funktion ist flagArticleImages(). Mit dieser Methode können wir Bilder nach dem Umbenennen markieren, damit sie beim nächsten Aufruf des Cronjobs nicht mehr bearbeitet werden.
Hierbei fragen wir zunächst ab, ob das Artikelbild bereits in unserer Tabelle vorhanden ist. Falls nicht, wird es hinzugefügt.
Damit Shopware weiß, dass es sich hierbei um einen Service handelt, müssen wir auch diesen in der services.xml registrieren. Dabei erweitern wir die Datei einfach folgendermaßen:
Auch dieses Mal ist wieder wichtig, dass der Name der Klasse stimmt. Den subscriber-tag lassen wir dieses Mal weg, weil es sich nicht um einen Subscriber handelt. Damit wäre unser Queue-Service auch schon fertig.
Um unseren Queue-Service im Cronjob-Subscriber benutzen zu können, müssen wir den Service einbinden. Das haben wir in unserem Queue-Service bereits mit dem ModelManager gemacht, allerdings bin ich noch nicht genau darauf eingegangen.
In der services.xml haben wir unserem Queue-Service eine id gegeben. Mit dieser können wir ihn in anderen Services/Subscribern verwenden. Dafür müssen wir in dem service-Eintrag des Subscribers ein "argument" mit der passenden Service-id hinzufügen.
Die services.xml sieht damit folgendermaßen aus:
Damit ist aber nur einer von zwei Schritten erledigt. In unserem Subscriber müssen wir die Klasse des Interfaces hinzufügen, hinterlegen noch die Variable und schreiben eine sogenannte __construct() Funktion. Diese wird jedes Mal aufgerufen, wenn eine neue Instanz unserer Plugin-Klasse erstellt wird. Den Parameter des Konstruktors haben wir in der services.xml definiert (unseren Queue-Service). In dem Konstruktor selbst weisen wir den Service einer Variable zu, um ihn später benutzen zu können.
In der Listener-Methode des Cronjobs können wir nun über die Service-Variable "$this->queueService" die Funktionen des Services aufrufen. Die geholten Artikel speichere ich in der Variable $articleImages;
Zum Testen des Plugins markiere ich die geholten Bilder direkt über die flagArticleImages(). Der aktuelle Stand unseres Cronjob-Subscribers sieht damit folgendermaßen aus:
Um das ganze zu testen, brauchen wir natürlich einige Demo-Daten. Falls ihr so wie ich eine frische Shopware-Installation verwendet, könnt ihr euch einfach das Demo-Plugin von Shopware aus dem Community-Store herunterladen. Sucht im Plugin-Manager dafür einfach nach "Demo".
Nun da wir Artikel im Shop haben, können wir unsere Erweiterung gleich ausprobieren. Stellt den Cronjob so ein, dass der nächste Ausführungstermin in der Vergangenheit liegt. Außerdem muss das "Cron"-Plugin von Shopware aktiviert sein. Danach ruft ihr die URL www.mein-shop.de/backend/cron auf, wobei ihr natürlich eure URL einsetzt.
Nun sollte der Cron ausgeführt werden. Da unser Code im Backend arbeitet, solltet ihr bisher nichts sehen (außer es wird eine Fehlermeldung ausgeworfen). Um zu kontrollieren, ob alles geklappt habt, gehen wir in die Datenbank. Hier sucht ihr nach der Tabelle emz_renamed_images. Falls ihr hier 10 Einträge findet, hat alles so funktioniert, wie es soll.
Wenn die Tabelle aber weiterhin leer ist, müsst ihr auf Fehlersuche gehen. Am Ende dieses Beitrages findet ihr den aktuellen Stand des Plugins zum Herunterladen. Ihr könnt es mit eurem vergleichen, um der Ursache des Problems auf die Spur zu kommen.
Das wars auch schon mit diesem Teil unserer Blog-Reihe. Im nächsten Teil erstellen wir einen weiteren Service, der für das eigentliche Umbenennen der Bilder verantwortlich ist. Ich hoffe, ihr seid nächstes Mal auch wieder am Start!
Den aktuellen Stand des Plugins findet ihr hier: Plugin-Download
Weitere Links:
Unsere Standorte
Zentrale
Technologiepark 23
33100 Paderborn
Leipzig
Bernhardstraße 34
04315 Leipzig
Kontakt
E-Mail: support@8mylez.com
Telefon: +49 (0) 5251 284 710
Shopware Dienstleistungen
Über 8mylez
✓ 38 Mitarbeiter
✓ Shopware Gold Partner
✓ 40.000+ Plugin Downloads
✓ 160+ betreute Shops
✓ Full-Service Shopware Agentur
✓ 70 Shopware Videos auf Youtube
✓ Alle Shopware Zertifizierungen
Social
florian
Florian
beim installieren des Plugins über den Plugin Manager gibt es einen Internal-Server-Error und die Shopware installation ist nicht mehr erreichbar (HTTP ERROR 500).
Benutze Shopware Version 5.6.7
was löst den Fehler aus?
Alexander Wolf
ein 500-er Error bedeutet wahrscheinlich, dass es einen PHP-Fehler in deinem Plugin gibt.
Du kannst in der config.php die config-tweaks aktivieren, um Dir die Fehlermeldung anzeigen zu lassen: https://developers.shopware.com/developers-guide/shopware-config/#example-development-config
Chris
Wir suchen eine Lösung wie man beim Installieren eine individuelle Meldung an den User in verschiedenen Sprachen ausgeben kann nach erfolgreicher installation oder beim deinstallieren (also z. B.: per EN Sprache im Backend eingeloggt ist statt DE Sprache). Habt Ihr da vielleicht einen Lösungsansatz für uns?
Daniel Wolf
es freut uns, dass dir unser Beitrag gefällt :)
Schreib uns am besten direkt an support@8mylez.com, damit wir dir bei deinem Anliegen helfen können.
LG
Daniel
Götz
Klasse, dass man euch mal über die Schulter schauen darf.
Kommt denn der 3. Teil noch?
Daniel Wolf
es freut mich sehr, dass dir der Beitrag gefällt. Aktuell haben wir alle Hände voll zu tun mit Shopware 6, weshalb wir diese Blogserie und damit auch den dritten Teil erstmal pausieren. Danke für dein Verständnis :)
LG
Daniel
Dieter
ich finde dieses Tutorial wirklich sehr informativ. Gut strukturiert und gut geschrieben. Führte bei mir zu einigen Aha-Erlebnissen. Ich freue mich auf die Fortführung des Kurses und eventuell weitere Kurse.
Danke, Dieter
Daniel Wolf
vielen Dank für die netten Worte :)
Weitere Tutorials sind bereits in Arbeit!
Liebe Grüße
Daniel
Dieter
Daniel Wolf
wir haben das Problem gerade eben behoben. Vielen Dank für deinen Hinweis! :)
Liebe Grüße
Daniel
Marc Baur
sollte gefixt sein :-)
LG
Marc
Robin
Daniel Wolf
es freut uns sehr, dass dir unsere Beiträge gefallen. Teil 3 ist bereits in Bearbeitung, wird aber wahrscheinlich noch ein paar Wochen brauchen :)
Viele Grüße
Daniel
Sebastian
Daniel Wolf
vielen Dank für den Hinweis. Wir haben die Code-Beispiele angepasst, sodass sie nun wieder sichtbar sind :)
Viele Grüße
Daniel
Was denkst du?