Pluginübergreifend Klassen referenzieren

Hallo Zusammen Ich entwickle eine Extension, die Preisregeln abbildet (PriceRules). Diese Preisregeln referenzieren Shopware Categorien sowie Unternehmen von einer zweiten eigenen Extension (CorporateStructure). [code]<?php namespace Shopware\CustomModels\PriceRules;
use Shopware\Components\Model\ModelEntity,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\ORM\Mapping AS ORM;

/**

  • @ORM\Entity
  • @ORM\Table(name=„s_pricerules_rule“)
    */
    class Rule extends ModelEntity {

/**

  • @var \Doctrine\Common\Collections\ArrayCollection
  • @ORM\ManyToMany(targetEntity=„Shopware\Models\Category\Category“)
  • @ORM\JoinTable(name=„s_pricerules_rules_categories“,
  • joinColumns={@ORM\JoinColumn(name=„rule_id“, referencedColumnName=„id“)},
  • inverseJoinColumns={@ORM\JoinColumn(name=„category_id“, referencedColumnName=„id“)}
  • )
    */
    protected $categories;
    public function setCategories(\Doctrine\Common\Collections\ArrayCollection $categories) { $this->categories = $categories; } public function getCategories() { return $this->categories; } public function addCategory(\Shopware\Models\Category\Category $category) { $this->categories->add($category); } public function removeCategory(Shopware\Models\Category\Category $category) { $this->categories->removeElement($category); } /** * @var \Shopware\CustomModels\CorporateStructure\Corporation * @ORM\ManyToOne(targetEntity="\Shopware\CustomModels\CorporateStructure\Corporation") * @ORM\JoinColumn(name=„corporation_id“, referencedColumnName=„id“) **/ private $corporation; public function setCorporation(\Shopware\CustomModels\CorporateStructure\Corporation $corporation) { $this->corporation = $corporation; } public function getCorporation() { return $this->corporation; } } [/code] Sowohl die Referenz zu den Kategorien wie auch weitere Referenzen innerhalb des Namespaces funktionieren bereits. Nur das Anlegen einer Referenz zu einer Klasse im andern Plugin (CorporateStructure) macht Probleme. Die Installation bricht mit der folgenden Fehlermeldung ab: Plugin Price Rules could not be installed The target-entity Shopware\CustomModels\CorporateStructure\Corporation cannot be found in ‚Shopware\CustomModels\PriceRules\Rule#corporation‘. Im Bootstrap hab ich registerCustomModels() drin im afterInit. Ich hab zwar herausgefunden, das ich durch Anlegen einer Instanz des andern Plugins im Bootstrap das Problem lösen kann doch es taucht dann einfach beim Controller wieder auf. Wie kann ich Shopware sagen, das er das CorporateStructure-Plugin immer laden soll, wenn das PriceRules Plugin aktiv ist, damit deren Models verfügbar sind? Gruss aus der Schweiz

Ich antworte mir mal selbst: Scheinbar ist es nicht möglich, zentral zu definieren, welche andern Plugins referenziert und geladen werden müssen. Entsprechend muss für jede Klasse das externe Plugin registriert und instanziiert werden. Um diese Logik nicht über die ganze Extension zu verstreuen, habe ich dafür Events eingesetzt: Beliebige Model Klasse: [code]<?php namespace Shopware\CustomModels\MyExtension;
use Shopware\Components\Model\ModelEntity,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\ORM\Mapping AS ORM;

/**

  • @ORM\Entity
  • @ORM\Table(name=“s_myextension_rule”)
    */
    class Rule extends ModelEntity {

    public function __construct() {
    // Class path used as event name → unique identifiable Enlight()->Events()->notify(‘Shopware\CustomModels\MyExtension\Rule::onConstruct’, array()); } … } [/code] Bootstrap class Shopware\_Plugins\_Backend\_MyExtension\_Bootstrap extends Shopware\_Components\_Plugin\_Bootstrap { ... private function registerEventsAndHooks() { // controller predispatch event $this-\>subscribeEvent('Enlight\_Controller\_Action\_PreDispatch\_Backend\_MyExtension', 'loadNamespaces'); // model class rule instantiation event $this-\>subscribeEvent('Shopware\\CustomModels\\MyExtension\\Rule::onConstruct','loadNamespaces'); // service class instantiation event $this-\>subscribeEvent('Shopware\\Plugins\\Backend\\MyExtension\\Service\\DiscountCalculator::onConstruct', 'loadNamespaces'); } ... public function loadNamespaces() { // load service namespace $this-\>Application()-\>Loader()-\>registerNamespace('Shopware\\Plugins\\Backend\\MyExtension\\Service', $this-\>Path().'Service/'); // load foreign plugin namespace $this-\>Application()-\>Loader()-\>registerNamespace('Shopware\\CustomModels\\CorporateStructure', $this-\>Path()); // instantiate foreign plugin -\> without instance shopware won't find the referenced classes $corporateStructure = Shopware()-\>Plugins()-\>Backend()-\>CorporateStructure(); } ... private function createDatabaseSchema() { // load classes befor creating db tables -\> doctrine needs foreign models to create foreign key columns $this-\>loadNamespaces(); $this-\>registerCustomModels(); $modelManager = $this-\>Application()-\>Models(); $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($modelManager); try { $schemaTool-\>createSchema($this-\>getModelMetadata($modelManager)); } catch (\Doctrine\ORM\Tools\ToolsException $e) { Shopware()-\>Debuglogger()-\>info(json\_encode($e-\>getMessage())); } } ... public function removeDatabaseTables() { $this-\>loadNamespaces(); $modelManager = $this-\>Application()-\>Models(); $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($modelManager); ... } ... public function afterInit() { // Shopware -\> load models $this-\>registerCustomModels(); // load namespaces every time bootstrap is instantiated $this-\>loadNamespaces(); } ... } Kennt jemand noch eine einfachere Lösung?

Ich hänge gerade an der gleichen Sache. Gibt es keine einfachere Lösung? Vielleicht könnte sich ja jemand von Shopware dazu äußern. :sunglasses: Gruß Marco

Hi, grundsätzlich gibt es dazu keine fertige Lösung. Der registerNamespace-Ansatz ist aber schon richtig: &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; $this-\>Application()-\>Loader()-\>registerNamespace('Shopware\\CustomModels\\CorporateStructure', $this-\>Path()); Um auf diese Weise die CustomModels eines anderen Moduls zu registrieren, müsst ihr statt $this->Path() aber den Pfad des anderen Plugins angeben. Dafür gibt es noch keinen schönen Helfer - in der s_core_plugins findet ihr aber die Spalten namespace für die Unterscheidung Frontend / Core / Backend und die Spalte source für die Unterscheidung Local / Default / Community. Mit den Informationen solltet ihr euch einen validen Pfad erzeugen und registrieren können. Damit sollten die Models abgefrühstückt sein. Für das „Teilen“ von Ressourcen / Services unter verschiedenen Plugins würde ich das Shopware Resource-System verwenden - d.h. wenn ihr euren Service „MyFancyService“ global verfügbar machen wollt, registriert ihr euch das Event $this-\>subscribeEvent( 'Enlight\_Bootstrap\_InitResource\_MyFancyService', 'onInitMyComponent' ); und gibt im Callback eine Instanz eurer Klasse zurück. Dann können alle Plugins über Shopware()->MyFancyResource oder über den Container Shopware()->Container()->get(‚myfancyservice‘) darauf zugreifen. lG Daniel

1 Like

Hallo Daniel, vielen Dank für die Antwort. Leider stehe ich etwas auf dem Schlauch wie du das meinst. Sagen wir, das eine Plugin heißt MyPluginVideo und das andere MyPluginAudio. In MyPluginAudio möchte ich das CustomModel Video von MyPluginVideo nutzen. Muss ich jetzt beide Plugins anpassen, oder nur MyPluginAudio wo ich das fremde CustomModel nutzen möchte? Ein fertiges Beispiel wäre super. Das subscribeEvent verstehe ich noch, aber beim Inhalt der Methode und anschließenden Verwendung im CustomModel von MyPluginAudio hört es auf. Danke und Gruß Marco

Hallo, Du musst soweit ich jetzt weiß nur ein [color=black]Addon[/color] abändern. Ich wüsste jetzt nicht gegenteiliges. Dann sollte es auch klappen. Vielleicht kann dir ja jemand ein fertiges Beispiel senden. Grüße

Du musst quasi nur dafür sorgen, dass die init() Methode innerhalb der bootstrap des zweiten Plugins geladen wird. Du kannst zb einfach eine fake Komponente laden, die das zweite Plugin bereit stellt. Viele Grüße

Vielen Dank für die Antworten :thumbup: [quote=“Aquatuning GmbH”]Du kannst zb einfach eine fake Komponente laden, die das zweite Plugin bereit stellt.[/quote] Kannst du mir hierfür einen Codeschnipsel geben? Ich weiß nicht, was du mit einer fake Komponente meinst. Danke und Gruß Marco

Ungetestet: public function onInitResource( Enlight\_Event\_EventArgs $arguments ) { $component = new stdClass(); return $component; } Nichts besonders sinnvoll - aber sollte funktionieren. Viele Grüße

[quote=“Aquatuning GmbH”]Du musst quasi nur dafür sorgen, dass die init() Methode innerhalb der bootstrap des zweiten Plugins geladen wird. Viele Grüße[/quote] Hi, wie oben beschrieben sollte es in der Regel reichen, über den Loader den Namespace des Models des anderen Plugins zu registrieren. Da nicht sichergestellt ist, dass das andere Plugin die Models in der init Methode registriert, ist es nicht zielführend, einfach nur dessen init-Methode aufzurufen. lG Daniel

Hallo, vielen Dank die Antworten. Ich habe das Problem mal mit phpStorm und Xdebug angesehen. Es lag an der Annotation für Doctrine. Xdebug ist hier wirklich Gold wert. Ich habe die vorheriege Initialisierung wieder gelöscht und es geht auch ohne bei mir. Wollte nur mal Feedback geben. Danke und Gruß Marco