Vypnutí autowired pro sloužby načtené pomocí search

Sniclman
Člen | 15
+
0
-

Ahoj, potřeboval bych poradit jak vypnout autowired pro služby načtené pomocí search.
Potřebuji mít služby v DI kontejneru kvůli čtení ale nepotřebuji je nikde používat přes autowired.

Část mého konfigračního souboru:

search:
	# Pluginy
	plugins:
		in: %appDir%/Plugins/ # Hledám ve složce App/Plugins/
		extends: App\Model\Plugin\Plugin # Chci pouze třídy, které jsou rozšířené o abstraktní třídu App\Model\Plugin\Plugin
		classes:
			- *Plugin # Třídy v pluginech musí mít postfix Plugin
		files:
			- *Plugin.php # Samotný soubor musí mít postfix Plugin.php

Chtěl jsem použít decorator na abstraktní třídu App\Model\Plugin\Plugin ale decorator neumí konfigurovat parametr autowired.
Přes services se mě to také napodařilo, buď to nejde nebo nevím jak.

Budu rád za každou radu.

David Grudl
Nette Core | 8201
+
0
-

Můžeš vysvětlit, k čemu ti je taková služba bez autowiringu?

Sniclman
Člen | 15
+
0
-

David Grudl napsal(a):

Můžeš vysvětlit, k čemu ti je taková služba bez autowiringu?

Mám složky s pluginy. Každá složka s pluginem obsahuje základní soubor (třídu AbcdPlugin rozšířenou o abstraktní třídu Plugin) s konfigurací pluginu a případnými metodami, které se provádí při aktivaci/deaktivaci pluginu.
Většina metod je statických, obsahují například cesty k NEON konfiguraci, cestu k SQL dotazům (vytvoření/smazání tabulek), název pluginu, popis pluginu, a jiné…

Třídy rozšířené o abtraktní třídu Plugin nepotřebují být „autowired“, nikde se nepoužívají v konstruktoru/inject.

K samotné aktivaci pluginu se používá třída PluginProvider (která je autowired). Třída PluginProvider má například metodu getPlugins() která vrací všechny třídy pluginů (například AbcdPlugin) a čte je pomocí Container::findByType(Plugin::class).

V presenteru pak aktivace/deaktivace pluginu vypadá následovně:

	protected PluginProvider $pluginProvider;

	public function injectAdminDependencies(
		PluginProvider $pluginProvider
	): void
	{
		$this->pluginProvider = $pluginProvider;
	}

	public function actionTest(string $name): void
	{
			if (!$plugin = $this->pluginProvider->getByName($name)) {
				$this->redirect('this');
			}

			try {
				$this->pluginProvider->isActivated($plugin) ? $this->pluginProvider->deactivate($plugin) : $this->pluginProvider->activate($plugin);
				$this->flashMessage('item_changed', FlashType::Success);
			} catch (Throwable $th) {
				$this->flashMessage($th->getMessage(), FlashType::Danger);
			}
	}


Neexistuje situace kdy by bylo v presenteru například následující:

	public function __construct(protected AbcdPlugin $plugin)
	{
	}

nebo

	protected AbcdPlugin $plugin;

	public function injectPluginDI(AbcdPlugin $plugin)
	{
		$this->plugin = $plugin;
	}

Předchozí verze PluginProvider si načetla třídy rozšířené o třídu Plugin pomocí RobotLoader a uložilo do Nette/Cache. Ale toto původní řešení se mi tolik nelíbilo, proč dělat funkce pro hledání tříd. když už to umí samotné Nette.

Původní řešení pro získání všech tříd pluginů:

	/**
	 * @return array<string,Plugin>
	 */
	protected function getPlugins(): array
	{
		/** @var array<string,Plugin> $plugins */
		$plugins = (array) $this->cache->load('plugins', function (?array &$dependencies) {
			$folder = (string) Folder::create()->app->addPath('Plugins');
			// Nastaví hlídání cache pro vše ve složce App/Plugins
			$dependencies[Cache::Files] = [$folder];

			/** @var array<string,Plugin> $plugins */
			$plugins = [];

			$loader = new RobotLoader();
			$loader->addDirectory($folder);
			$loader->setTempDirectory((string) Folder::create()->cache);
			$loader->refresh();
			/** @var array<class-string> $classes */
			$classes = array_keys($loader->getIndexedClasses());

			foreach ($classes as $class) {
				$reflection = new ReflectionClass($class);

				if (!$reflection->isSubclassOf(Plugin::class)) {
					continue;
				}

				/** @var Plugin $plugin */
				$plugin = $reflection->newInstanceWithoutConstructor();
				$plugins[$plugin::getName()] = $plugin;
			}

			return $plugins;
		});

		return $plugins;
	}
David Grudl
Nette Core | 8201
+
+1
-

To je dost atypické použití. Na vypnutí autowiringu bude asi nejsnazší si vytvořit DI rozšíření

Sniclman
Člen | 15
+
0
-

David Grudl napsal(a):

To je dost atypické použití. Na vypnutí autowiringu bude asi nejsnazší si vytvořit DI rozšíření

Zkusil jsem vytvořit rozšíření s použitím SearchExtension. Pokud by existovalo čistší a jednodušší řešení byl bych rád za jakoukoliv radu :-).

SearchExtension bude přidáno do konfigurace hledání ve složce (viz předchozí konfigurace NEON). Následně se SearchExtension spustí.
Podobné téma: https://forum.nette.org/…lerextension

Hlavní konfigurační NEON se výrazně zjednodušil.

Původní

services:
	- App\Model\Plugin\PluginProvider

search:
	plugins:
		in: %appDir%/Plugins/
		extends: App\Model\Plugin\Plugin
		classes:
			- *Plugin
		files:
			- *Plugin.php

Nový

extensions:
	plugin: App\Model\Plugin\PluginExtension

plugin:
	dir: %appDir%/Plugins/

Samotné rozšíření:

<?php declare(strict_types = 1);

namespace App\Model\Plugin;

use Exception;
use Nette\DI\CompilerExtension;
use Nette\DI\Extensions\SearchExtension;
use Nette\Schema\Expect;
use Nette\Schema\Processor;
use Nette\Schema\Schema;

final class PluginExtension extends CompilerExtension
{

	public function getConfigSchema(): Schema
	{
		return Expect::structure([
			'dir' => Expect::string()->required(),
		]);
	}

	public function loadConfiguration(): void
	{
		$searchExtension = $this->compiler->getExtensions(SearchExtension::class)['search'] ?? null;
		if (!$searchExtension instanceof SearchExtension) {
			throw new Exception(sprintf('Expected extensions type "%s", "%s" given', SearchExtension::class, gettype($searchExtension)));
		} elseif (!is_array($searchConfig = $searchExtension->getConfig())) {
			throw new Exception(sprintf('Expected "%s" config type "array", "%s" given', SearchExtension::class, gettype($searchConfig)));
		}

		$pluginConfig = [
			'plugins' => [
				'in' => $this->config->dir, // @phpstan-ignore-line
				'extends' => Plugin::class,
				'classes' => '*Plugin',
				'files' => '*Plugin.php',
			],
		];

		$pluginConfig = (new Processor())->process($searchExtension->getConfigSchema(), $pluginConfig);
		if (!is_array($pluginConfig)) {
			throw new Exception(sprintf('Expected "%s" config type "array", "%s" given', self::class, gettype($pluginConfig)));
		}

		$searchExtension->setConfig($searchConfig + $pluginConfig)->loadConfiguration();

		$this->getContainerBuilder()->addDefinition(null)->setFactory(PluginProvider::class);
	}

	public function beforeCompile(): void
	{
		$services = $this->getContainerBuilder()->findByType(Plugin::class);
		foreach ($services as $service) {
			$service->setAutowired(false);
		}
	}

}