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.
Backend-Plugins mit Extjs
0 KommentareInhaltsverzeichnis
- 1 Was ist Extjs?
- 2 Welche Version verwendet Shopware?
- 3 Wo finde ich weitere Informationen & Tutorials rund um Extjs?
- 4 Tutorial: Unser erstes Extjs-Plugin
- 5 Infrastruktur schaffen
- 6 Basisgerüst umsetzen
- 7 Integration Grid
- 8 Integration Treemenü
- 9 Laden und Darstellen der Kategorien im Tree
- 10 Reagieren auf Events. Klick auf Node soll Alert erzeugen.
- 11 Verknüpfen von Komponenten
- 12 Laden der Grid-Daten
- 13 Laden der Grid-Daten Part 2
Was ist Extjs?
Extjs ist ein clientseitiges JavaScript- bzw. Ajax-Framework für interaktive Webanwendungen, das für Open-Source-Projekte unter der GPL, für andere Projekte unter kommerziellen Lizenzen erhältlich ist. In erster Linie bietet Ext JS eine umfangreiche Sammlung von Steuerelementen. (Quelle Wikipedia: http://de.wikipedia.org/wiki/Extjs
Mit Hilfe von Extjs können Web-Anwendungen entwickelt werden, die sich in Punkto Bedienung und Look & Feel genau wie lokal installierte Software verhalten.
Das gesamte Shopware-Backend baut in vielen Bereichen auf Extjs auf - so basieren die aller meisten Backend-Module auf dieser Technologie.
Welche Version verwendet Shopware?
Aktuell verwenden wir Extjs in der Version 3.2 - im Februar 2011 ist die Veröffentlichung von ExtJs 4.0 geplant, welches eine ganze Reihe Verbesserungen mit sich bringen wird und die Basis für das in Shopware 4.0 geplante Backend Redesign sein wird.
Wo finde ich weitere Informationen & Tutorials rund um Extjs?
Auf der Herstellerseite unter www.sencha.com - können Sie auf eine Vielzahl von Tutorials und Demos rund um Extjs zurückgreifen!
Tutorial: Unser erstes Extjs-Plugin
Im Gegensatz zu den vorherigen Tutorials, in denen wir die Backend-Komponenten "Plain" und ohne Zuhilfenahme eines Frameworks realisiert haben, werden wir heute ein umfangreiches Backend-Modul aufbauen, welches aus fertigen Extjs Komponenten besteht und sich somit von der Usability an den bestehenden Shopware Modulen orientiert.
Welche Funktionen wollen wir abbilden?
- Das Backend-Modul soll im wesentlichen aus 2 Komponenten bestehen
- Links soll es eine Kategorie-Auswahl in Form eines Baummenüs geben, wie man es aus diversen anderen Shopware Modulen kennt
- Rechts soll eine Grid-Darstellung integriert werden
- In dem Grid sollen aktive Banner / Einkaufswelten / Liveshopping-Artikel und Bundle-Artikel dargestellt werden
- So soll man auf einen Blick, die in den jeweiligen Kategorien aktiven Marketing-Elemente einsehen können
Infrastruktur schaffen
Wie bereits in den vorherigen Tutorials beschrieben, legen wir zunächst die Grund-Struktur unseres Plugins an.
Dazu erstellen wir den neuen Ordner engine/Shopware/Plugins/Local/Backend/Extjs
In diesem Ordner legen wir eine neue Datei Bootstrap.php mit folgendem Inhalt an:
<?php class Shopware_Plugins_Backend_Extjs_Bootstrap extends Shopware_Components_Plugin_Bootstrap { public function install() { $event = $this->createEvent( 'Enlight_Controller_Dispatcher_ControllerPath_Backend_Extjs', 'onGetControllerPathBackend' ); $this->subscribeEvent($event); $parent = $this->Menu()->findOneBy('label', 'Marketing'); $item = $this->createMenuItem(array( 'label' => 'Übersicht', 'onclick' => 'openAction(\'Extjs\');', 'class' => 'ico2 layout', 'active' => 1, 'parent' => $parent, 'style' => 'background-position: 5px 5px;' )); $this->Menu()->addItem($item); $this->Menu()->save(); return true; } public static function onGetControllerPathBackend(Enlight_Event_EventArgs $args) { return dirname(__FILE__).'/Extjs.php'; } } ?>
Anschließend erstellen wir im gleichen Verzeichnis eine neue Datei mit der Bezeichnung Extjs.php
Diese Datei erhält zunächst folgenden Inhalt:
<?php class Shopware_Controllers_Backend_Extjs extends Enlight_Controller_Action { public function init(){ $this->View()->addTemplateDir(dirname(__FILE__)."/templates/"); } public function indexAction(){ $this->View()->loadTemplate("index.tpl"); } public function skeletonAction(){ $this->View()->loadTemplate("skeleton.tpl"); } }
Anschließend müssen wir noch 2 Template-Dateien anlegen, die wir gleich im nächsten Step benötigen.
Legen Sie die Datei /templates/index.tpl im gleichen Verzeichnis an.
{extends file="backend/index/parent.tpl"} {block name="backend_index_body_inline"} Hallo Welt {/block}
Legen Sie die Datei /templates/skeleton.tpl im gleichen Verzeichnis an.
{ "init": { "title": "{s name='WindowTitle' force}Marketing Übersicht{/s}", "width": 900, "height": 650, "id": "coupon", "minwidth": 800, "minheight": 650, "content": "", "loader": "action", "url": "{url action='index'|escape:'javascript'}", "help": "http://wiki.shopware.de/Hilfe:Artikel#Cross-Selling" } }
Nun installieren wir das Plugin kurz über den Plugin-Manager, führen im Backend einen Reload durch und stellen sicher, dass sich das Plugin über Marketing > Übersicht aufrufen lässt!
Dort sollten wir die Ausgabe "Hallo Welt" erhalten, da wir bislang noch keine weiterführende Funktionalität hinterlegt haben.
Alle vorbereitenden Aufgaben sind nun abgeschlossen und wir können uns voll und ganz auf die eigentliche Programmierung konzentrieren.
Basisgerüst umsetzen
Zunächst einmal müssen wir überlegen, wie der Grundaufbau unseres Modul sein soll. In den meisten Fällen besteht ein Shopware Backend Modul aus 2 oder mehr Komponenten, die an definierten Stellen im View positioniert werden sollen.
In unserem Fall planen wir 2 Bereiche:
- Links soll es ein Treemenü zur Auswahl der Kategorie geben
- Rechts soll ein Grid integriert werden, in dem wir alle zugeordneten Elemente sehen können
Eine gute Anlaufstelle, um das für den jeweiligen Anwendungszweck geeignete Layout zu finden, ist die Demo-Seite von Extjs. http://dev.sencha.com/deploy/dev/examples/layout/complex.html
In unserem Fall (und auch bei den meisten Standard-Modulen von Shopware) kommt am ehesten ein Layout vom Typ Border in Frage - bei diesem Layout-Typ haben alle Komponenten eine fest definierte Position. Mögliche Positionen sind east,west,north,south und center - letztere muss zwingend definiert werden, das heißt, eine Komponente muss der "center" Region zugeordnet werden.
Der Basis-Code für die neue Komponente sieht wie folgt aus:
{extends file="backend/index/parent.tpl"} {block name="backend_index_body_inline"} <script> Ext.ns('Shopware.Extjs'); (function(){ View = Ext.extend(Ext.Viewport, { layout: 'border', initComponent: function() { View.superclass.initComponent.call(this); } }); Shopware.Extjs.View = View; })(); Ext.onReady(function(){ OurView = new Shopware.Extjs.View; }); </script> {/block}
(Einzufügen in die index.tpl)
Um die Einbindung von ExtJS müssen wir uns nicht kümmern, da Shopware diese automatisch vornimmt.
Zunächst definieren wir per Ext.ns einen Namespace - jedes Backend-Plugin erhält so einen eigenen Gültigkeitsbereich. Das ist sinnvoll, um eine klare Funktionstrennung in den verschiedenen Komponenten zu erhalten.
Anschließend erzeugen wir ein neues Objekt vom Typ "Ext.Viewport" - diesem Objekt weisen wir das Layout "Border" zu, außerdem legen wir einen Funktionsrumpf für initComponent an, in diesen Rumpf integrieren wir später die zusätzlichen Komponenten, die wir benötigen werden.
Im onReady Event des Ext-Objekts erzeugen wir dann eine Instanz unseres Viewports - dieses führt dazu, dass der View automatisch angezeigt wird.
Der Code kann so natürlich noch nicht funktionieren, da wir noch keine Komponente eingebunden haben, die der Center-Region zugeordnet ist - dieses ist für die Funktionsfähigkeit des Viewports jedoch unerlässlich. Das Grundgerüst können wir aber in dieser Form für beliebige Backend-Plugins verwenden.
Weiter geht es nun mit der Einbindung der Komponenten:
Integration Grid
Legen Sie die neue Datei grid.tpl mit folgendem Inhalt an:
<script type="text/javascript"> Ext.ns('Shopware.Extjs'); (function(){ {block name="Shopware_Plugins_Extjs_Grid"} var Grid = Ext.extend(Ext.grid.GridPanel, { title: 'Angelegte Aktionen', // Bezeichnung stripeRows:true, // Zeilen unterschiedlich einfärben region: 'center', // Positionsbestimmung initComponent: function() { /* Datenquelle definieren */ this.store = new Ext.data.Store({ url: '{url action=getList}', autoLoad: true, reader: new Ext.data.JsonReader({ root: 'data', totalProperty: 'count', id: 'id', fields: [ 'valid_from','valid_to','typ','description','mode' ] }) }); /* Spalten definieren */ this.columns = [ { xtype: 'gridcolumn', dataIndex: 'valid_from', header: 'Start', sortable: false, width: 150 }, { xtype: 'gridcolumn', dataIndex: 'valid_to', header: 'Ende', sortable: false, width: 150 }, { xtype: 'gridcolumn', dataIndex: 'typ', header: 'Typ', sortable: false, width: 150 }, { xtype: 'gridcolumn', header: 'Bezeichnung', dataIndex: 'description', sortable: false, width: 150 } ]; Grid.superclass.initComponent.call(this); } }); {/block} Shopware.Extjs.Grid = Grid; })(); </script>
Fügen Sie in der index.tpl, oberhalb von
View.superclass.initComponent.call(this);
folgenden Code ein:
this.grid = new Shopware.Extjs.Grid; this.items = [this.grid];
Fügen Sie unterhalb von:
{block name="backend_index_body_inline"}
folgenden Code ein:
{include file="grid.tpl"}
Wenn Sie das Modul nun aufrufen, sehen Sie bereits unsere Grid-Komponente.
Natürlich können zu diesem Zeitpunkt noch keine Daten dargestellt werden, hierzu müssen wir später noch die korrespondieren Controller-Methoden entwickeln.
Erklärung:
- Mit include laden wir den Inhalt der Datei grid.tpl
- In Grid.tpl
- Zunächst erzeugen wir ein neues Objekt, welches sich von Ext.grid.GridPanel ableitet
- In der initComponent-Methode des Objekts, erstellen wir einen Store, mit dem wir später auf Daten zugreifen können
- Außerdem definieren wir die Spalten des Grids
- Unser neues Objekt erhält abschließend den Namen Shopware.Extjs.Grid
- In der Index.tpl erzeugen wir eine neue Instanz unseres Grids und weisen diese Instanz den Items des Viewports zu - um die Darstellung kümmert sich Extjs automatisch.
Integration Treemenü
Legen Sie die neue Datei tree.tpl mit folgendem Inhalt an:
<script type="text/javascript"> Ext.ns('Shopware.Extjs'); (function(){ {block name="Shopware_Plugins_Extjs_Tree"} var Tree = Ext.extend(Ext.tree.TreePanel, { title: 'Kategorien', // Bezeichnung region: 'west', // Positionsbestimmung width: 225, // Breite initComponent: function() { this.loader = new Ext.tree.TreeLoader({ dataUrl:'{url action=getCategories}'}); this.rootVisible = false; this.root = new Ext.tree.AsyncTreeNode({ text: 'Test', draggable:true, id:'1' }); Tree.superclass.initComponent.call(this); } }); {/block} Shopware.Extjs.Tree = Tree; })(); </script>
Bearbeiten Sie die Datei index.tpl wie folgt:
Fügen Sie unterhalb von:
{include file="grid.tpl"}
die Zeile:
{include file="tree.tpl"}
ein.
Fügen Sie unterhalb von:
this.grid = new Shopware.Extjs.Grid;
die Zeile:
this.tree = new Shopware.Extjs.Tree;
ein.
Ändern Sie die Zeile:
this.items = [this.grid];
in:
this.items = [this.tree,this.grid];
Wenn Sie das Plugin nun erneut aufrufen, sehen Sie im linken Bereich unser noch leeres Tree-Menü
Das Layout des Plugins ist also komplett, nun müssen wir uns nur noch um die Anzeige der Daten kümmern und die Komponenten miteinander verknüpfen.
Laden und Darstellen der Kategorien im Tree
Mit der Zeile
this.loader = new Ext.tree.TreeLoader({ dataUrl:'{url action=getCategories}'});
im Tree-Menü, haben wir definiert, dass wir die Daten des Menüs aus der Controller-Action getCategories laden möchten.
Hierzu wechseln wir wieder in den Controller Extjs.php
Fügen Sie dort die folgende Methode hinzu:
public function getCategoriesAction(){ $this->View()->setTemplate(); $node = $this->request()->node; if (empty($node)) $node = 1; $nodes = array(); $sql = " SELECT c.id, c.description, c.position, c.parent, COUNT(c2.id) as count, ( SELECT categoryID FROM s_articles_categories WHERE categoryID = c.id LIMIT 1 ) as article_count FROM s_categories c LEFT JOIN s_categories c2 ON c2.parent=c.id WHERE c.parent=? GROUP BY c.id ORDER BY c.position, c.description "; $getCategories = Shopware()->Db()->fetchAll($sql,array($node)); if (!empty($getCategories) && count($getCategories)){ foreach ($getCategories as $category){ $category["description"] = utf8_encode(html_entity_decode($category["description"])); if (!empty($category["count"])){ $nodes[] = array('text'=>$category["description"], 'id'=>$category["id"], 'parentId'=>$category["parent"], 'cls'=>'folder'); }elseif(!empty($_REQUEST["move"])&&empty($category["article_count"])) { $nodes[] = array('text'=>$category["description"], 'id'=>$category["id"], 'parentId'=>$category["parent"], 'cls'=>'folder'); }else{ $nodes[] = array('text'=>$category["description"], 'id'=>$category["id"], 'parentId'=>$category["parent"], 'cls'=>'folder', 'leaf'=>true); } } } echo json_encode($nodes); }
Speichern Sie die Datei und öffnen Sie das Plugin erneut. Nun werden im Tree-Menü automatisch die Shop-Kategorien angezeigt. Die Kategorien werden hierbei asynchron über Ajax nachgeladen.
Beschreibung:
- $this->View()->setTemplate(); - Diese Action soll kein Template laden, sondern direkt Json formatierte Daten zurückgeben
- $node = $this->request()->node; - Die Variable "node" wird automatich vom Tree-Menü erzeugt und liefert die ID der aktuellen Kategorie, diese benötigen wir, um die passenden Unterkategorien zu laden
- $nodes = array(); - alle Unterkategorien werden dem Array $nodes zugewiesen
- echo json_encode($nodes); - Das Array nodes wird Json kodiert an den Browser geschickt
- Das Tree-Menü dekodiert den Json-String und erzeugt automatisch die dazugehörigen Nodes im Tree
Reagieren auf Events. Klick auf Node soll Alert erzeugen.
Nun sollen im Grid ja die Aktionen der jeweiligen Kategorie angezeigt werden, wir müssen also beide Komponenten miteinander verknüpfen beziehungsweise auf das Anklicken eines Nodes reagieren. Dazu stellt jede Extjs-Komponente eine Vielzahl von Events bereit, in denen man die gewünschte Funktionalität integrieren kann.
In unserem Fall wollen wir auf das Event vom Typ click reagieren.
Fügen Sie hierzu folgenden Code in der Datei tree.tpl oberhalb von
Tree.superclass.initComponent.call(this);
ein:
this.on('click', function(e){ alert(e.attributes.id); },this);
Es gibt verschiedene Methoden um einen Event-Listener zu registrieren, zum Beispiel stellt jede Extjs-Komponente die Methode addEvent bereit. Eine Liste der möglichen Events je Komponente finden Sie in der Extjs Dokumentation.
In diesem Fall reagieren wir auf das Anklicken eines Nodes. Dann wird jeweils eine Funktion aufgerufen. In dieser Funktion geben wir die ID des markierten Nodes aus. Über e.attributes können wir auf alle Werte zugreifen, die wir im Controller dem Node-Array übergeben haben.
Sie können die Datei nun speichern und die Funktionalität sicherstellen, im nächsten Schritt implementieren wir den endgültigen Event-Listener, der auf Basis der Kategorie-Id den Store des Grids neu lädt.
Verknüpfen von Komponenten
Wechseln Sie in die Datei index.tpl
Ersetzen Sie:
this.tree = new Shopware.Extjs.Tree;
durch:
this.tree = new Shopware.Extjs.Tree({ grid:this.grid});
Wechseln Sie in die Datei tree.tpl
Ersetzen Sie:
alert(e.attributes.id);
durch:
console.log(this.grid);
Rufen Sie das Plugin im Backend auf:
Beschreibung:
Jeder Klick auf einen Node des Tree-Menüs gibt nun das Objekt this.grid in der FireBug Konsole aus. Wir haben also eine Möglichkeit geschaffen, aus dem Tree-Menü heraus, auf unsere Grid-Komponente zuzugreifen.
Realisiert haben wir dies, in dem wir das Grid, im Objekt-Konstruktor mit dem Tree-Menü verknüpft haben ({ grid:this.grid})
Nun haben wir technisch alle Informationen zusammen, um das Grid dynamisch, abhängig von der gewählten Kategorie, neu zu laden.
Laden der Grid-Daten
Öffnen Sie die Datei Extjs.php
Fügen Sie die Methode getListAction hinzu:
public function getListAction(){ $this->View()->setTemplate(); }
Öffnen Sie die Datei tree.tpl
Ersetzen Sie console.log(this.grid); durch:
this.grid.store.baseParams["id"] = e.attributes.id; this.grid.store.load();
Beschreibung:
- Zunächst haben wir den Funktionsrumpf für die Controller-Methode implementiert, die später für die Rückgabe der Grid-Daten benötigt wird.
- Dann haben wir den Click-Event modifiziert
- Das Store Objekt des Grids kümmert sich um das Laden der Daten
- Diesem Objekt weisen wir den Parameter id zu - also der ID der aktuell ausgewählten Kategorie
- Anschließend rufen wir die Load-Methode des Objekts auf - dieses lädt die im Store (grid.tpl) definierte URL und übergibt als Post-Parameter unsere Kategorie-ID.
Laden der Grid-Daten Part 2
Nun müssen wir nur noch die Methode getListAction vollständig implementieren, um die Daten zurückzugeben, die wir im Grid darstellen wollen.
Fügen Sie unterhalb von:
$this->View()->setTemplate();
ein:
$id = $this->request()->id; $getElements = Shopware()->Db()->fetchAll(" SELECT `description`, `valid_from`,`valid_to`,'' As mode,'Banner' AS typ FROM s_emarketing_banners WHERE categoryID = ? UNION SELECT `description`, `valid_from`,`valid_to`,`mode`,'Einkaufswelt' AS typ FROM s_emarketing_promotions WHERE category = ? UNION SELECT `description`, `start` AS `valid_from`,`end` AS `valid_to`,'' As mode,'Aktion' AS typ FROM s_emarketing_promotion_main WHERE parentID = ? ",array($id,$id,$id)); // Banner echo json_encode(array("count"=>count($getElements),"data"=>$getElements));
Erklärung:
- Zunächst greifen wir auf die per Post übermittelte ID der Kategorie zurück
- Dann führen wir eine kombinierte SQL-Abfrage über die Tabellen s_emarketing_banners, s_emarketing_promotions und s_emarketing_promotion_main aus
- Aus allen Tabellen, laden wir die Datensätze, die zur aktuellen Kategorie passen
- Aus dem Ergebnis erzeugen wir einen Json-String, den wir zurück an den Browser schicken
Um die Anzeige der Daten kümmert sich Extjs nun vollkommen automatisch.
Im Store des Grids haben wir dazu die Felder definiert, die der Request zurückgeben kann:
fields: [ 'valid_from','valid_to','typ','description','mode' ]
Anschließend haben wir für jede Spalte des Grids den dazugehörigen Data-Index definiert, somit können die Daten, die der Request zurückgibt, automatisch in die korrespondierenden Spalten eingetragen werden.
Nun müssen wir noch den Einkaufswelt-Typ ausgeben, hierzu können wir auf die Row-Renderer Funktionalität des Grids zurückgreifen.
Ändern Sie:
{ xtype: 'gridcolumn', dataIndex: 'typ', header: 'Typ', sortable: false, width: 150 },
in:
{ xtype: 'gridcolumn', dataIndex: 'typ', header: 'Typ', sortable: false, width: 150, renderer: function (value, p, record){ if(record.data.mode){ value = value + "( "+record.data.mode+" )"; } return value; } },
Beschreibung:
Über den Renderer-Event können wir eine Funktion definieren, die bei jeder Zeile im Grid automatisch ausgeführt wird. Darüber können wir die Daten manipulieren, die in den jeweiligen Spalten angezeigt werden sollen. In diesem Fall fügen wir hinter die Typ-Bezeichnung noch den Einkaufswelt-Modus hinzu.
So sieht das fertige Modul aus:
Wir erhalten also per Klick auf eine Kategorie, eine vollständige Liste, aller dort angelegten Marketing-Instrumente.
Fertig!
Artikel-PDF erstellen
Artikel bewerten
Kommentare:
Artikel kommentieren
Weitere interessante Artikel:
Bestell-Nr.: SW1487
Lieferzeit ca. 5 Tage
Preise inkl. gesetzlicher
MwSt. zzgl. Versandkosten*
Preise inkl. gesetzlicher
MwSt. + Versandkosten*
Kategorien:


