Leider finde ich nirgends eine Erklärung dazu, weder in der Symfony noch Shopware Dokumentation.
Wenn ich per $this->decoratedService->… auf eine private Method zugriffen möchte, dann bekomme ich folgende Fehlermeldung:
Call to private method
Ist irgendwie naheliegend. Heißt das im Umkehrschluss, dass ich alle privaten Methoden eines decorated Services im ServiceDecorator vollständig übernehmen muss?
Hi Max, du darfst/kannst nur Public Methoden von außen aufrufen. Die internen Methoden werden vor dir versteckt und sind nach außen nicht sichtbar. Demzufolge brauchst du die private Methoden nicht implementieren.
Wenn ich eine public method dekoriere, diese intern auf eine private methode zeigt, dann muss ich diese implementieren, sonst bekomme ich den oben genannten Fehler.
Oder mache ich da etwas falsch?
Gibt es hierzu mittlerweile eine Lösung?
Ich dekoriere MailService
und versuche den Header und Footer aus allen E-Mail-Templates zu entfernen. Wir nutzen ein Plugin zum visuellen Erstellen von Mail-Templates, das selbige ebenfalls hinzufügt – sie werden also doppelt ausgegeben.
Header und Footer werden in der privaten Methode buildContents
an den Inhalt der Mail gehängt: shopware/src/Core/Content/Mail/Service/MailService.php at 3e8a794d242f9fc3a4b2d3dfb185ca20662006b8 · shopware/shopware · GitHub
Wie passe ich dieses Verhalten jetzt an?
Private Methoden sind mit Absicht privat, du musst dazu zusätzlich die public Methode überschreiben die das aufruft und die private mit kopieren.
Das ist nicht schön aber Fakt, das deutet im Umkehrschluss auch darauf hin dass die Logik dieser private Methoden in einen weiteren Service ausgelagert gehört.
Ich hatte damals den anderen Fall. Ich habe eine public Methode dekoriert. In dieser wurde aber eine private Methode aufgerufen, was aus meiner dekorierten Methode heraus nicht möglich war. Daher musste ich die Klasse erweitern.
Kann leider sehr lästig sein, hatte letztens eine Funktion, die kaskadiert durch vier private Funktionen aufgerufen wurde bis man zu einer public-Funktion kam, d.h. es mussten 5 private Funktionen kopiert werden, um eine Zeile zu ändern. Das kann doch auch nicht im Sinne des Erfinders sein.
1 „Gefällt mir“
Doch, denn Methoden sind bewusst als private deklariert. Manche Dinge soll man schließlich auch nicht machen - auch damit Shopware in bugfix releases Änderungen durchführen kann ohne dass diese breaking changes sind. Alternativ: über reflection kannst du auch private Methoden aufrufen.
Viele Grüße
1 „Gefällt mir“
Wäre aber nicht zumindest in manchen Fällen ein protected ausreichend? Bzw. was spricht aus Deiner Sicht dagegen? Denn private Funktionen zu kopieren wird ja am Ende doch wieder „breaking changes“ erzeugen.
Danke fürs Erinnern an die reflection.
Shopware (oder andere open source Softwareentwickler) verwenden (unter anderem) private Methoden damit du diese eben nicht nutzen kannst. Die Klasse hat ein klar definiertes interface und hier würde jede Änderung ein breaking change bedeuten. Indem Shopware intern private Methoden verwenden, kann Shopware diese internen Strukturen jederzeit anpassen, ändern, optimieren etc ohne sich darüber Sorgen machen zu müssen.
Viele Grüße
1 „Gefällt mir“
Dekorieren ist neu für mich und die Infos dazu sind wie immer dürftig.
Kann mir jemand ein Beispiel geben, wie ich jetzt Header und Footer aus den Mail-Templates bekomme? Möglichst ohne die halbe Klasse überschreiben zu müssen.
Ich brauche nur ein Praxis-Beispiel, um zu verstehen, wie der ganze Bums funktioniert.
Wenn du das Konzept verstehen willst, dann teste es mit einem service, bei dem du nur die interface Methoden überschreiben musst. Siehe auch: Adjusting a Service | Shopware Documentation
Viele Grüße
Danke für die Antwort. Leider beschreibt die Dokumentation wieder nur die Hälfte.
Dort wird die public-Methode des dekorierten Service aufgerufen, um dem Rückgabewert etwas hinzuzufügen, aber das bringt mir in diesem Fall nichts.
Ich will nicht den Rückgabewert erweitern, sondern den Inhalt anpassen, bevor dieser gesendet wird. Das passiert halt vor dem return
.
Nach allem was ich bisher an verschiedenen Stellen gelesen habe, müsste ich quasi den halben MailService ersetzen, um das zu erreichen was ich vor habe.
Da man weder private Methoden, noch public Methoden, die private Methoden aufrufen, ordentlich dekorieren kann (so ist mein Eindruck), müssten diese komplett übernommen werden. In der send-Methode werden auch private Eigenschaften verwendet – diese müsste ich ja auch irgendwie überschreiben.
Wenn ich so darüber nachdenke, ist nicht Dekorieren mein Ziel, sondern komplett den Service auszutauschen (vermutlich).
Fragen über Fragen.
Da man weder private Methoden, noch public Methoden, die private Methoden aufrufen, ordentlich dekorieren kann
Die sollst du auch nicht dekorieren - genau deswegen sind sie private.
Ich will nicht den Rückgabewert erweitern, sondern den Inhalt anpassen, bevor dieser gesendet wird. Das passiert halt vor dem return
.
Du kannst und sollst die Rückgabe der core Klasse anpassen und/oder überschreiben. Wenn das in diesem Fall nicht geht, dann wirst du dir andere Wege suchen müssen.
Viele Grüße
Ich glaube, das war auf meine Aussage bezogen. Ist schon eine Weile her, daher bin ich mir nicht mehr 100% sicher, aber …
Die MailService Klasse hat eine public function send(). Wenn ich MailService nun dekoriere und send() überschreiben möchte, dann wirft mir PHP ein Fehler, da send() bspw. die private function getValidationDefinition() abruft.
Und MailService ist gewiss eine Klasse, die dazu gedacht ist dekoriert zu werden. Ansonsten würde sie keine public function getDecorated() haben.
Wie gesagt, ist eine Weile her, aber ich glaube das war die Fehlerlogik, die PHP damals ausgegeben hat.
Die [MailService] Klasse hat eine public function send(). Wenn ich MailService nun dekoriere und send() überschreiben möchte, dann wirft mir PHP ein Fehler, da send() bspw. die private function getValidationDefinition() abruft.
Warum sollte das denn passieren?!
Und MailService ist gewiss eine Klasse, die dazu gedacht ist dekoriert zu werden. Ansonsten würde sie keine public function getDecorated() haben.
Ja definitiv. Du kannst/sollst nur die privaten Methoden nicht direkt nutzen.
Viele Grüße
Also ich hab das jetzt hinbekommen, indem ich den Service komplett überschrieben habe:
<service id="Shopware\Core\Content\Mail\Service\MailService " class="MyPlugin\Service\MailService">
...
</service>
Schade, dass es wohl nicht anders geht. Ich würde mir Filter-Hooks wie in WordPress wünschen.
So könnte man einzelne Variablen bequem überschreiben. Vielleicht ist so etwas mit einem Event Subscriber möglich – es gibt z.B. das Event MailBeforeSentEvent
.
Allerdings weiß ich nicht, wie man das bewerkstelligen könnte. Die Dokumentation beschreibt wieder nur, wie man einen solchen Subscriber erstellt – nicht was man alles damit machen kann.
In den Symfony Docs gibt es sogar ein Beispiel einer solchen Event-Klasse. Dort sind setter-Methoden definiert, um z.B. den Mail-Betreff zu ändern.
Leider gibt es keine setter-Methoden in der MailBeforeSentEvent
Klasse.
Shopware kann natürlich auch nicht jeden Anwendungsfall vorhersehen und auch nicht in jeder zweiten Zeile ein event schmeißen. Du kannst aber immer einen begründeten pull request erstellen und dann wird Shopware das mit hoher Wahrscheinlichkeit übernehmen.
Viele Grüße
Wenn in den Core-Services die privaten Methoden als protected deklariert wären, könnte im Beispiel von @art2media vom Core-Service geerbt und nur die eine public-Methode angepasst werden.
Für mein Empfinden wäre das der bessere Weg, dann könnte ich:
- dekorieren für einfache Anpassungen
- Service überschreiben und vom Core-Service erben, um public-Methoden einfacher anzupassen (ohne die private-Methoden mitkopieren zu müssen)
- Service komplett überschreiben
Wäre, wäre, Fahrradkette - ist halt so.
2 „Gefällt mir“