Verbesserungsvorschlag um Module noch einfacher und sauberer entwickeln zu können

Hallo an euch alle,

ich bin nun mit meinem Modul fast fertig (sind nur noch Kleinigkeiten) und habe prompt einen Verbesserungsvorschlag aus einer Technik die ich bei Magento und Shopware kennen gelernt habe.

Es geht um das eingreifen in bestehende Core Funktionalitäten. Mir ist schmerzlich aufgefallen das, wenn man z.B. eine Model oder Controller Klasse in seiner Funktionalität erweitern und manipulieren will man gezwungen ist die bestehenden Klassen zu erben und dann dort Methoden etc. zu überschreiben.

Das finde ich persönlich nicht wirklich gut und gelungen umgesetzt. Wie kann man dies deutlich verbessern? Nun Magento und Shopware machen dies auf sehr elegante Art und Weise vor.

Kleine Gegenüberstellung wie die Systeme das machen.

[B]Magento:[/B]

Magento stellt ein Event Obersver Pattern da zur Verfügung. Das bedeutet man kann da in seiner XML Datei (config.xml) Events registrieren.
Sieht kurz geschrieben so aus.


<events>
    <sales_quote_save_before>
        <observers>
            <mycompany_coupon_sales_observer>
                <class>mycompany_coupon/sales_observer</class>
                <method>calculateMycompanyPrice</method>
            </mycompany_coupon_sales_observer>
        </observers>
    </sales_quote_save_before>
</events>

Alles was in Events steht wird registriert im Event Observer von Magento.

Der Node <sales_quote_save_before> legt das auszuführende Event fest.

Der Node besagt das es sich um einen Oberserver handelt.

Der Node <mycompany_coupon_sales_observer> gibt Pfad und Klassenname des Obersvers aus dem eigenen Modul an.

Der Node dient im Magento dazu ein Obersver Objekt zu instanzieren vor dem / steht der Pfad zum Modul und dahinter interne Pfad zur eigentlichen Observer Klasse, hinter dem letzten _ steht immer der Klassenname.

Der Node legt die Methode fest die Obersver Code ausführen soll.

[B]Shopware:[/B]

Shopware legt für jedes Modul eine Bootstrap fest und in dieser gibt es dann eine Methode isntall(), diese wird nur ausgeführt wenn im Backend das Modul installiert wird. In dieser kann man dann Events und Hooks definieren. Über die Methode subscribeEvent(); werden dann bei der Modulinstallation Events und Hooks in die Datenbank gespeichert. Shopware nutzt bzw. baut dabei auf den Event Manager von Symfony auf.

So sieht das in der Boostrap aus.


$event = $this->createEvent(
			'Enlight_Controller_Action_PostDispatch_Frontend_Checkout',
			'onCheckoutPostDispatch');
		$this->subscribeEvent($event);

Im ersten Teil der createEvent() Methode wird gesagt das ein Event im PostDispatch des Checkout Frontend Controllers ausgeführt werden soll.

Der zweite Teil besagt die Event Mothode. Und diese sieht dann im Code folgendermaßen aus.


public function onCheckoutPostDispatch(Enlight_Event_EventArgs $args)
{

}

Über den Parameter $args welcher ein Objekt von Eventargumenten beinhaltet hat man auf die verschiedensten Funktionalitäten udn Daten Zugriff die zu diesem Zeitpunkt existieren und verfügbar sind.

Das Post und Pre Dispatch kommt daher, weil auch Shopware wie Magento auf dem Zend Framework aufbauen.

Das ist aber nebensache und dient nur zur Anschauung.

Zusätzlich bietet Shopware aber noch die Funktionalität von sogenannten Hooks, dass heißt man kann zu einem ganz bestimmten Zeitpunkt in einer Methode seinen Code aus seiner Event Hook Methode ausführen lassen.

Bei beiden liegt der Vorteil gegenüber Oxid klar auf der Hand. Ich kann eigene Modulfunktionalität in die Core Klassen injecten ohne auch nur ein bisschen zu erben und bestehende Methoden verändern zu müssen. Bei Magento muss das nur ganz selten und bei Shopware gar nicht (ist mir zumindest nicht aufgefallen).

Nun ich finde den Oxidshop gerade in seiner Geschwindigkeit überragend und wenn man nun noch ein solches Eventmangement einbringen würde für die Modulentwicklung waäre Oxid meiner Meinung nach der Renner schlecht hin.

Ich hoffe das hier eine geute Diskussion herauskommt in der man neue Erkenntnisse gewinnnen kann.

Ich grüße euch und die Diskussion ist eröffnet.

Gruß Daniel

ich kenne mich mit Magento und Shopware leider gar nicht aus, deswegen kenne ich nicht alle Aspekte.
So wie du es beschreibst, haben alle Vorgehensweisen ihre Vor- und Nachteile.
Spontan sehe ich z.B: folgendes Problem bei Magento:
wenn du etwas aufbaust, wo du mehrere Funktionen/Events in verschiedenen Klassen/Objekten erweiterst, hast du alles in der config.xml Bei großen Projekten wird es entsprechend viel und unübersichtlich.
Bei OXID ist es dagegen strikt OOP und aufgeräumt: 1 Datei mit allen Funktionen pro Klasse. So kann ich z.B. beim Update.die benötigten Stellen schnell lokalisieren oder auch wo anders wiederverwerten ohne davor aufräumen zu müssen.
Einen Vorteil von

<events>
    <sales_quote_save_before>
        <observers>
            <mycompany_coupon_sales_observer>
                <class>mycompany_coupon/sales_observer</class>
                <method>calculateMycompanyPrice</method>
            </mycompany_coupon_sales_observer>
        </observers>
    </sales_quote_save_before>
</events>

gegenüber

class mein_erweiterung extends meine_erweiterung_parent

sehe ich nicht, denn das Ergebnis ist im Endeffekt gleich ob man Events registriert/abonniert und eigenen Code ausführen lässt, oder wie bei OXID den Code in die Funktion reinpackt.

Events und Hooks kenne ich bei OXID nicht, da es wie gesagt rein OOP ist, d.h. dass du die Funktionen einfach erweitern musst, um deinen Code irgendwo anzuhängen.
Installationsroutine gibts aber auch bei OXID, schau dir mal die events in der metadata.php an.

Die Möglichkeit den eigenen Code irgendwo mitten in der Funktion ausführen zu lassen finde ich sehr gut, habe ich schon oft bei OXID vermisst.

Code dran- oder davorhängen ist ja klar, aber wie sieht es denn aus, wenn ich bei Magento eine Funktion komplett überschreiben/verändern will?

Hallo,

nunja das mit dem XML war auch nicht als Vorteil gedacht, es sollte nur wiederspiegeln wie das Magento macht, bei Shopware ist ähnlich wie bei Oxid, dort macht es die Module Bootstrap über die install Methode.

Auch bei Magento ist alles rein OOP, die XMLs werden von allen Modulen eingesammelt.

Auch bei Magento kann man Controller Methoden komplett überschreiben, dass ist aber in den aller seltensten Fällen nötig, weil eben durch das Event Observer Pattern man eigentlich fast immer die Möglichkeit hat einzugreifen. Wie gesagt beide, also Shopware und Magento bauen auf Zend auf und verfügen somit, bzw. machen sich da dem Dispatcher zu nutze mit PreDispatch und PostDispatch. PreDispatch vor dem Action Code und Post Dispatch nach dem Action Code.

Jetzt wo ich unser Modul für Oxid entwickle vermisste ich eben grad diese Event und Hook Funktionalität, dass hält eben den eigenen Code schlanker und begrenzt ihn wirklich auf das, was man mit seinem Modul machen will. Man lässt eben den Core Code unangetastet und spart sich in den meisten Fällen unnötige Vererbung.

Observer oder Event Dispatcher (ich denke du weist was das ist) sind ja auch rein OOP. Natürlich bieten beide Vor und Nachteile, so wie alle Pattern.

Gruß Daniel

Vorab: ich habe noch keine Module für Magento entwickelt, daher kenne ich die Vorgehensweise nicht, bitte korrigiere mich falls ich irgendwo falsch liege.

Auch bei Magento kann man Controller Methoden komplett überschreiben
Aber nur einmal, oder? Wenn zwei Module dieselbe Methode überschreiben funktioniert das dann?

Jetzt wo ich unser Modul für OXID entwickle vermisste ich eben grad diese Event und Hook Funktionalität, dass hält eben den eigenen Code schlanker und begrenzt ihn wirklich auf das, was man mit seinem Modul machen will. Man lässt eben den Core Code unangetastet und spart sich in den meisten Fällen unnötige Vererbung.

Auch bei OXID lässt man den Core Code unangetastet, soweit möglich. Z.B.:

<?php
class my_oxarticle extends my_oxarticle_parent
{
    public function getPictureUrl( $iIndex = 1 )
    {
        //New Code
        
        $returnval = parent::getPictureUrl( $iIndex );
        
        //New Code
        
        return $returnval;
    }
}

So ist man nicht darauf angewiesen dass ein Hook/Event existiert, verwendet den Code aus dem Core, und das können evtl. auch mehrere Module mit der gleichen Methode machen und es funktioniert trotzdem, weil jedes Modul das nächsthöhere Parent aufruft.

Warum ist es ein Vorteil, wenn man sich Vererbung spart? Mit Vererbung hat man vollen Zugriff auf das Objekt inkl. protected Methoden und kann den Grad der Modifikation selbst beeinflussen.

Hallo,

spannende Diskussion. Es gibt ja durchaus solche Methoden-Monster in OXID wie oxOrder::finalizeOrder(). Da kommste einfach nicht vernünftig zwischen rein. Haben heute erst wieder ein Modul gefixt, bei dem der Macher wohl das selbe Problem hatte.

Dem kann man nur durch kleine Methoden begegnen. Oder?

Gruß Joscha

[QUOTE=leofonic;121508]
Warum ist es ein Vorteil, wenn man sich Vererbung spart? Mit Vererbung hat man vollen Zugriff auf das Objekt inkl. protected Methoden und kann den Grad der Modifikation selbst beeinflussen.[/QUOTE]

Richtig man hat vollen Zugriff, aber braucht man den immer? Kann man den nicht auch über Events und Observer erlangen? Ich denke schon.

Warum ist es ein Nachteil immer Vererbung zu nutzen, sollte eher die Frage sein.

Ganz einfach, es kommt diesbezüglich immer wieder auf das mit Vererbung keinen Einstiegspunkt in Methoden findet, also muss man den ganzen Methoden kopieren und kann seine Änderungen machen. Das machen dann z.B. 8 Module und du hast 8 mal den selben Code, also sinnlose Code Duplikate auch genannt redundanten Code.

Wann immer es möglich ist sollte man Vererbung vermeiden können, dazu enstanden Pattern wie EventObserver oder EventDispatcher, welche eben Shopware sowie Magento aus gutem Grund anbiten. Shopware macht die sogar noch etwas weiter ausgebaut.

Es wird dem Entwickler einfach die Möglichkeit geboten zu bestimmten Zeitpunkten, in gewisse Prozesse einzugreifen ohne auch nur einmal einen Code duplizieren zu müssen, welches das Gesamtsystem wieder schlanker hält.

Ein ganz einfaches Besipiel in der Methode finalizeOrder des oxorder Models. Du willst als Module Entwickler die Möglichkeit haben Funktionalität vor oder nach dem speichern eines Orders einzubringen, momentan muss man nun erben und den Code auch noch duplizieren weil man einfach nicht rein kommt.

Besser wäre es doch aber wenn man sagen kann, es wird ein Event registriert welches meinen Ober Klassennamen hat, einen Eventnamen und welche Methode meines Observers dort ausgeführt werden soll.

Dass könnte Seitens des Oxid Core Codes so aussehen. Das ist jetzt sehr stark vereinfacht und dient nur zur Grundidee.


EventObserver::dispatch('save_order_before', $this, $eventObserver->loadEvents());
        
        //saving all order data to DB
        $this->save();

        EventObserver::dispatch('save_order_before', $this, $eventObserver->loadEvents());

So der Modulentwickler freut sich nun darüber, er legt in seinem Modul Beispielsweise einen Ordner Observer an und legt sich seine Klasse OrderObserver.php an. Die kann nun so aussehen.


class OrderObserver
{
	public function onOrderSaveBefore(EventObsever $observer)
	{
		// ausführender code
	}
}

Über die metadata.php könnte man solche Events registrieren, um das einsammeln der Events und das richtige ausführen muss sich dann ein Core Event Dispatch Mechanismus kümmern.

So und das ist alles um es Entwicklern einfacher zu machen und oft überflüssige Vererbung einfach zu vermeiden, dass dies nicht immer möglich ist klar, dass ist es auch bei Magento und Shopware nicht.

Gruß Daniel

[QUOTE=jkrug;121821]er Macher wohl das selbe Problem hatte.

Dem kann man nur durch kleine Methoden begegnen. Oder?

Gruß Joscha[/QUOTE]

Wie meinst du das bitte genau?

Also ich glaube nicht dass es einen Unterschied macht, ich bin sogar der Meinung die Observer Geschichte würde den Core schlechter lesbar machen. Methoden wie finalizeOrder müssen einfach noch weiter aufgebrochen werden.

Im Endeffekt ist es doch egal ob ich per Modul eine Methode überschreibe oder das per Event abfange und mich da einhänge. Damit dieses System wirklich Sinn macht müssten ja alle Core Methode um das dispatching erweitert werden - da wird aus einer Method wie:

public function setSkipAssign($blSkipAssign)
    {
        $this->_blSkipAssign = $blSkipAssign;
    }

ganz schnell mal:

public function setSkipAssign($blSkipAssign)
    {
        oxDispatcher::dispatch('beforeSetSkipAssign',$this);
        $this->_blSkipAssign = $blSkipAssign;
        oxDispatcher::dispatch('afterSetSkipAssign',$this);
    }

ich weiß das ist ein blödes Beispiel - aber ich denke man sieht worauf das hinausläuft. Mehr Code und mehr machen kann man auch nicht.

Dieses Observer pattern macht Sinn für riesige Methoden die sich einfach nicht mehr aufstückeln lassen (was ich mir nicht vorstellen kann).

Diese Eventgeschichte macht schon Sinn, klar am Anfang ist viel Arbeit das rein zu implementieren, aber hinten hinaus bringt es schon was.

Und wiegesagt OOP spricht von wiederverwendung, guter Erweiterbarket. Code zu duplizieren um ans Ziel zu kommen ist das nicht grad das gelbe vom Ei.

Aber dennoch müssten solche riesen Methoden aufgebrochen werden, dass stimmt allerdings.

Nun ich finde den Oxidshop gerade in seiner Geschwindigkeit überragend […]

Ich denke, das spricht für sich und hat auch seinen Grund. :wink:

Es gibt sicher viele Varianten ein Shopsystem umzusetzen und die verfügbaren Lösungen zeigen uns klar auf, daß jedes System seine Vor- und Nachteile hat.

Bisher habe ich zwar nur kleinere Module entwickelt für xt-basierte Shop-varianten und OXID, aber mir gefällt die Struktur von OXID bisher am besten.

Ich persönlich finde die OXID-Variante sehr gut gelungen und fühle mich eher bei Magento wie im Labyrinth. Aber das sind persönliche Auffassungen und sollen keine Bewertung darstellen.

Am Ende zählt ja auch die Stabilität und Geschwindigkeit, wie Du ja auch schon angesprochen hast.
Und da ist OXID ganz weit vorne, finde ich!
:slight_smile:

Hallo,

genau so eine Diskussion möchte ich gern auf der Unconference erleben. Kommt Ihr?

Gruß

[QUOTE=Daniel_Seelig;121879]Wie meinst du das bitte genau?[/QUOTE]

[QUOTE=Daniel_Seelig;121889]
Aber dennoch müssten solche riesen Methoden aufgebrochen werden, dass stimmt allerdings.[/QUOTE]

genau das meinte Joscha :slight_smile:

[QUOTE=Marco Steinhaeuser;121901]Hallo,

genau so eine Diskussion möchte ich gern auf der Unconference erleben. Kommt Ihr?

Gruß[/QUOTE]

nein, dieses jahr generell keine commons - wir bauen da an was großem.

[QUOTE=Daniel_Seelig;121878]
Ein ganz einfaches Besipiel in der Methode finalizeOrder des oxorder Models. Du willst als Module Entwickler die Möglichkeit haben Funktionalität vor oder nach dem speichern eines Orders einzubringen, momentan muss man nun erben und den Code auch noch duplizieren weil man einfach nicht rein kommt.[/QUOTE]
Dazu kann man die save-Methode überschreiben und muss nichts duplizieren. Ich kenne aber das Problem dass man Code kopieren muss weil man mitten im Code eingreifen muss, und Events wären hier eventuell eine gute Lösung, die Frage ist in wieviel Prozent der Fälle sich an der entsprechenden Stelle tatsächlich ein Event befinden würde, der einem weiterhilft. Prinzipiell ist die Idee ja kompatibel zum bisherigen System, also wäre es sicher eine Überlegung wert, aber eben auch eine Menge Arbeit die richtigen Stellen zu definieren an denen ein Event stehen sollte. Eventuell findet man dann auch, dass die Methode sowieso refactored werden sollte.

Ich glaube auch immer noch dass Events bei Magento sehr viel notwendiger sind weil 2 Module die die gleiche Methode überschreiben bei Magento nicht funktionieren, bei Oxid schon.