Service Decorator inkompatibel zwischen Legacy und neuer Plugin Struktur (am Beispiel SwagPromotion)

Hi,

mir ist beim Testen mit einigen eigenen Plugins und dem SwagPromotion Plugin (in Version 1.x und 2.x) von @Shopware aufgefallen, dass die Arten und Weisen Service Decorator zu definieren in der Legacy Plugin Struktur und der neuen nicht miteinander kompatibel sind.

tl;dr;

Die Service-Decorator welche über die services.xml in der neuen Plugin-Struktur definiert werden, überschreiben die alte Service ID, und somit können sich die Legacy Plugins nicht mehr auf diesen registrieren.

tl;dr;

Szenario 1

Plugin A (ein Legacy Plugin unter engine/Shopware/Plugins) registriert einen Service Decorator wie folgt

public function install()
    {

        $this->subscribeEvent(
            'Enlight_Bootstrap_AfterInitResource_shopware_storefront.list_product_service',
            'decorateService'
        );

        return true;
    }

    public function afterInit()
    {
        $this->get('Loader')->registerNamespace('LegacyDecoratorOne', $this->Path());
    }

    public function decorateService()
    {
        $coreService = Shopware()->Container()->get('shopware_storefront.list_product_service');
        $listProductService = new \LegacyDecoratorOne\StoreFrontBundle\ListProductService($coreService);
        Shopware()->Container()->set('shopware_storefront.list_product_service', $listProductService);
    }

Plugin B (neues Plugin unter custom/plugins mit services.xml) registriert einen Service Decorator wie folgt

So auch geschehen bei einem alten Plugin welches schon länger existiert als Shopware 5.2 und dem SwagPromotion Plugin in Version 2.x. 

Resultat: Mit der Registrierung von Plugin B wird der Enlight_Bootstrap_AfterInitResource_shopware_storefront.list_product_service Event nicht mehr getriggert, da jetzt nur noch die Service ID custom.one.list_product_service_decorator initialisiert wird. Plugin A muss hier jetzt also hingehen und statt den shopware_storefront.list_product_service zu erweitern, sich auf Enlight_Bootstrap_AfterInitResource_custom.one.list_product_service_decorator registrieren, damit es weiterhin ausgeführt wird. Kann im Einzelfall gehen, aber im Falle eines Community Plugins ist das nicht tragbar.

Szenario 2

Plugin A (altes Plugin)

Plugin B (ebenfalls altes Plugin)

so geschehen mit dem selben Plugin A aus Szenario 1 und dem SwagPromotion Plugin Version 1.x. Hier wird der Service Decorator von SwagPromotion, da alte Plugin Struktur, ebenfalls über die Bootstrap registriert wie bei Plugin A.

Resultat: Beide harmonieren wie gewünscht und werden nacheinander ausgeführt.

Szenario 3

Plugin A (neues Plugin)

Plugin B (neues Plugin)

so geschehen mit einem neuen Testplugin und dem SwagPromotion Plugin Version 2.x. Beide Decorator werden über die services.xml registriert

Resultat: Beide werden wie gewünscht nacheinander ausgeführt. Einzige Problematik welche ich hier feststellen konnte, ist eine extrem hohe Speicherauslastung, wenn beiden Decorators noch diverse andere Services via Dependency Injection übergeben werden.

Hier wäre eine Lösung wünschenswert, da es doch jede Menge Plugins gibt welche noch auf der alten Plugin Struktur beruhen aber für Shopware 5.2+ freigegeben sind, und sich so mit Plugins der neuen Plugin Struktur in die Quere kommen. Sollte es hierfür keine Lösung geben, müssten theoretisch sämmtliche Plugins ab 5.3 (Stichwort AjaxListing) auf der neuen Plugin Struktur aufbauen um diesem Problem aus dem Weg zu gehen.

Wer sich davon gerne selbst ein Bild machen möchte, ich habe auf github mal 4 Dummy Plugins erstellt, welche die Szenarien 1-3 aufzeigen (mit Ausnahme der Speicheraufwendung)

Für Vorschläge oder Lösungen bin ich jederzeit offen und würde mich darüber freuen.

Beste Grüße

Daniel

Push - Issue Link Shopware Issuetracker

Hey,

cooler Fund. Ich denke kaum das, das Thema angegangen wird da es etwas komplizierter ist, und ein Upgrade auf das neue Pluginsystem es behebt.

Ich habe gerade kurz versucht anhand einer Decoration Map zu lösen (Parameter mit neue $id => $orginal Id, um das richtige Event aufzurufen) und mir ist alles explodiert wenn zwei Plugins das selbe dekorieren. 

@Shyim schrieb:

Hey,

cooler Fund. Ich denke kaum das, das Thema angegangen wird da es etwas komplizierter ist, und ein Upgrade auf das neue Pluginsystem es behebt.

Ich habe gerade kurz versucht anhand einer Decoration Map zu lösen (Parameter mit neue $id => $orginal Id, um das richtige Event aufzurufen) und mir ist alles explodiert wenn zwei Plugins das selbe dekorieren. 

Mag sein, dass es komplizierter ist. Nur leider basieren noch die meisten Plugins auf der alten Struktur. Das wird sich wahrscheinlich auch nicht so schnell ändern (bei den kleinen Plugin Herstellern). Daher wäre eine Lösung seitens @Shopware von Vorteil. 

@derwunner schrieb:

Mag sein, dass es komplizierter ist. Nur leider basieren noch die meisten Plugins auf der alten Struktur. Das wird sich wahrscheinlich auch nicht so schnell ändern (bei den kleinen Plugin Herstellern). Daher wäre eine Lösung seitens @Shopware von Vorteil. 

Ich bin deiner Meinung und würde mich über eine Lösung von Shopware freuen!

Hallo,

die entscheidende Stelle ist doch in der Datei 

engine/Shopware/Components/DependencyInjection/Container.php

Hier werden in der Funktion doLoad die legacy Events (InitResource und AfterInitResource) nicht mehr geworfen.

Ich habe die Funktion einmal angepasst, damit auch die Legacy Events berücksichtigt werden.

Beim Beispiel von Daniel werden nun alle vier var_dumps ausgegeben.

Allerdings gibt es dann eine feste Reihenfolge.

In meinem Fall werden erst alle Legacy Decorator und dannach alle Decorator des neuen Plugin Systems aufgerufen.

Über Eure Meinung und Einschätzung bin ich gespannt.

private function doLoad($id, $invalidBehavior = self::NULL_ON_INVALID_REFERENCE)
    {
        $eventManager = parent::get('events');

        $legacyId = array_search ($id, $this->aliases);

        if (!empty($legacyId)) {
            /** @var \Enlight_Event_EventArgs|null $event */
            $legacyEvent = $eventManager->notifyUntil(
                'Enlight_Bootstrap_InitResource_' . $legacyId,
                ['subject' => $this]
            );
        }

        /** @var \Enlight_Event_EventArgs|null $event */
        $event = $eventManager->notifyUntil(
            'Enlight_Bootstrap_InitResource_' . $id,
            ['subject' => $this]
        );

        $circularReference = false;

        try {
            if ($event) {
                $this->services[$id] = $event->getReturn();
            } else {

                if ($legacyEvent) {
                  $this->services[$id] = $legacyEvent->getReturn();
                } else {
                  $this->services[$id] = parent::get($id, $invalidBehavior);
                }

            }
        } catch (ServiceCircularReferenceException $e) {
            $circularReference = true;
            throw $e;
        } finally {
            if ($circularReference === false) {

                if (!empty($legacyId)) {
                    $eventManager->notify(
                        'Enlight_Bootstrap_AfterInitResource_' . $legacyId, ['subject' => $this]
                    );
                }

                $eventManager->notify(
                    'Enlight_Bootstrap_AfterInitResource_' . $id, ['subject' => $this]
                );
            }
        }

        return $this->services[$id];
    }

Eventuell gibt es hier ja noch Seiteneffekte, die ich so nicht bedacht habe.

 

Hi Deluxe,

das sieht ja auf den ersten Blick schonmal nach einer vielversprechenden Lösung aus, muss mir das auch mal mit produktiven Decorators anschauen, um eventuelle Seiteneffekte feststellen zu können.

Danke auf jeden Fall schon mal für Deine Mühen.

Gruß

Daniel

Das Problem besteht sogar zwischen Shopware eigenen Plugins! Daher hoffe ich auf eine baldige Lösung von Shopware.

  • Liveshopping aktuelle Version. Alte Plugin Struktur.
  • Promotion Suite aktuelle Version. Neue Plugin Struktur.

Mit aktivem Promotion Suite Plugin bekomme ich auf der Detailseite keine Liveshopping Informationen angezeigt.
Beide dekorieren ListProductService. 

Ich muss dazu sagen, der Shop ist SW 5.3.4.
Ab 5.3.5 gibts das Liveshopping Plugin in der neuen Struktur. Leider kann der Shop im Moment nicht geupdatet werden.

 

 

Ich bin mal so frei das Thema wieder auf den Tisch zu holen :slight_smile: