Magento Commerce Programmstruktur

Dieser Beitrag spiegelt meine Eindrücke und Überlegungen beim ersten Durchsehen des Magento Commerce Codes wider. Ich bin mir ziemlich sicher, daß ich noch nicht alles durchschaue, und so sind Fehler nicht auszuschließen. Ich habe mir jedoch Mühe gegeben die geschilderten Zusammenhänge so verständlich wie möglich darzulegen. Anmerkungen und Kommentare sind wie immer erwünscht.

Verzeichnisbaum

Da Magento Commerce mit dem Zend Framework entwickelt wird, basiert auch der Magento Code auf dem MVC Designpattern. Zunächst ein Blick auf die unteren zwei Verzeichnisebenen:

idefix:/var/www/magentocommerce$ svn up
Revision 13000
idefix:/var/www/magentocommerce$ tree -L 2 -d > wordpress
  • app
    • code
    • design
    • etc
    • locale
  • de
  • fr
  • js
    • adminhtml
    • calendar
    • extjs
    • lib
    • mage
    • prototype
    • scriptaculous
    • tiny_mce
    • varien
  • lib
    • Varien
    • Zend
  • media
  • skin
    • adminhtml
    • frontend
  • var
    • cache
    • session

27 Verzeichnisse

Die untersten drei Verzeichnisebenen von Magento Commerce gibt es auf einer extra Seite.

Was liegt wo bei Magento?

Im Prinzip sind die Verzeichnisnamen selbsterklärend, und wenn man sich etwas mit MVC beschäftigt hat auch sinnvoll angeordnet. In „app/code“ liegt die Programmlogik und die Models. Weiter unten in diesem Artikel werde ich exemplarisch einen Controller und ein Model um eine momentan noch nicht vorhandene Funktion erweitern, ich hoffe dann wird die Struktur noch klarer.

Das gesamte System ist übrigens um eine „Superklasse“ herum aufgebaut, die sich „Mage“ nennt. Diese Klasse ist auch für den Bootstrap von Magento Commerce verantwortlich. Dazu später mehr.

Unter „app/design“ liegen die Views für admin und frontend, von wo aus die Controller ihre Anweisungen erhalten und diese an die Models weiterleiten.

In „app/etc“ liegt die Magento Konfiguration in XML-Dateien verpackt. Hier wäre YAML schön gewesen, denn XML liest sich einfach schlecht für den Menschen. Aber meckern ist bekanntlich immer einfacher als besser machen, und vielleicht bin ich auch etwas von RoR beeinflusst 😉

Weiter zu „app/locale“ – die Sprachdateien für die einzelnen locales liegen dort als CSV. Ich tippe darauf, daß der erste Ausdruck der Schlüssel eines assoziativen Arrays ist, und der zweite die jeweilige Übersetzung. Sauberer Ansatz, ob die „problematischen“ Locales wie chinesisch, japanisch etc. damit abgedeckt werden können weiß ich allerdings nicht.

Das war es auch schon unter „app/„… später werde ich noch einmal auf die vielen Unterordner in „app/code/core/Mage“ zurück kommen.

Weiter mit „de, fr“ – diese Verzeichnisse enthalten nur eine index.php, die Mage mit der entsprechenden Locale startet, z.B. „Mage::run(‚german‘);“. Die „run“ Funktion wird im nächsten Abschnitt etwas ausführlicher beleuchtet.

In „js“ liegen – wie der Name es vermuten lässt – die Javascript-Bibliotheken von Magento. Dort finden sich bekannte Namen wie Scriptaculous, Tiny_MCE, Prototype und Dynarch Calendar. Ja, ich mag Open-Source 🙂

Dazu kommen noch einige selbstgeschriebene Libs und Skripte unter „Varien“, „Mage“ usw.

Eine schlanke und gute Alternative zu den JS-Boliden scriptaculous und Prototype ist übrigens mootools. Damit kann man sich ein eigenes kleines Framework zusammenstellen, das nur das beinhaltet was man wirklich braucht. Das können auch schnell mal 50 kb Ersparnis pro Seitenaufruf werden, was sich bei Traffic-intensiven Seiten durchaus bemerkbar macht.

Weiter mit „lib/„. Die Libraries (Bibliotheken) die Magento benutzt liegen hier, sowohl selbstgeschriebene als auch die vom Zend Framework. Interessant ist hier der „Router“ unter „lib/Varien/Controller/Router“, der für das Mapping der aufgerufenen URLs zu den einzelnen Controllern von Magento Commerce zuständig ist. Dort liegt noch eine ganze Menge anderer Sachen, wie es für Libs üblich ist.

In „media/“ liegt… gar nichts. Hier werden wahrscheinlich Produktbilder, PDFs usw. gespeichert.

Zuletzt ein Blick auf „skin/“ und „var/„. Unter „skin“ liegen die Bilder, Stylesheets und zusätzlicher Javascript die von einem Skin benötigt werden. Die CSS-Dateien des default-skins wurden sauber unterteilt, und auch der Internet Explorer hat wieder seine Extra-Wurst bekommen.

Das Verzeichnis „var“ beinhaltet dann nur noch die variablen Dateien von Magento, also den Cache und die Sessions. Ich bin gespannt ob es die Möglichkeit geben wird, Sessions in der Datenbank zu speichern.

Das war der Kurzüberblick über die Programmstruktur von Magento Commerce. Lassen Sie uns jetzt einen Blick darauf werfen wie das innere Getriebe von Magento arbeitet!

Was passiert beim Aufruf von Magento Commerce?

Die Superklasse „Mage“ (s.o.) inkludiert einige Funktionen aus „app/code/core/Mage/Core/functions.php“, lädt die Konfiguration, das Design, bereitet Logging, Cron und einiges andere vor und startet dann die Applikation. Die init-Funktion für Mage findet sich übrigens in „app/Mage.php“, zusammen mit der Funktion die alles ins Rollen bringt und ganz einfach „run“ heißt.

Schaut man in die index.php im Root-Ordner wird klar was passiert:

require 'app/Mage.php';
Mage::run('base');

„Lade die Superklasse und starte das base-system“. Sie erinnern sich an die Dummy-Index-Dateien der verschiedenen Locales? Die Funktion „run“ startet zuerst den Profiler und führt dann die init-Methode aus. Diese lädt die Konfiguration und setzt einige Umgebungsvariablen. Die Run-Methoden setzt dann den Standard- bzw. Startcontroller auf „Mage_Core_Controller_Varien_Front“, initiiert diesen und spuckt dann den fertigen HTML-Code aus. Ganz einfach 🙂

Auffällig ist, daß überall Profiling und Errorhandling auftaucht, was späteres Debuggen und das Finden von Bottlenecks vereinfachen wird.

Schauen wir uns jetzt exemplarisch den Aufruf einer anderen URL an. Ich nehme als Beispiel etwas ganz Alltägliches:

http://www.magento-installation.de/catalog/category/view/s/test-kategorie/id/5/

Übersetzt heißt das Ganze dann ungefähr „Rufe die Sektion ‚Catalog‘ mit dem Controller ‚category‘ auf. Übergebe den Befehl ‚view‘ mit dem Parameter ‚id=5‘.“

Erinnern Sie sich an den „Router“? Dieser leitet die Anfragen an den Controller weiter. Jetzt schauen wir mal, ob wir im Controller-Verzeichnis unser „catalog“ wiederfinden…

Und tatsächlich: „app/code/core/Mage/Catalog/controllers/CategoryController.php“ ist unser Kandidat für diesen Programmaufruf. Wie es sich gehört steht dort eine kommentierte Funktion „viewAction“, die das zugehörige Category-Model (es liegt unter „../Model/Category.php“) mit der übergebenen ID instantiiert, das Layout lädt und rendert.

In dieser Manier lässt sich anhand der URL sehr leicht die Stelle im Programmcode finden die für die momentane Aktion verantwortlich ist. Lassen Sie uns dieses Wissen jetzt in der Praxis ausprobieren, indem wir eine fehlende Funktion implementieren.

Magentos Code erweitern und anpassen

Eine Anpassung des Codes erfolgt demnach im Controller, im Model und / oder im zugehörigen View. Lassen Sie uns exemplarisch die delete-Funktion für Websites anfangen, die momentan (16.10.2007) noch fehlt.

Um das zu überprüfen legen Sie im Admin-Bereich eine neue Website an und versuchen diese anschließend wieder zu löschen. Der Aufruf zum Löschen einer Website sieht ungefähr so aus:

http://www.magentodev.de/admin/system_website/delete/website/buecher/

Sie erinnern sich an die Feststellung von oben, daß sich die Stelle im Code durch die URL herausfinden lässt? Wir befinden uns momentan in einem View des Admin-Bereichs, also „app/code/core/Mage/Adminhtml“, und der Controller der aufgerufen wird ist offensichtlich „system_website“. Daraus ergibt sich der vollständige Pfad zu unserem Controller:

app/code/core/Mage/Adminhtml/controllers/System/WebsiteController.php

In der Revision 13.117 steht bei der zugehörigen Aktion im Controller (Zeile 86) nur dies:

public function deleteAction()
{
    $this->_redirect('*/system_config');      
}

Also ein Redirect in den System-Konfigurationsbereich ohne jegliche Aktion. Stimmt, wenn ich versuche eine angelegte Website wieder zu löschen lande ich wieder auf der Systemconfig-Startseite und die Website ist immer noch da… Mein erster Ansatz in so einem Fall ist, mir die vorhandenen fertigen Aktionen anzuschauen. Die „saveAction“ sieht gut aus:

public function saveAction()
{
    if ($data = $this->getRequest()->getPost()) {
        Mage::getConfig()->getCache()->remove('config_global');
        $model = Mage::getModel('core/website');
        $model->setData($data);
        try {
            $model->save();
            Mage::getSingleton('adminhtml/session')->addSuccess(__('Website was successfully saved'));
            Mage::getSingleton('adminhtml/session')->setFormData(false);
            $this->_redirect('*/system_config/edit', array('website'=>$model->getCode()));
        } catch (Exception $e) {
            Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
            Mage::getSingleton('adminhtml/session')->setFormData($data);
            $this->_redirect('*/*/edit', array('website' => $this->getRequest()->getParam('website')));
            return;
        }
    }
}

Sie packt die vorhandenen $_POST Daten in eine Variable $data, instantiiert das zugehörige Model „core/website“, füllt dieses mit den übergebenen Daten und versucht in einem try..catch Block das Model dazu zu bewegen sich selbst zu speichern. Wir müssen also nur dafür sorgen, daß bei der „deleteAction“ das Gleiche passiert, nur daß sich das Model dieses mal löschen soll.

Der Code im Controller ist also schnell geschrieben, im Prinzip werden nur die ->save Aufrufe durch ->delete ersetzt und die Success- und Failure- Nachrichten angepasst. Schwieriger wird’s im Model, denn die Models sind für die Konsistenz der Daten und die Datenhaltung allgemein verantwortlich. Eine beliebte Phrase beim MVC-Pattern ist auch „Alle Macht den Models“.

Zunächst suchen wir die Quelldatei zu dem Model. Wie in der Action zu sehen, wird es mit „core/website“ instantiiert. Mit ein bißchen Überlegung ist klar wo das Model liegt, und zwar unter „app/code/core/Mage/Core/Model/Website.php“. Dort sieht man, daß die Core-Models von der Elternklasse „Mage_Core_Model_Abstract“ abstammen, welche wiederum „Varien_Object“ als Elternklasse haben.

Die delete- und save-Aktionen sind in diesem Fall nicht im Website-Model deklariert, sondern in der Elternklasse. Die dort aufgerufenen Funktionen „_beforeDelete“ und „_afterDelete“ erinnern mich übrigens stark an RoR

Aber zunächst zurück zum Controller. Die Variable $data wird bei der saveAction mit $_POST Daten gefüllt, was beim Aufruf der deleteAction nicht zutreffend wäre. Hier wird nur per GET-Aufruf die Bezeichnung der Website übergeben die gelöscht werden soll. Also schauen wir uns die Funktion mal an:

$data = $this->getRequest()->getPost()

Mit ein bißchen Rückverfolgung die Objektkette entlang landet man schließlich bei der Zend-Lib unter „lib/Zend/Controller/Request/Http.php“. Dieser Controller wird aufgerufen um übergebene Parameter aus GET und POST Aufrufen zu extrahieren, und ist damit unser Kandidat um den Namen der Website aus dem GET-Aufruf zu bekommen:

$data = $this->getRequest()->getParam('website')

Mit einem einfachen „echo“ lässt sich feststellen, daß jetzt wirklich der Name der Website in $data steckt. Die nächsten Schritte wären also, das Model „Website“ mit diesem Parameter aufzurufen, alle abhängigen Models (Stores, Produkte usw.) ebenfalls zu löschen oder zu verschieben, und am Ende das Model selbst zum Löschen zu veranlassen.

Da diese Aufgabe noch eine ganze Menge mehr Aufwand bedeutet breche ich an dieser Stelle ab und hoffe dem ein oder anderen einen ersten Einblick in die Funktionsweise der Programmstruktur von Magento Commerce verschafft zu haben.

Getagged mit: , , , ,
7 Kommentare zu “Magento Commerce Programmstruktur
  1. Jan sagt:

    Ich hoffe, dass es dazu eine vernünftige Anleitung geben wird. Ist ja schon recht mühselig so.

  2. Phoenix sagt:

    Vielen Dank für den tollen Artikel! Das macht den Einstieg in Magento um einiges leichter 🙂

  3. Jan F. sagt:

    Dank dir für den Artikel. Ich finde ihn sehr gut und nicht zu tiefgreifend, halt ideal für den Anfang und um einen Überblick der Systemarchitektur zu kriegen.

  4. willky sagt:

    Vielen Dank für den ausführlichen und verständlichen Artikel. Im übrigen habe ich gerade gesehen, dass Magento den 31.03. als Release-Termin jetzt erstmals offiziell bestätigt hat. Insofern kann man Deine Erläuterungen auch gleich an einem „Live-Projekt“ testen… Ich bin in jedem Fall gespannt wie sich Magento entwickeln wird. Das Interesse an der Software scheint ja doch enorm zu sein.

  5. willky sagt:

    Wir sind von der Programmarchitektur von Magento absolut überzeugt und haben mittlerweile zwei Shops in der Entwicklung. Unsere Erfahrungen werden wir dann in jedem Fall veröffentlichen. Auch wenn das Tool noch an ein paar Kinderkrankheiten leidet, glaube ich schon, dass über kurz oder lang kein Weg an Magento vorbei führen wird.

  6. Jan Bludau sagt:

    Hi,

    danke für diesen netten Guide 🙂

    weiter so

    gruss jan

  7. Eine Anleitung gibt es bisher nur in Englisch … deutschsprachige Entwicklerbücher kommen erst ende 2009 auf dem Markt … soweit ich es jedenfalls weiß.

2 Pings/Trackbacks für "Magento Commerce Programmstruktur"
  1. […] Beispiel findet man hier. Last but not least sei für die besonders Technik-affinen die Analyse der Programmstruktur von Magento bei Exanto erwähnt. Geschrieben in Magento, TrackBack-URL RSS-Feed für Kommentare zu […]

  2. […] Beispiel findet man hier. Last but not least sei für die besonders Technik-affinen die Analyse der Programmstruktur von Magento bei Exanto erwähnt. Diesen Beitrag […]

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.