Pluginstruktur 5.2 - Konfiguration in LESS-Variable speichern pro Subshop

Hello, 

ich möchte in einem Plugin nach der neuen Pluginstruktur Less-Variablen aus der Konfiguration holen. Um die Events etwas zu strukturieren habe ich mir daher Subscriber angelegt und befinde mich daher in einer class Subscriber ``implements SubscriberInterface.

Mit dem unteren Code kann ich mir die Konfiguration in LESS-Variablen sammeln. Schön und gut. Nun möchte ich jedoch die Konfiguration je Subshop / Multishop separat speichern.  

public function onCollectLessFiles(\Enlight_Event_EventArgs $args)
    {
        $less = new LessDefinition(
        	//configuration
            [
                'less-variablenname' => Shopware()->Config()->get('less-config-element')
            ],

            //less files to compile
            [__DIR__. '/../Resources/Views/frontend/_public/src/less/all.less'],
            
            //import directory
            __DIR__
        );
        return new ArrayCollection([$less]);
    }

 

In der alten Plugin-Bootstrap hätte ich mir zunächst eine Instanz des Shops geholt. 

public function addLessFiles(Enlight_Event_EventArgs $args)
    {
        $shop = $args->getShop();
        $config = $this->collection->getConfig($this->name,$shop);
 
        $less = new LessDefinition(
        	//configuration
            array(
                'less-variablenname' => $config->get('less-config-element'),              
            ),

            //less files to compile
            array(
                __DIR__. '/Views/frontend/_public/src/less/all.less'
            ),

            //import directory
            __DIR__
        );
        return new ArrayCollection([$less]);
    }

 

In dem Subscriber bekomme ich hiermit beim Kompilieren einen JSON Fehler. 

Wie macht man das also in der neuen Plugin-Struktur + in einem Subscriber?

Du musst dir den config Service per Dependency Injection in deine Subscriber Klasse reinreichen.

https://developers.shopware.com/developers-guide/plugin-system/#event-subscriber

Du musst also den config Service in der services.xml angeben. Wie im Beispiel statt dem slogan Service. Der config Service geht einfach dumm „config“ ;) 

1 Like

Hi @arnebecker‍

vielen Dank für deine Antwort.

Meinst du quasi den Tag 

In der Liste kann ich keinen mit config finden: Dependency Injection Tags

Ups. Sorry. Der Sevice heißt auch “shopware.plugin.cached_config_reader” und nicht “config”. Die Liste ist schlecht und einfach nicht richtig gepflegt und unvollständig.

Ich meinte nicht sondern:

$this->name setzt du vorher irgendwo?

Hi @arnebecker‍

danke für deine Hilfe. Ich habe es nun folgendermaßen umgesetzt:

public function onCollectLessFiles(\Enlight_Event_EventArgs $args)
	{
		$shop = Shopware()->Shop();
		$configReader = Shopware()->Container()->get('shopware.plugin.config_reader');
		$pluginConfig = $configReader->getByPluginName('pluginname', $shop);

		$less = new LessDefinition(
			[
				'lessVariable' => $pluginConfig['configLessWert']
			],
			[__DIR__. '/../Resources/Views/frontend/_public/src/less/all.less']
		);
		return new ArrayCollection([$less]);
	}

 

Im Service habe ich auch 

eingefügt. Es kommt nun keine Fehlermeldung mehr und die Variablen sind in LESS vorhanden. Allerdings frage ich mich, ob es notwendig ist wegen:

$configReader = Shopware()->Container()->get('shopware.plugin.config_reader');

 

Könnte ich hier nun auch problemlos den shopware.plugin. cached _config_reader verwenden? Soll ja performanter sein, aber was wären die Nachteile? Man muss den Cache leeren? Muss man das nicht eh?

Auch konnte ich es noch nicht testen, da ich unter vagrant keinen Multishop habe -.- Sieht das nun so korrekt aus?

 

$configReader = Shopware()->Container()->get('shopware.plugin.config_reader');

Ne, wenn du das so machst ist die Dependency Injection nicht notwendig. Allerdings sollte man vermeiden das globale Shopware() Objekt zu benutzen. Es ist schlechter Programmierstil globale Objekte zu nutzen. Das verhindert, das du irgendwann mal Tests für deine Software schreiben könntest. Also es funktioniert, ist aber schlechter Stil :wink:

Du kannst in deiner Zeile auch den cached_config_reader nutzen. Der cached Reader speichert Konfig Einträge die du aufgerufen hast im Arbeitsspeicher. Wenn du also auf einen Konfig Wert öfter in deinem Request zugreifst, und ihn dir nicht selber in einer Variablen speicherst, spart man sich ein Paar Datenbank Abfragen. Ist aber kein großes Ding. Nachteile sind zu vernachlässigen. Den Cache leeren muss man auch nicht.

Sieht für mich korrekt aus.

1 Like

Hi @arnebecker‍

Den Zugriff auf das Shopware-Objekt mache ich, weil es so in der Doku stand und in einem Subscriber $this->container nicht funktioniert. Wie wäre es sauberer?

 

Siehe: https://developers.shopware.com/developers-guide/plugin-system/#plugin-configuration-/-forms

Also da gibt es auch mehere Ansätze. Du kannst einfach den ganzen Service Container in deine Subscriber Klasse reinreichen. Das ist bequem und du hast keine Probleme. Nachteil ist, dass man nicht wirklich auf den ersten Blick sehen kann, von welchen anderen Services dein Service abhängig ist (die Subscriber Klasse ist auch ein Service). Das mag erstmal kein Problem sein, schränkt aber die Testbarkeit deines Services ein. Wenn du nicht weist welche Services du nutzt, weist du auch nicht welche du in deinen Tests mocken musst. Dies bezieht sich jetzt aber jetzt nur auf die Testbarkeit. Da bei Shopware sowieso so gut wie niemand Tests schreibt ist das aber auch zu vernachlässigen. Falls du deinen Programmierstil weiter entwickeln willst, wäre es aber schon gut sich daran zu orientieren :wink:

Bequem - service.xml

1plus mit Sternchen - service.xml 

  %dein_plugin.plugin_name%

hier nur dein_plugin ersetzen. “.plugin_name%” soll wirklich so da stehen.

Bequem - constructor deine Subscriber Klasse

container = $container;
    }
}

Hier kannst du jetzt mit $this->container->get() auf sämtliche Services zugreifen.

1plus mit Sternchen - constructor deine Subscriber Klasse

pluginName = $pluginName;
        $this->configService = $configService;
        $this->shopService = $shopService;

        $this-> pluginConfig = $this->configService->getByPluginName($this->pluginName, $this->shopService);
    }
}

Hier hast du jetzt nur die Services reingereicht, die ein eigener Service nutzt. Das ist sehr explizit und man weiß wovon dein Service anhängt und welche Services man testen mocken muss.

HTH, Arne

PS: Der Abschnitt aus der Doku stammt soagr von mir. Ich hatte die globale Abhängigkeit dort auch rausgelöscht. Aber Shopware wollte es lieber, dass es für die Kompatibilität drin bleibt.

2 Likes

Hi @arnebecker‍

 

bei deiner 1+ Sterchen Lösung kommt leider eine Fehlermeldung im Backend:

deinPlugin\Subscriber\Subscriber::__construct() must be an instance of Shopware\Models\Shop\Shop, none given, called in /home/vagrant/www/sw-plugins/var/cache/production____REVISION___/proxies/

 

pluginName = $pluginName;
		$this->configService = $configService;
		$this->shopService = $shopService;

		$this->pluginConfig = $this->configService->getByPluginName($this->pluginName, $this->shopService);
	}

	public static function getSubscribedEvents()
	{
		return [
			'Enlight_Controller_Action_PostDispatchSecure_Frontend' => 'extendsFrontend',
			'Theme_Compiler_Collect_Plugin_Less' => 'onCollectLessFiles',
			'Theme_Compiler_Collect_Plugin_Javascript' => 'onCollectJsFiles'
		];
	}

	/**
	 * Provide the needed javascript files
	 *
	 * @return ArrayCollection
	 */
	public function onCollectJsFiles()
	{
		$jsFiles = array(
			__DIR__. '/../Resources/Views/frontend/_public/src/js/jquery.beispiel.js',
			__DIR__. '/../Resources/Views/frontend/_public/src/js/jquery.beispiel2.js',
		);
		return new ArrayCollection($jsFiles);
	}

	/**
	 * @return ArrayCollection
	 */
	public function onCollectLessFiles(Enlight_Event_EventArgs $args)
	{
// $shop = Shopware()->Shop();
// $configReader = $this->container->get('shopware.plugin.config_reader');
// $pluginConfig = $configReader->getByPluginName('deinPlugin', $shop);

		$less = new LessDefinition(
			[
				'beispiel' => $this->pluginConfig['beispiel']
			],
			[__DIR__. '/../Resources/Views/frontend/_public/src/less/all.less']
		);
		return new ArrayCollection([$less]);
	}


	public function extendsFrontend(Enlight_Event_EventArgs $args)
	{
		/** @var \Enlight_Controller_Action $controller */
		$controller = $args->getSubject();
		$view = $controller->View();

		$view->addTemplateDir( __DIR__. '/../Resources/Views');

		$configReader = Shopware()->Container()->get('shopware.plugin.config_reader');
		$pluginConfig = $configReader->getByPluginName('deinPlugin');

		$config = array(
			'beispiel' => $pluginConfig['beispiel']
		);

		$view->assign('deinPlugin', $config);

	}
}

Edit: Bezüglich der xml bekomme ich auch einen Fehler: 

Fatal error: Uncaught Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The service "dein_plugin.subscriber.subscriber" has a dependency on a non-existent service "shop"

 

habe ich dann weggelassen - dann gehts

 

Edit 2: 

Uncaught TypeError: Argument 1 passed to deinPlugin\Subscriber\Subscriber::__construct() must implement interface Symfony\Component\DependencyInjection\ContainerInterface

%deinPlugin.plugin_name%

habe ich dann ebenfalls weggelassen - dann gehts

zu Edit:

Sorry. shop gibt es tatsächlich nicht als Service. Mein Fehler. Der Servide heißt shopware_storefront.context_service und den Shop bekommt men durch getShopContext()->getShop(); Ich hab mein Beispiel oben angepasst.

zu Edit 2:

Hm das wundert mich etwas. Hast du vorher die bequeme Lösung probiert und vielleicht vergessen den Cache zu leeren? Hab die Erfahrung gemacht, dass man nach Änderungen an der service.xml den Cache leeren muss.

@arnebecker‍

hi, ich habe diesen Thread hier gefunden, weil ich Probleme mit meinem __construct habe. (siehe https://forum.shopware.com/discussion/54120/sw-5-5-0-beta-you-have-requested-a-non-existent-service-shop#latest

Leider kann ich deinen 1+ mit Sternchen weg nicht nachvollziehen, weil die Probleme mit dem Service Shop und getShopContext() nicht aktualisiert wurden… kannst Du mir hier vll. weiterhelfen?

Konkret habe ich Probleme den Subshop zu bekommen.