SearchExtension – použití v jiné CompilerExtension
- Felix
- Nette Core | 1245
Ahoj. Vzdycky si danou extension muzes nainstancovat sam a ovladat ji dle potreby.
<?php
use Nette\DI\CompilerExtension;
use Nette\DI\Extensions\SearchExtension;
class MyExtension extends CompilerExtension
{
public function loadConfiguration()
{
$config = [...];
$ext = new SearchExtension($this->tempDir);
$ext->setCompiler($this->compiler, $this->prefix('innersearch'));
$ext->setConfig($config);
$ext->loadConfiguration();
$ext->beforeCompile();
$ext->findClasses($config);
}
}
- Gappa
- Nette Blogger | 208
Funkční prototyp:
<?php
declare(strict_types=1);
use Nette\DI\Extensions\SearchExtension;
use Nette\DI\InvalidConfigurationException;
use Nette\Schema\Processor;
use Nette\Schema\Schema;
use Nette\Schema\ValidationException;
final class Extension extends CompilerExtension
{
public function loadConfiguration(): void
{
$builder = $this->getContainerBuilder();
$extConfig = [
'in' => __DIR__ . '/../',
'classes' => [
'*Facade',
'*Package',
'*Factory',
'*Presenter',
'*Repository',
]
];
$name = 'packagesearch';
$searchExt = new SearchExtension($builder->parameters['tempDir'] . '/cache/' . $name);
$searchExt->setCompiler($this->compiler, $this->prefix($name));
$config = $this->processSchema($searchExt->getConfigSchema(), $extConfig);
$searchExt->setConfig($config);
$searchExt->loadConfiguration();
$searchExt->beforeCompile();
}
/**
* Merges and validates configuration against scheme.
* @param Schema $schema
* @param array $config
* @return array|object
*/
private function processSchema(Schema $schema, array $config)
{
$processor = new Processor;
try {
return $processor->process($schema, $config);
} catch (ValidationException $e) {
throw new InvalidConfigurationException($e->getMessage());
}
}
}
Doplnění:
- Metoda
findClasses
není potřeba volat ručně, volá se už vloadConfiguration
. - Pro větší pohodlí jsem tam doplnil validaci pomocí Scheme, když už tam je :)
Editoval Gappa (3. 2. 2020 21:03)
- Gappa
- Nette Blogger | 208
Felix napsal(a):
Pekny. BeforeCompile bych volal tez v beforeCompile. At zachovas lifecycle.
To jsem zkoušel, ale pokud je takto zaregistrovaný jakýkoliv presenter, tak pak začne Nette vyhazovat následující chybu:
Error
Call to a member function isSent() on null
File: .../src/Application/UI/Presenter.php:200
190:
191:
192: public function run(Application\Request $request): Application\IResponse
193: {
194: try {
195: // STARTUP
196: $this->request = $request;
197: $this->payload = $this->payload ?: new \stdClass;
198: $this->setParent($this->getParent(), $request->getPresenterName());
199:
200: if (!$this->httpResponse->isSent()) {
201: $this->httpResponse->addHeader('Vary', 'X-Requested-With');
202: }
203:
204: $this->initGlobalParameters();
Vůbec se nevytvoří httpResponse objekt, pokud se v browseru snažím dostat na takto zaregistrovaný presenter.
Pokud beforeCompile()
zavolám v
loadConfiguration()
, tak je vše ok – divné :)
- Gappa
- Nette Blogger | 208
Takto:
<?php
declare(strict_types=1);
use Nette\DI\CompilerExtension;
use Nette\DI\Extensions\SearchExtension;
use Nette\DI\InvalidConfigurationException;
use Nette\Schema\Processor;
use Nette\Schema\Schema;
use Nette\Schema\ValidationException;
final class Extension extends CompilerExtension
{
/** @var SearchExtension */
private $searchExt;
public function loadConfiguration(): void
{
$builder = $this->getContainerBuilder();
$extConfig = [
'in' => __DIR__ . '/../',
'classes' => [
'*Facade',
'*Package',
'*Factory',
'*Presenter',
'*Repository',
]
];
$name = 'packagesearch';
$this->searchExt = new SearchExtension($builder->parameters['tempDir'] . '/cache/' . $name);
$this->searchExt->setCompiler($this->compiler, $this->prefix($name));
$config = $this->processSchema($this->searchExt->getConfigSchema(), $extConfig);
$this->searchExt->setConfig($config);
$this->searchExt->loadConfiguration();
}
public function beforeCompile(): void
{
$this->searchExt->beforeCompile();
}
/**
* Merges and validates configuration against scheme.
* @param Schema $schema
* @param array $config
* @return array|object
*/
private function processSchema(Schema $schema, array $config)
{
$processor = new Processor;
try {
return $processor->process($schema, $config);
} catch (ValidationException $e) {
throw new InvalidConfigurationException($e->getMessage());
}
}
}
Editoval Gappa (4. 2. 2020 10:06)
- Gappa
- Nette Blogger | 208
Po nějaké době jsem se k tomu vrátil a nevím jak dříve, ale s aktuálními verzemi balíčků to hlásí stejnou chybu, i když se použije standardní způsob registrace v neonu:
search:
in: '%appDir%/packages/NewsPackage/'
classes:
- '*Facade'
- '*Package'
- '*Factory'
- '*Presenter'
- '*Repository'
Z čehož tedy usuzuji, že registrace presenterů v BeforeCompile je už
pozdě, ve vygenerovaném kontejneru se pro presentery nevolá
injectPrimary
, kde se předává spousta věcí:
public function createService047(): App\Packages\NewsPackage\Presenters\AdminPresenter
{
return new App\Packages\NewsPackage\Presenters\AdminPresenter(
$this->getService('045'),
$this->getService('041'),
$this->getService('042')
);
}
vs automaticky zaregistrovaný presenter:
public function createServiceApplication__6(): App\Module\Front\Presenters\HomepagePresenter
{
$service = new App\Module\Front\Presenters\HomepagePresenter;
$service->injectPrimary(
$this,
$this->getService('application.presenterFactory'),
$this->getService('routing.router'),
$this->getService('http.request'),
$this->getService('http.response'),
$this->getService('session.session'),
$this->getService('security.user'),
$this->getService('latte.templateFactory')
);
// dalsi sluzby
$service->invalidLinkMode = 5;
return $service;
}
Vůbec se nemůžu dopátrat, kde se rozhoduje, jestli se použije
injectPrimary nebo ne, nemůžu to najít ani jako string kromě místa, kde je
metoda definovaná, ani hledání Primary
nepomohlo :)
Editoval Gappa (22. 6. 2020 10:39)
- Marek Bartoš
- Nette Blogger | 1274
Vůbec se nemůžu dopátrat, kde se rozhoduje, jestli se použije injectPrimary nebo ne
Jde čistě jen o pořadí spouštění, žádnou podmínku nenajdeš.
ani hledání Primary nepomohlo :)
Ani to nenajdeš. InjectExtension hledá všechny inject metody a inject
property u služeb, které mají tag nette.inject
. Zkus jej
přidat v SearchExtension k presenterům.
Editoval Mabar (22. 6. 2020 10:54)
- Gappa
- Nette Blogger | 208
Uvádím pro úplnost – nakonec jsem zvolil toto řešení, uvidím, jak se osvědčí :)
Extension:
<?php
declare(strict_types=1);
namespace App\Packages\TestPackage\DI;
use Nette\DI\CompilerExtension;
use Some\Namespace\PackageServiceProvider;
use Nette\SmartObject;
final class TestPackageExtension extends CompilerExtension
{
use SmartObject;
use PackageServiceProvider;
public function loadConfiguration(): void
{
$config = $this->loadFromFile(__DIR__ . '/config.neon');
$this->compiler->loadDefinitionsFromConfig($config['services']);
$this->loadServices();
}
}
Trait:
<?php
declare(strict_types=1);
namespace Some\Namespace;
use ReflectionClass;
use Nette\DI\CompilerExtension;
/**
* @mixin CompilerExtension
*/
trait PackageServiceProvider
{
protected function loadServices(): void
{
$reflection = new ReflectionClass(get_class($this));
$dir = dirname((string) $reflection->getFileName());
$serviceProviderHelper = new ServiceProviderHelper($this->compiler, $dir, $this->getServicesMask());
$serviceProviderHelper->loadServices();
}
protected function getServicesMask(): array
{
return [];
}
}
Helper:
<?php
declare(strict_types=1);
namespace Some\Namespace;
use Nette\DI\Compiler;
use Nette\DI\Extensions\SearchExtension;
use Nette\DI\InvalidConfigurationException;
use Nette\Schema\Processor;
use Nette\Schema\Schema;
use Nette\Schema\ValidationException;
final class ServiceProviderHelper
{
/** @var Compiler */
private $compiler;
/** @var SearchExtension */
private $searchExtension;
/** @var string */
private $dir;
/** @var string[] */
private $defaultMask = [
'*Facade',
'*Manager',
'*Factory',
'*Repository',
];
/** @var string[] */
private $customMask = [];
public function __construct(Compiler $compiler, string $dir, array $customMask = [])
{
$this->compiler = $compiler;
$this->searchExtension = $this->getSearchExtension();
$this->dir = $dir;
$this->customMask = $customMask;
}
public function loadServices(): void
{
$config = $this->processSchema($this->searchExtension->getConfigSchema(), $this->getConfig());
$this->searchExtension->setConfig($config);
$this->searchExtension->loadConfiguration();
}
private function getSearchExtension(): SearchExtension
{
$extension = $this->compiler->getExtensions()['search'];
assert($extension instanceof SearchExtension);
return $extension;
}
private function getConfig(): array
{
$mask = array_merge($this->defaultMask, $this->customMask);
return [
'presenters' => [
'in' => $this->dir . '/../Presenters/',
'classes' => ['*Presenter'],
'tags' => ['nette.inject'],
],
'other' => [
'in' => $this->dir . '/../',
'classes' => $mask,
]
];
}
/**
* Merges and validates configuration against scheme.
* @param Schema $schema
* @param array $config
* @return array|object
*/
private function processSchema(Schema $schema, array $config)
{
$processor = new Processor;
try {
return $processor->process($schema, $config);
} catch (ValidationException $e) {
throw new InvalidConfigurationException($e->getMessage());
}
}
}