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 | 8201
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);
}
}
}