Im ersten Teil der Tipps zur Entwicklung mit Magento haben wir besprochen, mit welchem Setup und welcher Grundeinstellung man an die Entwicklung herangehen sollte. Bevor wir uns an konkrete Tipps machen können, müssen wir uns heute mit einer weiteren wichtigen Grundlage beschäftigen: der Aufteilung des Codes in der MVC-Architektur von Magento. Beißt durch und lest bis zum Ende, denn wer das Konzept versteht, findet sich im Magento-Code wesentlich einfacher zurecht. 😉
[toc levels=3 title=“Inhalt“]
5. Trennung von Logik, Daten und Darstellung verstehen und beachten (MVC-Architektur)
Magento ist nach dem MVC-Prinzip aufgebaut. MVC steht für Model, View und Controller – falls ihr noch nicht davon gehört habt, recherchiert dazu bitte im Web, denn dieses Architektur-Pattern ist ein wichtiger Grundstein von Magento. Einer von vielen vielen Einstiegslinks ist der entsprechende Wikipedia-Artikel.
Kerngedanke der MVC-Architektur ist, dass der Code für den grundlegenden Ablauf der Anwendung (Controller), das Auslesen und Verändern von Daten (Model) sowie die Darstellung des Shops (View) so weit wie möglich getrennt wird. Man spricht hier von eigenständigen Schichten, die sich jeweils nur um ihre eigenen Angelegenheiten kümmern. Der Controller soll also nicht direkt Daten manipulieren oder die Darstellung festlegen und so weiter.
Sehen wir uns nun an, wie diese Trennung in Magento erfolgt. Wer das Schema verinnerlicht, spart Zeit beim Suchen im Code und kann eigene, sauber strukturierte Erweiterungen entwickeln. Außerdem kommen wir dann hoffentlich auch einmal zu Tipps mit Code. 😉
5.1 Logik (Controller)
Wenn der Shop-Kunde eine Seite aufruft, analysiert Magento die URL und stellt fest, welche Aktion ausgeführt werden soll. Diese Aktionen lassen sich häufig thematisch gruppieren – beim Produkt-Katalog zum Beispiel in Aktionen rund um die Kategorien und in solche, die Produkte betreffen (Produkte anzeigen, Produktgalerie oder einzelne Bilder im Detail anzeigen etc.).
Aktionen, die demselben Thema zugeordnet werden können, haben oft Gemeinsamkeiten und sollten auch im Code nahe beieinander stehen, damit man den Überblick nicht verliert. Sie werden dazu in so genannte Controller zusammengefasst, die wiederum in Modulen gebündelt werden.
Sehen wir uns das anhand der Magento-Codestruktur an:
- Die standardmäßig mitgelieferten Module von Magento liegen in app/code/core/Mage. Da ich den Produktkatalog schon angesprochen habe, ziehe ich das Modul „Catalog“ gleich als Beispiel heran.
- Im Unterordner controllers/ liegen die Controller-Dateien. Wir sehen hier unter anderem eine CategoryController.php, eine ProductController.php und eine Product/CompareController.php. Bei jede dieser Dateien kann man sich gut vorstellen, wofür sie ungefähr zuständig ist.
- Öffnen wir ProductController.php. Darin befindet sich die Klasse Mage_Catalog_ProductController. Bei ihr handelt es sich Produkt-Controller. Wenn Magento anhand der URL entscheidet, dass dieser Controller für die Anfrage zuständig ist, wird die Datei automatisch geladen und die entsprechende Methode aufgerufen. Alle Klassenmethoden in diesen Dateien, die auf Action() enden, stellen Aktionen nach dem MVC-Schema dar. Dabei handelt es sich um eine Konvention des Zend Framework, welches von Magento als Code-Unterbau verwendet wird.
- Wir sehen, dass es im Product-Controller drei Aktionen gibt: view (die Anzeige eines Produkts), gallery (die Anzeige einer Produktgalerie) und image (die Anzeige eines Produktbildes). Die anderen Methoden sind Hilfsfunktionen, die von den drei Aktionen aufgerufen werden. Dadurch wird vermieden, dass derselbe Code mehrmals geschrieben werden muss.
- Wenn man sich den Code zur Brust nimmt, sieht man, dass der Controller zwar das Produkt mittels eines Models lädt und Anweisungen gibt, die Seite darzustellen, aber er gibt nur grundlegende Befehle. Er beschäftigt sich nicht mit den Details, sondern achtet darauf, dass alles in die Wege geleitet wird und steuert das Geschehen. Fast wie ein Manager.
Fassen wir zusammen: Die Controller liegen im Verzeichnis app/code/[scope]/[hersteller]/[modul]/controllers/ und steuern die Anwendung, ohne aber direkt auf Daten zuzugreifen oder die Darstellung zu beeinflussen.
5.2 Daten (Model)
Die Model-Schicht ist dafür zuständig, Daten abzufragen, zu verändern und bereitzustellen. Die Daten können in Datenbanken, Dateien, im RAM, in einem externen Webservice oder wo auch immer abgelegt sein. Um dieser Tatsache Rechnung zu tragen, unterscheidet Magento zwischen Models und Resource-Models.
Das Model wird im Verzeichnis app/code/[scope]/[hersteller]/[modul]/Model/ abgelegt und ist dafür da, bestimmte Funktionen und Inhalte zur Verfügung zu stellen. Die grundlegende Model-Klasse für ein Produkt zum Beispiel ist Mage_Catalog_Model_Product. Die Klasse ist dafür da, Produkte zu laden, zu aktualisieren und zu löschen; sie kann aber noch weit mehr, zum Beispiel alle Kategorien eines Produkts, mit ihm verbundene Produkte oder Informationen über die Verfügbarkeit, Sichtbarkeit etc. liefern. Hierbei handelt es sich um Datenlogik, also die logische Verarbeitung von Daten. Ob diese Daten überhaupt abgespeichert werden und – wenn ja – wo und wie, das soll für die Datenlogik egal sein.
An dieser Stelle kommen die Resource-Models ins Spiel. Sie liegen unterhalb des Verzeichnisses app/code/[scope]/[hersteller]/[modul]/Model/Resources/, sind über die Modul-Konfiguration mit den Models verbunden und kümmern sich um das Lesen oder Schreiben der Daten von bzw. in bestimmte Datenquellen. Das klingt vielleicht unnötig kompliziert, doch diese Vorgehensweise bringt einige Vorteile mit sich.
So wird es möglich, z.B. MySQL durch eine andere Datenbank oder gar ein System mit Dateien, Web-Services etc. auszutauschen, indem andere Resource-Models verwendet werden. In der Praxis wird dies nur selten passieren, doch indem die Funktionen zur dauerhaften Speicherung der Daten (auch Persistenz-Haltung genannt) ausgelagert werden, wird der Code zusätzlich besser wart-, les- und wiederverwendbar.
Wir fassen zusammen: Die Models liegen im Verzeichnis app/code/[scope]/[hersteller]/[modul]/Model/ eines Moduls. Es wird unterschieden zwischen Models, mit denen man Informationen ausliest, Daten schreibt oder verändert und Resource-Models, die sich darum kümmern, woher diese Daten kommen und wohin sie gehen. Das Model kümmert sich nicht darum, wie die Daten dargestellt werden.
5.3 Darstellung (View)
Die View-Schicht kümmert sich um die Präsentation des Webshops. Für Anfänger ist dieser Teil vielleicht noch verwirrender als die Controller- bzw. Model-Schicht. 😉 Bringen wir etwas Licht ins Dunkel.
Prinzipiell wird der Code für die Darstellung an zwei Orten gespeichert:
- Ein Teil des Codes wird von Magento intern verarbeitet und sollte nicht von außen aufrufbar sein. Dieser Code befindet unter app/, genauer gesagt unter app/design und app/code/[scope]/[hersteller][modul]/Block/. Was es damit auf sich hat, klären wir gleich.
- Der zweite Teil der Dateien muss für den Web-Browser (extern) erreichbar sein. Dazu zählen unter anderem CSS- und JavaScript-Dateien sowie Bilder. Diese Dateien werden unter skin/ abgelegt.
Von Packages & Themes
Sehen wir uns die Struktur dieser internen und externen Ordner einmal genauer an. Die Unterordner ist für app/design/ und skin/ weitgehend gleich. Ich ziehe als Beispiel den internen Ordner app/design/ heran.
Wir finden drei Unterordner vor: adminhtml, frontend und install.
- „adminhtml“ ist für die Darstellung der Administrationsoberfläche zuständig,
- „frontend“ für den eigentlichen Webshop und
- „install“ für das Installations-Tool von Magento.
Wir klicken uns in den Ordner für den Webshop, app/design/frontend, hinein. Dort sehen wir wieder in beiden Fällen zwei Ordner: base und default.
Sie existieren, da man in Magento mehrere Designs verwenden kann. Dadurch wird es möglich, das Aussehen des Shops z.B. je nach Sprache oder Saison zu verändern oder mit einer Magento-Installation mehrere Shops zu betreiben. Um die Designs voneinander zu trennen, werden die Darstellungsverzeichnisse in zwei Ebenen, Packages und Themes, unterteilt.
Bei „base“ und „default“ handelt es sich um Packages.
- „base“ legt (wie der Name vermuten lässt) die Basis für die Designs. Hier sind alle Dateien zu finden, die der Shop von Haus aus benötigt.
- Im Package „default“ liegen dann mehrere Designs bereit, die auf base aufbauen und mit Magento ausgeliefert werden. Man kann natürlich eigene Packages anlegen. Zumeist werden eigene Designs aber einfach unterhalb dieses Standard-Packages erstellt.
Wechselt man in den Package-Ordner app/design/frontend/base/ bzw. app/design/frontend/default/, dann stößt man wieder auf neue Unterordner. Hier sind wir endlich bei den Themes angekommen, also bei den Designs, die man im Webshop sieht.
- Unter base gibt es einen Ordner „default“. Wie erwähnt legt „base“ nur die Basis, und deswegen brauchen wir auch nur ein Theme darunter. Der Theme-Ordner heißt ebenfalls „default“, weil es sich – richtig vermutet – um das Basis-Theme handelt.
- In default gibt es vier Ordner: „blank“, „default“, „iphone“ und „modern“. Das sind die vier Themes, die tatsächlich von Anfang an im Webshop als Design angeboten werden:
- „blank“ stellt ein reduziertes Design dar, das vor allem zur Erstellung eigener Themes nützlich ist,
- „default“ ist das Design, das man in Demo-Shops bzw. direkt nach der Installation zu sehen bekommt,
- „iphone“ ist ein angepasstes Design für das Smartphone von Apple und
- „modern“ ist ein schickes Design, für das man sich alternativ entscheiden kann.
Bestandteile der View
Wir wissen jetzt, wo wir ein Theme finden. Jedoch gibt es in jedem Theme eine Menge Dateien. Wir müssen daher herausfinden, wie sie zu einer vollständigen Seite zusammengesetzt werden. Dazu ziehen wir die Basis-Dateien aus app/design/frontend/base/default/ heran.
Die Grundlage des Designs sind Layout-XML-Dateien. Sie liegen im Verzeichnis app/design/frontend/base/default/layout/ und werden von Magento allesamt eingelesen. In den XML-Dateien wird das Grundlayout für die Seiten definiert.
Genauer gesagt wird der XML-Code in so genannten Handles aufgeteilt. Für jeden Seitenaufruf ist ein Teil der Handles relevant. Der Code, der unterhalb des Handles zu finden ist, wird für die Darstellung der Seite berücksichtigt. In einem Beispiel erfahren wir gleich, wie das mit den Handles genau funktioniert.
Innerhalb des Handles werden Blöcke definiert, aus denen das Seiten-Layout besteht. Dabei wird unterschieden zwischen Strukturblöcken, die im Großen und Ganzen nur die Position wie „Header“, „links“, „Content“, „rechts“ oder „Footer“ angeben und Inhaltsblöcken, die Formulare, ein Produkt, eine Kategorieliste und ähnliches anzeigen.
Für jeden dieser Blöcke gibt es zwei wichtige Angaben:
- erstens wird definiert, welche Block-Klasse für den Block verwendet wird. Die Klassen sind wieder in den Modulen von Magento zu finden, diesmal im Ordner Block/ des Moduls.
- zweitens wird definiert, welche Template-Datei für den Block verwendet wird. Diese Datei befindet sich unterhalb des Ordners „template“ im verwendeten Theme.
Beispiel
Tasten wir uns über ein Beispiel, nämlich die Darstellung einer Produktseite, heran. Ich gehe nicht auf jede Einzelheit ein, es geht nur um das Grundverständnis.
Zuerst fragt man sich: welche Teile der Layout-XML-Dateien werden für die Darstellung der Produktseite berücksichtigt?
Hier kommen Handles ins Spiel. Ein wichtiges Handle ist <default>. Es wird bei nahezu allen Seitenaufrufen berücksichtigt. Heißt: alles, was in den Layout-XML-Dateien unter <default> zu finden ist, wird prinzipiell für die Produktseite verwendet.
Natürlich sind für die Produktseite spezielle Anweisungen nötig. Daher bastelt Magento aus dem Modul, Controller und der Action ein Handle, das ebenfalls für das Layout berücksichtigt wird. Klickt man auf ein Produkt, dann wird die Action „view“ des Controllers „Product“ im Modul „Catalog“ aufgerufen. Magento macht daraus ein Handle <catalog_product_view>, das in den Layout-XML-Dateien verwendet werden kann, um das Design zu beeinflussen.
Genau das passiert in der Datei app/design/frontend/base/default/layout/catalog.xml:
<catalog_product_view translate="label"> <!-- ... --> <reference name="content"> <block type="catalog/product_view" name="product.info" template="catalog/product/view.phtml"> <!-- ... --> </block> <!-- ... --> </reference> <!-- ... --> </catalog_product_view>
Dieser Code besagt:
- wenn die Seite catalog/product/view (also die Produktdetailseite) aufgerufen wird,
- dann füge in den Strukturblock „content“ einen Inhaltsblock ein.
- Der Inhaltsblock ist vom Typ „catalog/product_view“, hat den Layout-Namen „product.info“ und als Template-Datei verwende ich „catalog/product/view.phtml“.
Sehen wir uns die Anweisungen für den Inhaltsblock genauer an:
- Der Typ gibt die Block-Klasse nach dem Schema modul/ordner_und_dateiname_ohne_endung an.
Das heißt für „catalog/product_view“:
– Der Block liegt im Modul „catalog“. Damit ist app/code/core/Mage/Catalog/Block/ das Block-Verzeichnis, in dem wir suchen.
– Der Block wird weiters mit „product_view“ gekennzeichnet. Würde hier nur „view“ stehen, dann würde es sich um die Datei View.php im Block-Verzeichnis handeln. Finden sich Unterstriche im String, dann stellt nur der letzte Teil den Dateinamen dar, die Teile davor sind Verzeichnisse. „product_view“ sagt uns also, dass die gesuchte Datei Product/View.php ist.
So kommen wir also zu: app/code/core/Mage/Catalog/Block/Product/View.php.
Die Klasse darin heißt dementsprechend Mage_Catalog_Block_Product_View. - Der Layout-Name kann in der Layout-XML-Datei verwendet werden, um wieder auf den Block zuzugreifen. Damit kann man den Block später löschen, Methoden der Block-Klasse aufrufen etc.
- Die Template-Datei wird verwendet, um den Block darzustellen.
Der Pfad gilt relativ innerhalb des Verzeichnisses app/design/[package]/[theme]/template/, wir sind also weiterhin im internen Teil des Themes.
Das heißt: wenn wir vom default-Theme des base-Packages ausgehen, haben wir das Verzeichnis app/design/frontend/base/default/template und hängen den Pfad aus der XML-Datei an: app/design/frontend/base/default/template/catalog/product/view.phtml
Voilà!
Jetzt stellt ihr euch wahrscheinlich die Frage, wozu es die Trennung zwischen der Block-Klasse und der Template-Datei gibt. Die Template-Dateien sollten den HTML-Code zusammenstellen und möglichst wenig Logik enthalten. Man kann in der Template-Datei auf die zugehörige Block-Klasse zugreifen, um mittels ihrer Methoden Models zu laden oder komplexere logische Abhandlungen abzuwickeln.
Der Sinn davon:
- Die Block-Klasse dient als verbindendes Element zwischen Model und View. Es wird verhindert, dass Models direkt im Template geladen werden.
- Es wird so leichter, den Code der Template-Dateien schlank zu halten und HTML-Code etwas von der Präsentationslogik zu trennen.
- Zudem wird Code-Duplizierung vermieden, da man in verschiedenen Template-Dateien auf die Methoden derselben Block-Klasse zurückgreifen kann.
In dieser Hinsicht sind Helper-Klassen den Block-Klassen sehr ähnlich. Sie werden im Ordner app/code/[scope]/[hersteller]/[modul]/Helper/ gespeichert und können ebenfalls in Template-Dateien verwendet werden.
In den Template-Dateien können Hilfsmethoden verwendet werden, um extern zugängliche Dateien wie Bilder im HTML-Code einzufügen. Die Methoden erzeugen automatisch den richtigen Pfad abhängig vom aktuell ausgewählten Design, womit man sich Ärger bei wechselnden Designs oder mehreren parallel eingesetzen Designs erspart.
Wir fassen zusammen:
In Magento kann man mehrere Designs verwenden. Sie werden in Packages und Themes organisiert. Im Dateisystem werden die Themes an zwei getrennten Stellen vorgehalten: es gibt den internen Teil, der von Magento verarbeitet wird und für den Außenstehenden nicht erreichbar sein soll sowie den externen Teil mit Dateien, die der Webbrowser des Kunden herunterladen können muss.
Das Design wird über Layout-XML-Dateien zusammengesetzt. In diesen Dateien werden Struktur- und Inhaltsblöcke definiert, wobei die Blöcke aus einer Block-Klasse und einer dazu gehörigen Template-Datei bestehen.
5.4 Weiterführende Links
Controller
- Magento Knowledge Base: Magento Controller Dispatch
Erklärt, wie Magento von der URL auf die auszuführende Aktion schließt und zeigt, wie man einen eigenen Controller erstellt.
Model
- Magento Knowledge Base: Magento Models and ORM Basics
Erläutert Magento-Models und stellt vor, wie man eigene Models und Resource-Models aufbaut.
View
- Magento Knowledge Base: Magento Layouts, Blocks and Templates
Geht ausführlich auf die XML-Layout-Dateien und Block-Klassen ein. In einem Beispiel wird ein eigenes Handle eingebaut und eine eigene Seite eingebaut.
Fazit Teil 2
Heute ging es um die MVC-Architektur (Model, View & Controller) in Magento. Bravo, wer bis hier durchgehalten hat. 😉 Das Thema ist für manchen am Anfang mühsam, aber es ist wichtig, wenn man sich zurechtfinden und eigene Erweiterungen vornehmen will, die sauber programmiert sind. Im nächsten Teil setzen wir uns konkreter mit den einzelnen Schichten auseinander.
Habt ihr Fragen oder Link-Tipps zur Magento-MVC-Architektur, die ich aufnehmen soll? Dann meldet euch bitte in den Kommentaren!
Hallo,
bei uns ist auf der Produktdetailsseite der Header, Footer und die rechte bzw. linke Spalte verschwunden. Hier eine Vorschau http://www.mrdrinks.de/malzbier/kvass-russian-soda/kvass-0-33-l.html
Wie kriegen wir diese wieder ans Laufen?
Danke
Hallo,
für dieses Produkt oder für alle Produktseiten ist ein Template ausgewählt, das die entsprechenden Elemente (Header, Footer, Spalten) nicht enthält. Warum das so ist, kann man von außen aber überhaupt nicht sagen.
Wenn Sie nichts umgestellt haben, fragen Sie am besten bei demjenigen nach, der das Template für Sie implementiert hat bzw. bei dem Sie es gekauft haben.,
Danke für die Antwort.
Hab dies alles alleine erstellt, jedoch bin ich kein Fachmann und arbeite mich so langsam da durch.
Wo finde ich diese Template bzw. die Datei?
app / design / frontend / default / galabrandstore / template / catalog / product / view? Hab dort schnon nachgeschaut, jedoch kann ich dort nichts finden. Hier der Auszug aus der Datei:
* Product view template
*
* @see Mage_Catalog_Block_Product_View
* @see Mage_Review_Block_Product_View
*/
?>
helper(‚catalog/output‘); ?>
getProduct(); ?>
var optionsPrice = new Product.OptionsPrice(getJsonConfig() ?>);
getMessagesBlock()->getGroupedHtml() ?>
<form action="getSubmitUrl($_product) ?>“ method=“post“ id=“product_addtocart_form“getOptions()): ?> enctype=“multipart/form-data“>
<input type="hidden" name="product" value="getId() ?>“ />
getChildHtml(‚media‘) ?>
getPreviousNextProducts($this->getProduct()); ?>
<a href="getProductUrl(); ?>“ title=“__(‚Previous‘) ?>“ class=“prev“>__(‚Previous‘) ?>
<a href="getProductUrl(); ?>“ title=“__(‚Next‘) ?>“ class=“next“>__(‚Next‘) ?>
productAttribute($_product, $_product->getName(), ’name‘) ?>
getLayout()->createBlock(‚core/template‘)->setProduct($_product)->setTemplate(‚em_manufacture_brand/logo.phtml‘)->toHtml(); ?>
canEmailToFriend()): ?>
<a href="helper(‚catalog/product‘)->getEmailToFriendUrl($_product) ?>“>__(‚Email to a Friend‘) ?>
__(‚SKU:‘) ?> getSKU()?>
getChildHtml(‚alert_urls‘) ?>
getChildHtml(‚product_type_data‘) ?>
getTierPriceHtml() ?>
getChildHtml(‚extrahint‘) ?>
getReviewsSummaryHtml($_product, false, true)?>
getShortDescription()):?>
__(‚Quick Overview‘) ?>
productAttribute($_product, nl2br($_product->getShortDescription()), ’short_description‘) ?>
hasOptions()):?>
isSaleable()): ?>
getChildHtml(‚addtocart‘) ?>
helper(‚wishlist‘)->isAllow() || $_compareUrl=$this->helper(‚catalog/product_compare‘)->getAddUrl($_product)): ?>
__(‚OR‘) ?>
getChildHtml(‚addto‘) ?>
getChildHtml(‚extra_buttons‘) ?>
isSaleable()): ?>
getChildHtml(‚addto‘) ?>
getChildHtml(‚other‘);?>
isSaleable() && $this->hasOptions()):?>
getChildChildHtml(‚container1‘, “, true, true) ?>
isSaleable() && $this->hasOptions()):?>
getChildChildHtml(‚container2‘, “, true, true) ?>
//
getChildGroup(‚detailed_info‘, ‚getChildHtml‘) as $alias => $html):?>
<div class="box-collateral „>
getChildData($alias, ‚title‘)):?>
escapeHtml($title); ?>
getChildHtml(‚product_additional_data‘) ?>
getChildHtml(‚upsell_products‘) ?>
getChildHtml(‚review_list‘) ?>
getChildHtml(‚product_additional_collateral‘) ?>
getImageProduct_UseTabs()): ?>
//
//
Hallo Matthias,
Vielen Dank für den sehr guten Artikel. Ich arbeite mich seit ein paar Stunden in Magento ein, um eine Extension dafür zu programmieren. Für einen erfahrenen ZF1 und ZF2 Entwickler hat dein Artikel genau das richtige Level,um die Brücke zum Magento System zu schlagen.
Beste Grüße,
Alex
Hallo Alex,
danke für das Lob!
Klasse Artikel,
wo finde ich denn den Link zu Part 3 auf den du am Ende dieses Beitrags hinweist?
Danke für die super Erklärung!
Alex
Hallo Alex, hier findest du Teil 3: Tipps für die Entwicklung mit Magento: Teil 3 (Controller erweitern)
Hallo Matthias, bin mir nicht sicher ob du mir helfen kannst, aber ich versuche es mal.
Ich möchte für einige Artikel (Produkte) das Aussehen per css ändern. Habe gedacht ich könnte dies über den Reiter „Darstellung“ in der Artikelverwaltung lösen. aber dort wird mein zusätzliches angelegter theme leider nicht angezeigt. Muss ich etwas besonderes bedenken?
Hallo Brigitte,
normalerweise sollten alle Themes angezeigt werden, das Phänomen habe ich noch nicht erlebt. Wenn du nur ein paar kleine Designänderungen per CSS vornehmen willst, kannst du auch ohne eigenes Theme arbeiten: Magento fügt auf der Produktseite im Body-HTML-Tag eine CSS-Klasse „product-[produktname]“ hinzu (für ein Produkt „HTC Desire“ zum Beispiel „product-htc-desire“).
Über die CSS-Klasse kannst du ebenfalls produktspezifisch das Design ändern, ohne dass du ein eigenes Theme anlegen musst.
lg Matthias
Einfach nur Toll!
Das sollten sich viele mal anschauen, dann würde es vielleicht weniger Fragen geben.
Gruß aus München
Michael
Danke für das Lob, Michael!