DB model – service alebo factory

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

Ahoj,
toto je fakt hlupa otazka, ale fakt netusim co je spravnejsie.

Viem co je factory a viem co je service. Ale netusim ci je lepsie mat DB model ako factory alebo service. Alebo je to uplne fuk, dolezite je ze sa k tomu da dostat (cez kontext alebo Environment). (… alebo je mozne to nejak dorucit do presenteru/komponenety s DI?). Tiez som videl tu ze to nemusi byt ziadne z nich a instanciovat model priamo kde ho potrebujeme, ale to asi nebude uplne koser riesenie.

Pripojenie databaze je jasny service – pri prvom pristupe sa pripoji a potom sa pouziva.

Nette\Database modely dedia od Selection – a v quickstarte to je factory … ak kazda jedna instancia je konkretny vyber z tabulky, tak ano, je to na factory.

Ale s Dibi je to inak. Tam to nededi od nicoho, konstruktor si ulozi DibiConnection (s DI a autowiring), a az jednotlive funkcie vracaju nejake data. To vyzera na service, ale pride mi to trosku divne, mat tolko servicov, z kolkych tabuliek chcem selektovat. Da sa to este nejak pospojovat do nejakych celkov, ale tiez neviem ci je to spravne riesenie. Ako riesit napr nejaky komplexnejsi select krizom cez 4–5 tabuliek, do ktoreho modelu to dat?

Dajme tomu, ak by som chcel robit nejaku (vacsiu) aplikaciu s Dibi, je to spravny pristup? (co tabulka to service)

ViPEr*CZ*
Člen | 813
+
0
-

Když víte co je factory a co je service, pak se snad ani nemůžete ptát. :-D
Jen čistě náhodou … selským rozumem. Kdyby jste věděl co je service, pak ji nemůžete používat na každej model, protože její instance se udělá ať jste kde jste v aplikaci, i když jí zrovna nepotřebujete.
Přesně na service se hodí například Connection na databázi, protože tu využijete prakticky všude. Proto taky QS používá factory na modely ;-)

Editoval ViPEr*CZ* (4. 9. 2012 20:02)

Draffix
Člen | 146
+
0
-

Jen tak ze zvědavost, kde kromě již zmíněného připojení k databázi ještě můžu servisy využít? Všude se uvádí především to připojení, a tak by mě jen zajímalo, u čeho se to tak ještě častěji využívá? Jinak sorry za případný OT.

Filip Procházka
Moderator | 4668
+
0
-

Všechno co jde, tak nacpi do DI Containeru, i kdybys tam měl mít 1000 služeb.

DIC továrničky nepoužívej, raději si „ručně napiš“ factory jako službu

QS byl aktualizován aby nevyužíval DIC továrničky. Bude ale ještě jednou aktualizován, aby používal inject*()

Editoval HosipLan (4. 9. 2012 20:21)

ViPEr*CZ*
Člen | 813
+
0
-

Kruci koukám, že jsem malinko zabržděnej. :-/
Tím pádem je to fuk… faktorka akorát po každém volání udělá novou instanci a služba volá stále totožnou instanci. Krom toho teda ty zmiňované nové metody inject*().

miler
Člen | 75
+
0
-

Tak jsem zase trochu zamotaný. Když jsem navrhoval model podle QuickStartu poprvé, dědil od Selection. Poté se Quickstart trochu změnil na to, že modely dědily od Nette\Object a vytvářely se jako továrničky.

Teď jsem v tomto topicu zjistil, že je QuickStart trochu změněný a posunulo se to do služeb.

V configu aktuálního QuickStartu je třída TaskList\Tasks zaregistrovaná jako služba se jménem tasks a v presenteru se pak do privátní proměnné ukládá ve startupu. Já jsem to měl v konstruktoru, kam jsem předal kontext a z něj jsem ukládal.

Můžete mě prosím někdo vymotat jak se tedy služby mají správně předat do presenteru? Ve startupu, v konstruktoru nebo v inject?

Omlouvám se za hloupé dotazy, ale opravdu jsem se ztratil a děkuji těm, kteří se mnou budou mít trpělivost.

// class HomepagePresenter

/** @var TaskList\Tasks */
private $tasks;

protected function startup()
{
    parent::startup();
    $this->tasks = $this->context->tasks;
}

Editoval miler (4. 9. 2012 21:50)

mkoubik
Člen | 728
+
0
-

Pokud máš aktuální nette (2.0.5, nebo 2.1-dev), tak použij inject*() – vyhneš se problémům při dědičnosti. Pokud máš starší nette, tak si služby předej jako závislost do konstruktoru. Pak existuje ještě setContext().
Jinak $this->context je fuj a $this->context->create*() jakbysmet – presenter (ani jiná služba) by neměl vůbec vědět že nějaký kontejner existuje (v presenteru je kvůli zpětné kompatibilitě) a mělo by ho být možné sestavit i ručně (třeba v testech).

Edit: S tím contextem ve startup() ti to bude taky fungovat, ale není to tak pěkný. Nicméně předělávat to asi nemusíš.

Editoval mkoubik (4. 9. 2012 21:49)

miler
Člen | 75
+
0
-

Díky moc, ale pokud by neměl presenter vědět o kontejneru, jak z něj potom získám službu třeba pro httpRequest?

pg
Člen | 8
+
0
-

mkoubik napsal(a):

Jinak $this->context je fuj a $this->context->create*() jakbysmet – presenter (ani jiná služba) by neměl vůbec vědět že nějaký kontejner existuje (v presenteru je kvůli zpětné kompatibilitě) a mělo by ho být možné sestavit i ručně (třeba v testech).

Trochu off topic možná, ale co když mám továrničku na vlastní Message a potřebuji volat $this->context->createMessage()? Jak jinak to jde zavolat z presenteru? I v dokumentaci je $container->create*(). A v presenteru je container $this->context, pokud se nemýlím.

Editoval pg (4. 9. 2012 23:46)

redhead
Člen | 1313
+
0
-

Řekl bych tak, že budeš mít zaregistrovanou vlastní třídu továrničky jako službu a injectovat ji budeš pomocí inject*() metody. Pak budeš volat místo $this->context->createMessage() třeba něco jako $this->myMessageFactory->createMessage() a nebudeš nikde používat kontext.

pg
Člen | 8
+
0
-

Aha, takže až od 2.0.5. U 2.0.4 je používání contextu nevyhnutelné, je tak?

vvoody
Člen | 910
+
0
-

Nie, inject*() nahradilo injectovanie cez konstruktor. Používanie contextu nebolo nikdy nevyhnutné.

Tomas P
Člen | 27
+
0
-

HosipLan napsal(a):

Všechno co jde, tak nacpi do DI Containeru, i kdybys tam měl mít 1000 služeb.

OK, takze sluzby

DIC továrničky nepoužívej, raději si „ručně napiš“ factory jako službu

QS byl aktualizován aby nevyužíval DIC továrničky. Bude ale ještě jednou aktualizován, aby používal inject*()

Dik za info, to je dobre vediet…

Tak som skusil update na 2.0.5, pouzivam inject*() – to funguje pekne na presenteroch.

Na vlastne FooControl (od Nette\Application\UI\Control) je ale potrebne inject* volat sam z presenteru, z funkcie createComponentFoo() – je to tak? alebo existuje nejaka automaticka alternativa? (Do presentru sa to dostane uz cez wiring automaticky.)

OT: Celkovo mi pride (IMHO), ze na kazdej stranke sice mozem mat kopu komponent (UI\Control), ale nie su vobec samostatne, takze aktualny presenter (alebo jeho predok) s nimi musi byt dost previazany.

Dalsia otazka by bola:

Ako riesit napr nejaky komplexnejsi select krizom cez 4–5 tabuliek, do ktoreho modelu to dat?

Editoval Tomas P (5. 9. 2012 10:50)

Jan Endel
Člen | 1016
+
0
-

Ako riesit napr nejaky komplexnejsi select krizom cez 4–5 tabuliek, do ktoreho modelu to dat?

Záleží na situaci, ale přehodnotil bych pohled tabulka = model, vždy to neplatí.

David Ďurika
Člen | 328
+
0
-

pilec napsal(a):

Ako riesit napr nejaky komplexnejsi select krizom cez 4–5 tabuliek, do ktoreho modelu to dat?

Záleží na situaci, ale přehodnotil bych pohled tabulka = model, vždy to neplatí.

presne! niekedy ma model jednu tabulku niekedy viac a niekedy ani jednu…

miler
Člen | 75
+
0
-

Jak ale tedy při injectování vytáhnu z containeru to co jsem si v bootsrtapu uložil do $container->parameters (např. base URL), nebo jak získám v presenteru httpRequest, který je v containeru standardně? Teď jsem to ukládal z contextu.

Filip Procházka
Moderator | 4668
+
0
-

IMHO bys měl parametry mít obalené nějakou službou, jejíž chování ta nastavení budou měnit. Tu si pak injectneš snadno.

vvoody
Člen | 910
+
0
-

Parameters cez inject nedostanes (tento sa riesi) ale moj nazor je, ze do presenteru ani netreba ziadne parametre predavat. Tak ako napisal HosipLan „i kdybys tam měl mít 1000 služeb“ a do sluzieb tie parametre predavat mozeme.

Skus si jednoducho poziadat cez injectRequest(Nette\Http\Request $request)

miler
Člen | 75
+
0
-

Díky, takhle jsem o ten Request pořádal a funguje to. Jen jsem si nebyl jsitý jestli to tak je správně.

Params jsem chtěl původně kvůli tomuto:

$this->httpRequest = $context->getService('httpRequest');
$this->httpRequest->detectLanguage($context->parameters['acceptedLangs']);

Ale to už nebudu devastovat původní topic. Děkuji moc za vaše poskytnuté rady a ukázky kódu, jak se teď mění některé postupy tak jsem byl přeci jen trochu zmaten :-)

mkoubik
Člen | 728
+
0
-

(píšu z hlavy)

class LanguageDetectionService {
	private $acceptedLangs;
	private $request;

	public function __construct(\Nette\Http\Request $request, $acceptedLangs)
	{
		$this->request = $request;
		$this->acceptedLangs = $acceptedLangs;
	}

	public function detectLanguage(array $langs = null)
	{
		if ($langs === null) {
			$langs = $this->acceptedLangs;
		}
		return $this->request->detectLanguage($langs);
	}
}

config.neon:

services:
	languageDetection: LanguageDetectionService(..., %acceptedLangs%)

presenter:

public function injectLanguageDetection(\LanguageDetectionService $languageDetection)
{
	$this->languageDetection = $languageDetection;
}

...

$this->requestService->detectLanguage();

Editoval mkoubik (5. 9. 2012 17:07)

miler
Člen | 75
+
0
-

Opravdu děkuji moc! Fakt se stydím :-)

raketoplan2005
Člen | 147
+
0
-

Můžu přidat také svou trošku začátečnických dotazů? Používám NetteTranslator od Tomáše Votruby z kuchařky (https://componette.org/search/?…).

Služba je v configu zaregistrovaná takto:

services:
	translator:
		factory: NetteTranslator\Gettext::getTranslator

		setup:
			- addFile(%appDir%/lang, frontend)
			- NetteTranslator\Panel::register

Injectuji takto:

public function injectTranslator(NetteTranslator\Gettext $translator) {
        $this->translator = $translator;
}

Je to správně?

Editoval raketoplan2005 (5. 9. 2012 19:16)

Ivorius
Nette Blogger | 119
+
0
-

Podle návodu Inject Autowire

public function injectTranslator(\NetteTranslator\Gettext $translator) {
	if ($this->translator) {
        throw new \Nette\InvalidStateException('Translator has already been set');
    }
	$this->translator = $translator;
}