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

- Sniclman
- Člen | 15
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.

- Sniclman
- Člen | 15
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 | 8285
To je dost atypické použití. Na vypnutí autowiringu bude asi nejsnazší si vytvořit DI rozšíření

- Sniclman
- Člen | 15
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);
}
}
}