Design Patterns für Framework-agnostische Extensions/Plugins

Meine neue Artikelreihe auf integer-net.com stellt nützliche Design Patterns für entkoppelte Magento Extensions vor, die in zwei Teile geteilt sind: Das Magento Modul und eine Framework-unabhängige (Framework-agnostische) Bibliothek, die für Magento 1 und Magento 2 wiederverwendbar ist. Die selben Prinzipien sind natürlich auch für weitere Frameworks und Anwendungen anwendbar.

Diagramm: Komponenten und Abhängigkeiten

Sie wird nicht den Refactoring-Prozess von bestehenden Extensions hin zu diesem Modell behandeln, das ist ein anderes Thema, das ich beim Developers Paradise 2016 in Kroatien präsentieren werde. Bleibt dran!

Im ersten Teil geht es um den Zugriff auf Konfigurationsdaten mit Configuration Value Objects.

Weiterlesen auf integer-net.com (Englisch)

Comparable Interface für PHP

Vor etwa 5-6 Jahren hatte ich meine “PHP sollte mehr wie Java sein” Phase und habe viel mit Sachen wie String Objekten und Überladen von Methoden experimentiert, was meistens fiese Workarounds erforderte und die meisten Dinge stellten sich auf lange Sicht nicht als sehr praktikabel heraus.

Aber ein Package aus der Zeit gefällt mir immer noch ziemlich gut, und zwar ComparatorTools, was immerhin Platz 2 in den monatlichen PHPclasses.org Innovation Awards belegte. Es stellt Comparable und Comparator Interfaces zur Verfügung sowie Funktionen, analog zu den Core Array-Funktionen, die mit diesen arbeiten können.

Interfaces

Die Interfaces ähneln den entsprechenden Java interfaces, außer dass wir keine Generics in PHP haben, so dass nicht garantiert werden kann, dass die verglichenen Objekte den selben Typ haben. Dies muss zur Laufzeit in der Implementierung geprüft werden, sofern nötig. Ein Exception-Typ für diese Fälle ist verfügbar:

interface Comparable
{
	/**
	 * @param object $object
	 * @return numeric negative value if $this < $object, positive if $this > $object, 0 otherwise (if objects are considered equal)
	 * @throws ComparatorException if objects are not comparable to each other
	 */
	public function compareTo($object);
}
interface Comparator
{
	/**
	 * @param object $object1
	 * @param object $object2
	 * @return numeric Negative value if $object1 < $object2, positive if $object1 > $object2, 0 otherwise
	 * @throws ComparatorException if objects are not comparable to each other
	 */
	public function compare($object1, $object2);
}

Continue reading “Comparable Interface für PHP”

CSV-Verarbeitung in Magento

Ein Grundsatz bei der Entwicklung, nicht nur mit Magento, ist dass man nicht versuchen sollte, das Rad neu zu erfinden und insbesondere auf die Funktionen des verwendeten Frameworks zurückzugreifen, soweit möglich. Magento hat viele mehr oder weniger bekannte universelle Helfer, in den Helper-Klassen aus Mage_Core sowie unter lib/Varien, und natürlich im Zend Framework.

Ein Klassiker ist z.B. JSON Kodierung. PHP hat zwar built-in die Funktionen json_encode und json_decode, die haben aber einige Unzulänglichkeiten, die in der Implementierung von Zend_Json ausgebügelt wurden. So gibt es in Zend_Json::encode() einen Zyklen-Check. Magento hat in Mage_Core_Helper_Data::jsonEncode() noch Support für Inline-Translations innerhalb von JSON hinzugefügt.
In Magento sollte man also immer Mage::helper('core')->jsonEncode() (bzw. jsonDecode) benutzen.

Varien_File_Csv

Und wie sieht es bei der Verarbeitung von CSV Dateien aus? Da der import und Export im Standard mit CSV Dateien funktioniert, sollte Magento doch etwas haben… Vorhang auf für Varien_File_Csv. Naja, ich nehme das Ergebnis mal vorweg: außer bei ganz einfachen Aufgaben mit kleinen Dateien ist die Klasse nicht zu gebrauchen.

Continue reading “CSV-Verarbeitung in Magento”

Ein JSON-RPC Adapter für die Magento API

Beim Durchsehen meiner alten Antworten auf Magento StackExchange bin ich auf diese Frage zum Ansprechen der Magento API via JavaScript gestoßen und musste feststellen dass der Link auf GitHub, der einen wesentlichen Teil der Lösung enthielt, nämlich die Implementierung eines JSON-RPC Adapters für die Magento-API mittlerweile tot ist.

Also habe ich kurzerhand das komplette daraus entstandene Modul selbst veröffentlicht (der originale Link war ein Core Hack):

GitHub: SGH_JsonRpc

Das ganze Modul sind weniger als 100 Zeilen Code. In config.xml wird unser Controller der api Route hinzugefügt:

    <frontend>
        <routers>
            <api>
                <args>
                    <modules>
                        <sgh_jsonrpc before="Mage_Api">SGH_JsonRpc_Api</sgh_jsonrpc>
                    </modules>
                </args>
            </api>
        </routers>
    </frontend>

Der neue API Adapter wird in api.xml definiert:

Continue reading “Ein JSON-RPC Adapter für die Magento API”

Spryker vs. Magento

Neulich hatte ich die Gelegenheit, einen Blick in den Source Code von Spryker zu werfen, dem Ecommerce-Framework das sich anschickt zum neuen Player im Enterprise-Bereich zu sein. Spryker wird vom Berliner Inkubator Project A für den Einsatz in selbst geförderten Unternehmen entwickelt und soll in diesem Jahr der Öffentlichkeit zugänglich gemacht werden. Wobei Öffentlichkeit nicht ganz stimmt, denn soviel vorab: Eine Open Source Version ist nicht geplant, Lizenzen sollen um 100.000 € / Jahr kosten. Agenturen können sich als Partner registrieren, um ohne Lizenz Zugriff auf Source Code und Dokumentation zu bekommen. Mit der ersten verkauften Lizenz gibt es Zugriff auf zusätzliches Training-Material. Freelancer werden nicht angesprochen. Bereits offiziell als Partner gelistet sind auch die im deutschen Magento-Umfeld bekannten Agenturen CommercePlus und Symmetrics.

Was ist das Besondere an Spryker?

Spryker versteht sich nicht als fertig einsetzbares Produkt, sondern als Framework das alle Bausteine für eine individuelle E-Commerce Lösung bereitstellt, aus denen man sich für sein Projekt die benötigten auswählt und erweitert. Damit wird die Realität berücksichtigt, dass kein Projekt wie das andere ist und jeder Shop seine eigenen Prozesse und Infrastruktur hat, auf die individuell eingegangen werden muss.

Der Kern von Sprkyer sind zwei eigenständige Applikationen, Yves und Zed. Kurz gefasst, ist Yves eine leichtgewichtige Anwendung für das Frontend, Zed das schwere Geschütz fürs Backend.

Discover Spryker
Bild: http://spryker.com/product/

Yves (in der ersten Version mit Yii programmiert, jetzt mit Silex) liest sämtliche benötigten Daten ausschließlich aus einem In-Memory NoSQL Backend wie Redis.
Zed (Zend Framework 2) übernimmt die Kommunikation mit MySQL, Message Queue und Dritt-Systemen und enthält die Geschäftslogik zu Bestellprozessen usw.
Continue reading “Spryker vs. Magento”

Magento: Block-Inhalt verschwindet nach core_block_to_html_after Event

Vorsicht mit Mage_Core_Block_Abstract::toHtml() innerhalb eines Observers für core_block_to_html_after! Ich habe die Methode genutzt, um HTML aus einem Block in einen existierenden Block zu injizieren, mit dem Ergebnis dass alles außer diesem neuen Block von der Ausgabe entfernt wurde.

Beim Debugging im Core habe ich die Ursache darin gefunden, wie das Event dispatched wird:

        /**
         * Use single transport object instance for all blocks
         */
        if (self::$_transportObject === null) {
            self::$_transportObject = new Varien_Object;
        }
        self::$_transportObject->setHtml($html);
        Mage::dispatchEvent('core_block_abstract_to_html_after',
            array('block' => $this, 'transport' => self::$_transportObject));
        $html = self::$_transportObject->getHtml();

Wie man sieht, ist das Transport-Objekt, das genutzt wird um Änderungen am HTML in Observern zu ermöglichen, eine Klassenvariable von Mage_Core_Block_Abstract, und verhält sich ähnlich wie ein Singleton. Ich kann mir keinen guten Grund dafür denken aber es riecht verdächtig nach premature optimization.

Was nun passiert ist, dass während das Event verarbeitet wird, der neue Block seine eigene Ausgabe in seinem eigenen toHtml() Aufruf dem selben Transport-Objekt zuweist und die ursprüngliche Ausgabe verloren geht, wenn man sie nicht vorher gesichert hat.

Die Lösung für mein konkretes Problem war, den Block an anderer Stelle zu erstellen und vorzurendern, aber wenn das nicht getan wird, muss die Ausgabe direkt zu Beginn im Observer in eine neue Variable kopiert werden:

$html = $observer->getTransport()->getHtml(); // should be the first statement

// now you can do what you want, create other blocks, modifiy $html ...

$observer->getTransport()->setHtml($html); // should be the last statement