Jak získat službu ve statické třídě nebo objektu mimo DI

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
akadlec
Člen | 1326
+
+2
-

Je nějaké správné řešení jak se dostat ke službě ve třídě/objektu co je mimo DI? Zatím to řeším přes environment:

\Nette\Environment::getService('superCool.service');

ale to se hlásí jako deprecated takže bude v budoucnu asi úplně zrušeno?

Jan Endel
Člen | 1016
+
+1
-

Ano bude, proč služba nemůže být v DI?

akadlec
Člen | 1326
+
0
-

Protože se k tomu přímo nette nedostane, proto ji to nepředá závislost.

Vojtěch Dobeš
Gold Partner | 1316
+
+4
-

Správným způsobem je si onu superCool službu předat do daného kódu jako argument – to je DI. Je jedno, jestli to za tebe udělá Nette, anebo si ji předáš ručně. To je jediný způsob, jak se vyhnout Environment, který je postupně opouštěn právě proto, že obchází transparentní předávání argumentů.

class MyObjectOutsideOfDIContainer
{

	public static function methodThatNeedsSuperCoolService($superCoolService)
	{
		// no need for Environment...
	}

}
akadlec
Člen | 1326
+
0
-

problém je ten že to je prostě hotový balík do kterého „nejde zasáhnout“ a nebo jak jsem uvedl jde o statickou třídu/metodu takže taky není cesta jak ji tam přidat.

ViPEr*CZ*
Člen | 817
+
0
-

Pokud chcete mít něco uvnitř té statické metody, tak už do toho balíku stejně zasahujete jestli se nepletu?

mkoubik
Člen | 728
+
+6
-

Já bych si na to asi napsal wrapper třídu, která bude používat DI, zaregistruješ si ji jako službu a ona pak bude ve svých metodách volat tu statickou.

Nevím přesně jak ta třída vypadá a jak ji chceš používat, ale něco takového by nešlo?

class Wrapper
{
	private $dependency;

	public function __construct(Dependency $dependency)
	{
		$this->dependency = $dependency;
	}

	public function coolMethod($param)
	{
		UglyStaticClass::$dependency = $this->dependency;
		return UglyStaticClass::coolMethod($param);
	}
}

Editoval mkoubik (20. 6. 2014 11:18)

akadlec
Člen | 1326
+
0
-

ne, do toho balíku se dají dostat objekty/definice aniž by se nějak do něj zasáhlo.

akadlec
Člen | 1326
+
0
-

K těm statickým třídám/voláním, tady to mám v routeru:

		$list[] = new NetteRouteMock('[!<locale [a-z]{2,4}>/]mailbox/<mailbox>', array(
			'module'	=> 'Frontend',
			'presenter'	=> 'Default',
			'action'	=> 'default',
			'mailbox'	=> array(
				Route::FILTER_OUT	=> 'IPub\MailboxModule\Router\FoldersRouter::getFolderSlugById',
				Route::FILTER_IN	=> 'IPub\MailboxModule\Router\FoldersRouter::getFolderIdBySlug'
			),
			'locale'	=> array(
				Route::VALUE	=> 'en',
				'fixity'		=> Route::CONSTANT,
			)
		));

a pak samotná classa:

class FoldersRouter
{
	/**
	 * @param string $slug
	 *
	 * @return int|bool
	 */
	public static function getFolderIdBySlug($slug)
	{
		// Get module facade
		$folderFacade = \Nette\Environment::getService('mailboxModule.folderFacade');

		// Check if slug is number...
		if (is_numeric($slug)) {
			if ($folderEntity = $folderFacade->findOneByIdentifier((int) $slug)) {
				return $folderEntity;
			}

		// Check if slug is string...
		} else if (is_string($slug)) {
			if ($folderEntity = $folderFacade->findOneBySlug((string) $slug)) {
				return $folderEntity;
			}
		}

		return NULL;
	}
	//....
}

další případ je kdy to potřebuju dostat do objektu, ale vytvoření objektu je úplně mimo, ja jen té extension řeknu jakou classu má použít.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Pokud prostě neexistuje jiná cesta, než tahat si to odněkud globálně, tak lze buď použít Environment, anebo třeba $GLOBALS.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

V tom příkladu vůbec nechápu, proč by ta filtrovací třída nemohla fungovat jako klasický objekt, který nainstancuješ s tím, co je potřeba.

akadlec
Člen | 1326
+
0
-

no to jsem takhle původně chtěl, nicméně tyhle routy se dělají compilerextension takže sem je tam neměl jak (nebo nevěděl jak) dostat

Tomáš Votruba
Moderator | 1114
+
0
-

Tzn. že teď už víš?
Stačí přidat extension mezi služby. Pak už bude fungovat autowiring a můžes si do něj předávat služby.

Editoval Tomáš Votruba (20. 6. 2014 13:35)

akadlec
Člen | 1326
+
0
-

ehm tak teď se mimo. ten modul je normálně zaregistrovaný a o vytvoření roout se stará implementace routerprovider. Leda by do té metody dorazil container ze kterého by ta služba šla vytáhnout jinak nevím.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Začíná to nabírat sureálné obrysy. Z jakého důvodu nemůže být FoldersRouter normálně autowirovanou službou?

akadlec
Člen | 1326
+
0
-

Jak jsem psal leda by tam byl parametr ze kterého by to šlo vytáhnout:

class MailboxModuleExtension extends DI\CompilerExtension implements IRouterProvider
{
	public function getRoutesDefinition()
	{
		// Create module routes group
		$list = new NetteRouteListMock('Mailbox');

		$list[] = new NetteRouteMock('[!<locale [a-z]{2,4}>/]mailbox/<mailbox>', array(
			'module'	=> 'Frontend',
			'presenter'	=> 'Default',
			'action'	=> 'default',
			'mailbox'	=> array(
				Route::FILTER_OUT	=> 'IPub\MailboxModule\Router\FoldersRouter::getFolderSlugById',
				Route::FILTER_IN	=> 'IPub\MailboxModule\Router\FoldersRouter::getFolderIdBySlug'
			),
			'locale'	=> array(
				Route::VALUE	=> 'en',
				'fixity'		=> Route::CONSTANT,
			)
		));

		return array($list);
	}
}
David Matějka
Moderator | 6445
+
0
-

Tyhle providery se hodeji jen pokud prave to, co to vraceji, nema zavislost na sluzbach. Pokud je to na necem zavisle, je lepsi tu routu registrovat jako sluzbu a otagovat

Tomáš Votruba
Moderator | 1114
+
0
-

@akadlec Uvedu příklad:

services:
	- MailboxModuleExtension # případně přidat v samotném rozšíření v loadConfiguration()

extensions:
	- MailboxModuleExtension
David Matějka
Moderator | 6445
+
0
-

Koukal jsem na implementaci flame/modules a mohlo by fungovat:

array (
	"mailbox" => array(
		Route::FILTER_IN => new PhpLiteral('$this->getService("someService")->doFoo()'),
	),
)
Filip Procházka
Moderator | 4668
+
0
-

Proč je problém použít normálně objekty v tom routeru?

Route::FILTER_IN => [$object, 'getFolderSlugById'],

nebo

Route::FILTER_IN => function () use (...) { ... },
David Matějka
Moderator | 6445
+
0
-

@FilipProcházka ty routy se zpracovavaji pri kompilaci kontejneru a dosadeji se do setupu routeru. Takze se nejdriv museji exportovat, coz se sluzbou neudelas. viz https://github.com/…xtension.php#L238

Editoval matej21 (20. 6. 2014 20:34)

Filip Procházka
Moderator | 4668
+
0
-

@matej21 to proto, že je to udělané špatně.

akadlec
Člen | 1326
+
0
-

mno a je někde jiné „správné“ řešení router provideru? či jak jinak lépe definovat routy které jsou v rámci modulu?

David Matějka
Moderator | 6445
+
0
-

Jak jsem psal – router provider je dobry se statickyma routama. Pro komplexnejsi routy bych pouzil otagovane sluzby. Neco jako

$builder->addService('myRouteList')
	->setFactory(...)
	->addTag(Foo::ROUTE);

v beforeCompile pak vytahnout vsechny sluzby s timhle tagem a doplnit je do routeru

akadlec
Člen | 1326
+
0
-

@matej21: pls můžeš mě nakopnout tím správným směrem jak pak z té služby to dostat do routeru?

akadlec
Člen | 1326
+
0
-

nn toto vím ;) ale jak to dostat do routeru?

David Matějka
Moderator | 6445
+
0
-

Nejjednoduseji zhruba:

$router = $builder->getDefinition('router');
foreach(array_keys($builder->findByTag(...)) as $name) {
	$router->addSetup('offsetSet(?, ?)', array(NULL, '@' . $name));
}

nebo prependnout podobne jako ve flame modules https://github.com/…xtension.php#L238

Tomáš Votruba
Moderator | 1114
+
0
-

@akadlec Pro použití služeb v routeru při zachování modularity čekni tento PR.

Stačí ti službu s routami otagovat a v ní mít metodu createRouter v duchu routeru v sandboxu:

services:
    -
        class: App\CoreModule\Routing\RouterFactory
        tags: [flame.modules.router]

Editoval Tomáš Votruba (23. 8. 2014 20:21)

akadlec
Člen | 1326
+
0
-

A je pak možné mít více těchto routerů? Tj. pro každý modul zvlášť?

Tomáš Votruba
Moderator | 1114
+
0
-

Presne tak :)

Tomáš Votruba
Moderator | 1114
+
0
-

Přidány testy a mergnuto. Můžeš vyzkoušet

akadlec
Člen | 1326
+
0
-

Pecka funguje to…