Registrace služby

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

Mam komponentu (PresenterComponent), kterou chci zaregistrovat jako service. Do config.ini ji dat nemuzu (nebo ano?) protoze v konstruktoru je treba predat aktualni presenter. Takze jsem to vyresil tak, ze pridani sluzby davam do startup(). Vse funguje jak ma, jen pri prvnim prihlaseni to vyhodi chybu Service named ‚lang‘ has been already set. Je to tim, ze pri prihlaseni se vola application->restoreRequest, ktery provede presmerovani pres ForwardingException a dojde k tomu, ze metoda startup() se vola dvakrat. jednou pri prihlaseni a podruhe pri presmerovani, ale to uz je sluzba registrovana.

Je to bug nebo feature? Jaky je spravny postup pro pridani sluzby?

<?php
abstract class BasePresenter extends Presenter
{
	public function startup()
	{
		$component = new Lang($this, 'lang'); // PresenterComponent
		Environment::getServiceLocator()->addService($component, 'lang');
	}
?>
<?php
abstract class AdminPresenter extends BasePresenter
{
	public function startup()
	{
		$user = Environment::getUser();
		if (!$user->isAuthenticated()) {
			$backlink = $this->getApplication()->storeRequest();
			$this->redirect('Auth:login', $backlink);
		}
		parent::startup();
	}
}
?>
_Martin_
Generous Backer | 679
+
0
-

Nevím, zda jde přes config.ini předávat i objekt. Nicméně problém s dvojím voláním zkus vyřešit následujícícm způsobem:

	public function startup()
	{
		if (Environment::getServiceLocator()->getService('lang', FALSE) === NULL) {
			Environment::getServiceLocator()->addService(new Lang($this, 'lang'), 'lang');
		}
	}
Ondrej
Člen | 110
+
0
-

_Martin_ napsal(a):

Nevím, zda jde přes config.ini předávat i objekt. Nicméně problém s dvojím voláním zkus vyřešit následujícícm způsobem:

	public function startup()
	{
		if (Environment::getServiceLocator()->getService('lang', FALSE) === NULL) {
			Environment::getServiceLocator()->addService(new Lang($this, 'lang'), 'lang');
		}
	}

to moc neresi, protoze se pouzije prvni zavolani. Potrebuji mit v komponente odkaz na aktualni presenter. Tvuj priklad necha prvni presenter, ktery slouzil k obslouzeni prihlaseni. Pri naslednem presmerovani na novy presenter potrebuji $presenter v objektu Lang aktualizovat. To bych taky mohl udelat rucne

<?php
	public function startup()
 	{
 		if ($srv = Environment::getServiceLocator()->getService('lang', FALSE) === NULL) {
 			Environment::getServiceLocator()->addService(new Lang($this, 'lang'), 'lang');
 		}
		else $srv->presenter = $this;
 	}
?>

ale to urcite neni ciste reseni. Podle me by se mely sluzby vyresetovat pri presmerovani, protoze kdyz bych premerovaval ručne např. header(‚location: presenter:view‘) tak by to fungovalo.

_Martin_
Generous Backer | 679
+
0
-

Ale Nette přesměrování provádí hlavičkou Location, takže v tom případě se provádí nový HTTP požadavek a služba je tedy načtena znovu. Píšeš, že chyba se objevuje při přihlášení, konkrétně během obnovování původního požadavku – můžeš sem tedy napsat kód, který toto obnovení a následné přesměrování provede (tvůj původní kód se týká přesměrování na přihlašovací formulář)?
A teď (při bližším zkoumání kódu) mi došlo, že jako službu registruješ komponentu presenteru – jaký máš k tomu důvod?

Ondrej
Člen | 110
+
0
-

_Martin_ napsal(a):

Ale Nette přesměrování provádí hlavičkou Location, takže v tom případě se provádí nový HTTP požadavek a služba je tedy načtena znovu.

hlavickou se presmerovava pri pouziti $presenter->redirect().

Autentizace funguje tak, ze se puvodni request ulozi do session. A pri prihlaseni se pouzije ForwardindException, ktera v ramci $application->run udela dalsi iteraci.

Nette\Application.php

<?php
/**
	 * @param  string
	 * @return void
	 */
	public function restoreRequest($key)
	{
		$session = $this->getSession()->getNamespace('Nette.Application/requests');
		if (isset($session->$key)) {
			$request = $session->$key;
			unset($session->$key);
			throw new ForwardingException($request);
		}
	}
?>

_Martin_ napsal(a):

tvůj původní kód se týká přesměrování na přihlašovací formulář?

ano puvodni kod presmerovava na prihlasovaci furmular. Toto presmerovani funguje a nesouvisi s mym problemem, ktery nastane po odeslani prihlasovaciho formulare.

_Martin_ napsal(a):

A teď (při bližším zkoumání kódu) mi došlo, že jako službu registruješ komponentu presenteru – jaký máš k tomu důvod?

Chtel jsem globalni objekt, ktery by mi kdekoliv vratil napr. nastaveni jazyka. Vim, ze by to slo udelat pres perzistentni parametr, ale zdalo se mi lepsi to udelat jako sluzbu.

Konktretnejsi priklad: Chci generovat v aplikaci seofriedly odkazy a chci nekam na jedno misto dat volani $presenter->link(‚article:detail‘, 1, $title). Nechci to davat pokazde do sablony, protoze odkaz na article bude na nekolika mistech webu. Pokud casem zmenim strukturu url (napr. pridanim dalsiho parametru) $presenter->link(‚article:detail‘, 1, $title, $category) musel bych to menit na vsech mistech.
Napadlo me reseni pres sluzbu Environment::getServiceLocator()->addService(new SeoUrl($this, ‚seourl‘), ‚seourl‘). Tim by to byl globalni objekt, do ktereho bych mohl pristupovat z hepleru. Napr. {$article|create_link} /* $article je objekt, kde si create_link vytahne potrebne atributy (id,title) */

Nejvetsi problem je, ze metoda Link se da volat jen z prezenteru nebo controlu. Takze proto to vytvarim jako PresenterComponent, abych mel vzdy referenci na presenter a mohl volat link()

phx
Člen | 651
+
0
-

Ondrej napsal(a):
Nejvetsi problem je, ze metoda Link se da volat jen z prezenteru nebo controlu. Takze proto to vytvarim jako PresenterComponent, abych mel vzdy referenci na presenter a mohl volat link()

Kapku nechapu. V klasicke komponente muzes volat getPresenter() a tim ziskas odkaz na aktualni presenter, ktery jsi predal v konstruktoru ne?

Jinak ohledne presmerovani pouzij radeji klasicky redirect(). Melo by to vyresit problem se znovuregistraci komponenty.

Ondrej
Člen | 110
+
0
-

phx napsal(a):

Jinak ohledne presmerovani pouzij radeji klasicky redirect(). Melo by to vyresit problem se znovuregistraci komponenty.

Nemuzu pouzit jine presmerovani. O to se stara Nette\Application.

phx
Člen | 651
+
0
-

Proc ne? Koukni do prikladu jak je delane prihlasovani.

Ondrej
Člen | 110
+
0
-

phx napsal(a):

Proc ne? Koukni do prikladu jak je delane prihlasovani.

asi si nerozumime, prihlasovani mam vyresene presne podle prikladu. Reseni si nejake najdu, asi to udelam pres singleton.

Tema jsem otevrel, protoze me zajima jestli pri presmerovani pres ForwardingException je bug, ze se neresetuji sluzby nebo jestli je to chovani, se kterym musim pocitat.
Jestli jsem to spravne pochopil, tak pokud pristoupim na stranku bez prihlaseni, tak jsem presmerovan na prihlasovaci formular a jeste pred redirectem se do session ulozi aktualni request ten pak obnovim pres
$this->getApplication()->restoreRequest($this->backlink). Uvnitr restoreRequest() se vyhodi ForwardingException, coz je systemova zalezitost, kterou nemohu ovlivnit. Ke $this->redirect(‚Dashboard:‘) pak uz nedojde, pokud je predchozi request ulozen v session.

<?php
public function loginFormSubmitted($form)
	{
		try {
			$user = Environment::getUser();
			$user->authenticate($form['username']->getValue(), $form['password']->getValue());
			$this->getApplication()->restoreRequest($this->backlink);
			$this->redirect('Dashboard:');

		} catch (AuthenticationException $e) {
			$form->addError($e->getMessage());
		}
	}
?>
David Grudl
Nette Core | 8218
+
0
-

Ondrej napsal(a):

Mam komponentu (PresenterComponent), kterou chci zaregistrovat jako service. Do config.ini ji dat nemuzu (nebo ano?) protoze v konstruktoru je treba predat aktualni presenter.

Nemusí se předat v konstruktoru, vložení (nebo vyjmutí) se dá provést kdykoliv metodou addComponent (nebo removeComponent).

Takze jsem to vyresil tak, ze pridani sluzby davam do startup(). Vse funguje jak ma, jen pri prvnim prihlaseni to vyhodi chybu Service named ‚lang‘ has been already set.
Je to bug nebo feature? Jaky je spravny postup pro pridani sluzby?

Během životního cyklu aplikace se může spustit více presenterů, metoda startup() se může volat vícekrát. Je to feature ;) Takže buď službu nepřidávat v startup(), nebo ověřit, jestli existuje (metoda getService).

Ondrej napsal(a):

_Martin_ napsal(a):

Nevím, zda jde přes config.ini předávat i objekt. Nicméně problém s dvojím voláním zkus vyřešit následujícícm způsobem:

to moc neresi, protoze se pouzije prvni zavolani. Potrebuji mit v komponente odkaz na aktualni presenter.

V takovém případě je otázka, proč komponentu ukládat jako globální službu. Není lepší ji uložit přimo do proměnné presenteru? Služba by měla mít spíš platnost po celou dobu běhu aplikace.

Konktretnejsi priklad: Chci generovat v aplikaci seofriedly odkazy a chci nekam na jedno misto dat volani $presenter->link(‚article:detail‘, 1, $title). Nechci to davat pokazde do sablony, protoze odkaz na article bude na nekolika mistech webu. Pokud casem zmenim strukturu url (napr. pridanim dalsiho parametru) $presenter->link(‚article:detail‘, 1, $title, $category) musel bych to menit na vsech mistech.

Řekl bych, že uvažuješ možná příliš dopředu. Každopádně na zapouzdření odkazu existuje třída Link, kterou lze vygenerovat například metodou lazyLink() (používá se stejně jako link()). Odkaz tak lze vygenerovat na jednom místě a použít na více místech.

Napadlo me reseni pres sluzbu Environment::getServiceLocator()->addService(new SeoUrl($this, ‚seourl‘), ‚seourl‘). Tim by to byl globalni objekt, do ktereho bych mohl pristupovat z hepleru. Napr. {$article|create_link} /* $article je objekt, kde si create_link vytahne potrebne atributy (id,title) */

Na globální úložište se obrať až ve chvíli, kdy nebude jiného zbytí. Tohle použítí není zcela vhodné.

Nejvetsi problem je, ze metoda Link se da volat jen z prezenteru nebo controlu. Takze proto to vytvarim jako PresenterComponent, abych mel vzdy referenci na presenter a mohl volat link()

No pokud se dá volat jen z presenteru, tak předávej rovnou ten presenter, ne? Ale jinak tohle přesně řeší třída Link, viz výše.

Ondrej napsal(a):

Tema jsem otevrel, protoze me zajima jestli pri presmerovani pres ForwardingException je bug, ze se neresetuji sluzby nebo jestli je to chovani, se kterym musim pocitat.

Třída Environment je globální a o žádných presenterech „nic neví“. Tudíž se nic neresetuje. Je také možné použít service locator samotného presenteru nebo jakékoliv komponenty (metoda getServiceLocator()) a ten se s každým novým presenterem vytváří nový. Tahle funkcionalita ale ještě není finální.