Der Bereich Shopware Labs ist die Plattform für alle Entwickler. Hier findet man technische Dokumentationen und zahlreiche Tipps und Tricks rund um das Thema Programmieren. In dieser Rubrik stellen außerdem die Entwickler der shopware AG neue und experimentelle Lösungsansätze vor. Neue Funktionen, die in dieser Rubrik bereitgestellt werden, sind teilweise auch für zukünftige Releases geplant. Die Funktionen können dann ohne Programmierkenntnisse zukünftig direkt im Shopware Backend konfiguriert werden oder werden über Plugins bereitgestellt. Informationen über neue, geplante Funktionen finden Sie in unserer Roadmap.
Bitte beachten Sie, dass die hier bereitgestellten Lösungsansätze nicht offiziell supportet werden und nur eingebaut werden sollten, sofern Sie über das entsprechende, technische Wissen verfügen.
Tutorial: Plugins mit Shopware
0 KommentareInhaltsverzeichnis
Vorwort
In diesem Tutorial entwickeln wir exemplarisch ein Plugin, mit dem die Textbausteine des Frontends automatisch in eine Datei exportiert werden. Außerdem sollen Änderungen an dieser Datei, automatisch zurück in die Textbaustein-Datenbank von Shopware geschrieben werden.
Das Grundgerüst anlegen
Erstellen Sie den Ordner "SnippetTextFile" im Verzeichnis engine\Shopware\Plugins\Community\Backend Legen Sie dort eine leere Datei "Bootstrap.php" an.
Erklärung:
Jedes Plugin verfügt über ein eigenes Verzeichnis im Dateisystem. Zur Unterteilung der Plugins, gibt es die 3 Hauptorder Community, Local, Default.
- Community = Hier werden Plugins abgelegt, die über den Plugin-Manager installiert werden, also z.B. aus dem bald verfügbaren CommunityStore kommen.
- Local = Hier können lokale Anpassungen abgelegt werden, also z.B. Individualprogrammierung
- Default = Dieser Ordner ist für Plugins der shopware AG reserviert, also alle Funktionen die standardmäßig in das Produkt integriert sind
In unserem Fall verwenden wir den Ordner Community, da sich das Plugin später einfach über den Plugin-Manager installieren lassen soll.
Diese 3 Hauptordner unterteilen sich weiter in die Ordner
- Frontend
- Backend
- Core
Wo das Plugin abgelegt wird, ist letztlich nicht relevant. Diese zusätzliche Unterteilung dient einfach der Übersicht. Wenn Sie also ein Plugin schreiben, welches implizit im Frontend aktiv ist, sollte dieses entsprechend in den Frontend-Ordner.
Wenn Sie so wie wir, ein Plugin schreiben welches keine sichtbaren Änderungen im Frontend durchführt, sollte es in den Ordner Backend oder Core.
Die Datei "Bootstrap.php" ist der Dreh- und Angelpunkt eines jeden Shopware-Plugins, hier werden verschiedene Dinge durchgeführt:
- Installationsmethode um automatisch Konfigurationsanweisungen & Menüpunkte im Backend anzulegen oder um SQL-Dateien zu importieren
- Registrieren der Funktionen des Plugins / Events / Hooks
- Ggf. Deinstallationsmethode
In unserem Beispiel sieht der Rumpf des Plugins wie folgt aus:
<?php class Shopware_Plugins_Backend_SnippetTextFile_Bootstrap extends Shopware_Components_Plugin_Bootstrap { public function install() { $event = $this->createEvent( 'Enlight_Controller_Action_PostDispatch', 'onPostDispatch' ); $this->subscribeEvent($event); return true; } static function onPostDispatch(Enlight_Event_EventArgs $args) { // Noch leer } }
Zunächst einmal leitet sich jedes Plugin von der Klasse "Shopware_Components_Plugin_Bootstrap" ab, in dieser Hauptklasse werden die grundsätzlichen Methoden & Eigenschaften des Plugins defininiert. Das soll uns an dieser Stelle aber erst einmal nicht weiter interessieren!
Wichtig ist die Bezeichnung der Klasse unseres Plugins, diese muss dem Speicherort und Namen des Plugins entsprechen.
Shopware_Plugins_Backend_SnippetTextFile_Bootstrap
In unserem Fall haben wir das Plugin im Ordner Backend\SnippetTextFile abgelegt, also muss dieser Pfad auch in den Namen der Klasse übernommen werden.
Jedes Plugin muss mindestens die Methode "install" implementieren, in der Install-Methode wird global definiert, was das Plugin überhaupt macht - welche Events & Funktionen also beeinflusst werden sollen.
In diesem Fall verknüpfen wir den Event "Enlight_Controller_Action_PostDispatch" mit der Methode onPostDispatch in unserem Plugin.
Controller_Action_PostDispatch wird nach dem Ausführen jedes Controllers in Shopware aufgerufen, für unsere Zwecke also der optimale Ansatzpunkt. Schließlich soll das Plugin nicht nur auf der Startseite oder der Detailseite aktiv werden, sondern bei jedem Aufruf des Frontends!
Mit dem nachfolgenden Code wird diese Event-Zuordnung dauerhaft in der Datenbank (Tabelle s_core_subscribes) geschrieben.
$this->subscribeEvents($event);
Um die Entfernung des Events bei Deinstallation kümmert sich Shopware automatisch. Sie müssen die "deinstall" Methode also nur implementieren, wenn Sie z.B. eigene Datenbank-Tabellen für Ihr Plugin benötigen!
Das Plugin im Backend registrieren
Da wir in diesem Fall keine weiteren Events beeinflussen möchten, reicht das Grundgerüst für die Installation des Plugins im Backend aus! Die Verzeichnisstruktur und der Code der Bootstrap.php sollten nun wie auf dem nachfolgenden Screenshot aussehen:
Öffnen Sie Ihr Shopware Backend und wechseln Sie über Einstellungen > Plugins in den Plugin-Manager!
Klicken Sie hier Links auf Backend \ Community. In der Übersicht sollten Sie nun das Plugin SnippetTextFile sehen, klicken Sie dort auf das Hinzufügen Symbol und bestätigen Sie die Installation des Plugins!
Im nachfolgenden Fenster die Optionbox "Aktiv" auf Ja setzen und Speichern!
Herzlichen Glückwunsch, Sie haben so eben Ihr erstes Shopware Plugin installiert!
Das Plugin mit Leben füllen
Um zu überprüfen, ob das Plugin korrekt registriert worden ist, können Sie nun testweise ein die("Korrekt installiert"); in die Methode onPostDispatch des Plugins schreiben.
static function onPostDispatch(Enlight_Event_EventArgs $args) { die("Korrekt installiert"); }
Wenn Sie nun das Frontend aufrufen, sehen Sie statt Ihres Shops die oben definierte Ausgabe. Perfekt. Das Plugin wurde korrekt installiert, also entfernen wir die Ausgabe und machen uns nun an die Arbeit.
Zunächst einmal wollen wir verhindern, dass das Plugin auch im Backend ausgeführt wird, schließlich soll es nur die Textbausteine des Frontends exportieren. Hierzu bauen wir folgende Abfrage in unsere onPostDispatch Methode ein.
// Zugriff auf das Request-Objekt $request = $args->getSubject()->Request(); // Zugriff auf das Response-Objekt $response = $args->getSubject()->Response(); if(!$request->isDispatched()||$response->isException()||$request->getModuleName()!='frontend'){ // Der Request muss vollständig abgearbeitet sein // Es darf keine Exception aufgetreten sein // Das Plugin soll nur im Frontend aktv sein, andernfalls return; }
Die Ausführung der Methode wird also nur fortgesetzt, wenn keine Weiterleitung auf einen anderen Controller stattgefunden hat, wenn wir uns im Frontend befinden und wenn nicht andersweitig irgendein Fehler aufgetreten ist, den Shopware anzeigen müsste.
Da man in Shopware die Textbausteine je Shop und Sprache anlegen kann, müssen wir zunächst ermitteln, in welchem Shop und in welcher Sprache wir aktuell im Frontend unterwegs sind.
Dazu integrieren wir die nachfolgenden Zeilen in den Code.
$shop = Shopware()->Shop()->getId(); // Id des Shops aus s_core_multilanguage $locale = Shopware()->Locale()->getId(); // Id der Sprache aus s_core_locales
Damit haben wir die eindeutige ID des Shops und die eindeutige ID der Sprache, dieses benötigen wir um gleich die passenden Textbausteine auszulesen.
Wir sollten sicherheitshalber noch eine Überprüfung integrieren, ob diese Werte zur Verfügung stehen. Dazu verwenden wir folgenden Code:
if (empty($shop) || empty($locale)){ // Beispiel Fehlerbehandlung throw new Enlight_Exception("Plugin-Fehler! Shop oder Locale-Id leer!"); }
Wenn also einer der beiden Werte nicht ausgelesen werden kann, erzeugen wir uns eine Fehlermeldung im Frontend. Dann müssen wir nicht lange suchen, wenn an dieser Stelle ein Fehler auftritt.
Als nächstes überlegen wir uns, wo wir die Textdatei mit den Bausteinen im Dateisystem ablegen möchten. Anbieten würde sich der Ordner /uploads/, dieser ist in jeder Standard-Installation vorhanden und besitzt die passenden Schreibrechte.
In den Dateinamen übernehmen wir die shopID und die localeID, so erhalten wir für jede Frontend-Sprache und jeden Subshop eine eigene Datei:
// Pfad zum Speicherort unseres Textbaustein-Exports $path = Shopware()->DocPath()."uploads/snippets_".$shop."_".$locale.".php";
Nun müssen wir diese Datei erzeugen und die Textbausteine exportieren. Zunächst einmal integrieren wir aber eine Überprüfung, ob die Datei bereits vorhanden ist.
if (!is_file($path)){ // Datei erstellen } else { // Datei lesen }
Zum Auslesen der Textbausteine aus der Datenbank, implementieren wir eine neue Methode in der Klasse.
public function loadSnippetsFromDatabase($shop,$locale){ // Snippets aus Datenbank auslesen und einen Hash aus dem Namen und dem Namespace bilden $data = Shopware()->Db()->fetchAll(" SELECT CONCAT(namespace,'#',name) AS hash, namespace,shopID,localeID,name,value FROM s_core_snippets WHERE shopID = ? AND localeID = ? ",array($shop,$locale)); foreach ($data as $row){ $snippets[$row["hash"]] = $row; } return $snippets; }
Diese Methode erwartet die beiden Parameter $shop und $locale und liest die dazugehörigen Textbausteine aus der Datenbank-Tabelle s_core_snippets aus.
Dazu verwenden wir das global bereitstehende Shopware()->Db() Objekt, dieses basiert auf Zend_Db_Table und dem PDO-Adapter.
fetchAll liefert alle Datensätze einer Abfrage zurück, wohingegen fetchRow z.B. nur eine einzelne Zeile liefert. In der Dokumtentation zu Zend_Db_Table finden Sie alle Methoden, die dieses Objekt bereitstellt.
Nachdem wir die Datensätze ausgelesen haben, erzeugen wir ein neues Array ($snippets), wo wir als Key eine Kombination aus dem Namespace und dem Namen des Textbausteins erhalten. Dadurch ist jeder Baustein im Array eindeutig über den Key zu identifizieren. Das ist wichtig, damit wir lokale Änderungen in der Textdatei später einfacher feststellen können.
Fügen Sie nun oben in der onPostDispatch Methode unterhalb von
if (!is_file($path)){ // Datei erstellen
folgenden Code ein:
$getSnippets = Enlight_Class::Instance(__CLASS__)->loadSnippetsFromDatabase($shop, $locale); if (!empty($getSnippets)){ $config = new Zend_Config($getSnippets); // Snippets in ein Textformat umwandeln $writer = new Zend_Config_Writer_Array(array("config" => $config ,"filename" => $path)); $writer->write(); }
Dieser Code lädt das Ergebnis der gerade integrierten Methode loadSnippetsFromDatabase in die Variable $getSnippets. Anschließend erzeugen wir ein Zend_Config Objekt, welchem wir als Parameter das Array mit unseren Textbausteinen übergeben. Dieses Objekt wird mit dem Zend_Config_Writer_Array dann in eine lokale Textdatei gespeichert!
Zeit für einen Zwischenstop.
Wenn Sie den Code korrekt übernommen haben und nun das Frontend aufrufen, sollten Sie im Ordner /uploads/ die Datei "snippets_1_1.php" finden.
Diese beeinhaltet nun alle Textbausteine des deutschsprachigen Hauptshops!
Nun müssen wir noch einen Weg finden, Änderungen an dieser Datei zurück in die Shopware Datenbank zu schreiben. Ziel soll es ja sein, dass Sie Ihre Textbausteine direkt in der Datei bearbeiten können, ohne in das Backend zu müssen.
Zu diesem Zweck fügen Sie folgenden Code im verbleibenden, leeren else Block ein. Unterhalb von "Datei lesen"
}else { // Datei lesen }
$getLocalSnippets = include($path); $getDatabaseSnippets = Enlight_Class::Instance(__CLASS__)->loadSnippetsFromDatabase($shop,$locale); Enlight_Class::Instance(__CLASS__)->searchModifiedItems($getLocalSnippets,$getDatabaseSnippets,$shop,$locale);
Wenn die Datei also vorhanden ist, was nach unserem ersten ersten Test gerade der Fall sein sollte, wird diese per include der Variable $getLocalSnippets zugeordnet. In dieser befindet sich also nach der Zuordnung ein Array, welches wir für die nachfolgenden Vergleiche perfekt verwenden können. Anschließend laden wir erneut die Bausteine aus der Datenbank, schließlich wollen wir ja als nächstes einen Vergleich beider Quellen durchführen. Zum Schluss wird die Methode searchModifiedItems aufgerufen, die wir nun als nächstes implementieren.
Fügen Sie Ihrer Bootstrap-Klasse folgenden Code hinzu:
public function searchModifiedItems($file,$database,$shop,$locale){ $changed = false; // Loop durch lokale Textdatei foreach ($file as $key => $snippet){ if (empty($database[$key])){ // Eintrag existiert nicht, in Datenbank anlegen $insert = Shopware()->Db()->query(" INSERT INTO s_core_snippets (namespace,shopID,localeID,name,value) VALUES ( ?,?,?,?,? ) ",array($snippet["namespace"],$shop,$locale,$snippet["name"],$snippet["value"])); $changed = true; }elseif (str_replace(array("\r","\n"),"",$snippet["value"]) != str_replace(array("\r","\n"),"",$database[$key]["value"])){ // Eintrag wurde verändert, in Datenbank aktualisieren $update = Shopware()->Db()->query(" UPDATE s_core_snippets SET value = ? WHERE namespace = ? AND name = ? AND shopID = ? AND localeID = ? ",array($snippet["value"],$snippet["namespace"],$snippet["name"],$shop,$locale)); $changed = true; } } // Wenn Änderungen durchgeführt wurden, Cache leeren if ($changed == true){ Shopware()->Template()->utility->clearCompiledTemplate(); // Kompilierte Templates löschen Shopware()->Cache()->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('Shopware_Config')); // Konfiguration leeren } }
Was macht diese Methode?
Zunächst einmal erzeugen wir eine Schleife, mit der wir durch das Array gehen, welches wir vorher aus der lokalen Textdatei gelesen haben. Bei jedem Eintrag überprüfen wir, ob es diesen Eintrag auch in dem Array gibt, welches wir aus den Textbausteinen der Datenbank erzeugt haben. Falls nein, wurde in der lokalen Datei ein neuer Baustein eingefügt, den wir in die Datenbank übernehmen müssen.
Als nächstes prüfen wir, ob sich der Wert eines Bausteins verändert hat. Ist das der Fall, wurde die Datei angepasst und wir müssen den geänderten Wert in die Datenbank schreiben.
Wenn einer der beiden Fälle aufgetreten ist, setzen wir die Variable $changed auf true.
Ganz am Ende überprüfen wir, ob eine Änderung stattgefunden hat.
if ($changed == TRUE){ Shopware()->Template()->utility->clearCompiledTemplate(); // Kompilierte Templates löschen Shopware()->Cache()->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('Shopware_Config')); // Konfiguration leeren }
Wenn dies der Fall ist, löschen wir den Shopware Template Cache und den Konfigurationscache, so dass unsere Anpassungen sofort übernommen werden und nicht erst nach Ablauf der Caching-Zeit.
Das Plugin ist nun im Prinzip fertig. Sie können jetzt probieren, lokale Änderungen in der Textdatei vorzunehmen und überprüfen ob diese korrekt in das Frontend übernommen werden.
Im Anhang zu diesem Tutorial können Sie das Plugin als Zip-File herunterladen, so dass Sie es direkt über den Plugin-Manager installieren können.
Hier noch einmal der komplette Quellcode unseres Plugins:
<?php class Shopware_Plugins_Backend_SnippetTextFile_Bootstrap extends Shopware_Components_Plugin_Bootstrap { public function install() { $event = $this->createEvent( 'Enlight_Controller_Action_PostDispatch', 'onPostDispatch' ); $this->subscribeEvent($event); return true; } static function onPostDispatch(Enlight_Event_EventArgs $args) { // Zugriff auf das Request-Objekt $request = $args->getSubject()->Request(); // Zugriff auf das Response-Objekt $response = $args->getSubject()->Response(); // Zugriff auf das View / Template - Objekt (Wird in diesem Plugin nicht benötigt) $view = $args->getSubject()->View(); // Zugriff auf das Zend-Config Objekt if(!$request->isDispatched()||$response->isException()||$request->getModuleName()!='frontend'){ // Der Request muss vollständig abgearbeitet sein // Es darf keine Exception aufgetreten sein // Das Plugin soll nur im Frontend aktv sein, andernfalls return; } // Aktueller Shop & Sprache $shop = Shopware()->Shop()->getId(); // Id des Shops aus s_core_multilanguage $locale = Shopware()->Locale()->getId(); // Id der Sprache aus s_core_locales if (empty($shop) || empty($locale)){ // Beispiel Fehlerbehandlung throw new Enlight_Exception("Plugin-Fehler! Shop oder Locale-Id leer!"); } // Pfad zum Speicherort unseres Textbaustein-Exports $path = Shopware()->DocPath()."uploads/snippets_".$shop."_".$locale.".php"; if (!is_file($path)){ // Datei noch nicht vorhanden, Bausteine dieser Sprache auslesen $getSnippets = Enlight_Class::Instance(__CLASS__)->loadSnippetsFromDatabase($shop,$locale); if (!empty($getSnippets)){ $config = new Zend_Config($getSnippets); // Snippets in ein Textformat umwandeln $writer = new Zend_Config_Writer_Array(array("config"=>$config,"filename"=>$path)); $writer->write(); } }else { $getLocalSnippets = include($path); $getDatabaseSnippets = Enlight_Class::Instance(__CLASS__)->loadSnippetsFromDatabase($shop,$locale); Enlight_Class::Instance(__CLASS__)->searchModifiedItems($getLocalSnippets,$getDatabaseSnippets,$shop,$locale); } } public function searchModifiedItems($file,$database,$shop,$locale){ $changed = false; // Loop durch lokale Textdatei foreach ($file as $key => $snippet){ if (empty($database[$key])){ // Eintrag existiert nicht, in Datenbank anlegen $insert = Shopware()->Db()->query(" INSERT INTO s_core_snippets (namespace,shopID,localeID,name,value) VALUES ( ?,?,?,?,? ) ",array($snippet["namespace"],$shop,$locale,$snippet["name"],$snippet["value"])); $changed = true; }elseif (str_replace(array("\r","\n"),"",$snippet["value"]) != str_replace(array("\r","\n"),"",$database[$key]["value"])){ // Eintrag wurde verändert, in Datenbank aktualisieren $update = Shopware()->Db()->query(" UPDATE s_core_snippets SET value = ? WHERE namespace = ? AND name = ? AND shopID = ? AND localeID = ? ",array($snippet["value"],$snippet["namespace"],$snippet["name"],$shop,$locale)); $changed = true; } } // Wenn Änderungen durchgeführt wurden, Cache leeren if ($changed == true){ Shopware()->Template()->utility->clearCompiledTemplate(); // Kompilierte Templates löschen Shopware()->Cache()->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('Shopware_Config')); // Konfiguration leeren } } public function loadSnippetsFromDatabase($shop,$locale){ // Snippets aus Datenbank auslesen und einen Hash aus dem Namen und dem Namespace bilden $data = Shopware()->Db()->fetchAll(" SELECT CONCAT(namespace,'#',name) AS hash, namespace,shopID,localeID,name,value FROM s_core_snippets WHERE shopID = ? AND localeID = ? ",array($shop,$locale)); foreach ($data as $row){ $snippets[$row["hash"]] = $row; } return $snippets; } }
Artikel-PDF erstellen
Artikel bewerten
Kommentare:
Artikel kommentieren
Weitere interessante Artikel:
Bestell-Nr.: SW1404
Lieferzeit ca. 5 Tage
Preise inkl. gesetzlicher
MwSt. zzgl. Versandkosten*
Preise inkl. gesetzlicher
MwSt. + Versandkosten*
Kategorien:


