Vlastní ErrorPresenter pro modul v Nette 2.2

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

Dobrý den,
v aplikaci mám dva moduly (Front a Admin). Chci si pro modul Front nastavit vlastní ErrorPresenter, ale modul Admin zatím používal výchozí Nette ErrorPresenter.

Když nastavím v neonu errorPresenter: 'Front:Error', tak se použije ve všech modulech.

Našel jsem návody jak to nastavit ve starších verzích Nette přímo v metodě startup() BasePresenteru pro konkrétní modul, přes $this->getApplication()->errorPresenter, ale tato metoda je již označena jako deprecated.
Zkoušel jsem toto vyřešit tím že jsem si v BasePresenteru injectnul Nette\Application\Application a zkoušel mu nastavit errorPresenter. Toto však také nefungovalo a stále se volal výchozí ErrorPresenter.

Asi je to tím, že se aplikace k presenteru ještě ani nedostala, protože v routeru provádím dotaz do DB na zjištění presenteru. A již zde může nastat chyba, že presenter nebyl nalezen.

Jak nato? Nedá se již nastavit více ErrorPresenterů pro různé moduly přímo v neonu?

Jan Tvrdík
Nette guru | 2595
+
+15
-

Error presenter musí být vždy jenom jeden, ale uvnitř něho můžeš forwardnout (interně přesměrovat) na jiný error presenter.

use Nette\Application\UI;
use Nette\Application\Request as AppRequest;

class ErrorPresenter extends UI\Presenter
{
	public function renderDefault(\Exception $exception, AppRequest $request)
	{
		if (Strings::startsWith($request->presenterName, 'Front:')) {
			$this->forward(':Front:Error:', [
				'exception' => $exception,
				'request' => $request,
			]);
		}
		...
	}
}
Tomáš Kolinger
Člen | 136
+
+6
-

Ne, jde mít jen jeden error presenter.

Detekoval modul je problém, hlavně pokud nastane chyba před/při zavolání presenteru.

Nejednodušší je mít klasicky jeden presenter a teprve tam se rozhodovat co zobrazit, mít prostě sadu pohledů jak pro frontend, tak backend (např. backend_404.latte, frontend_500.latte, …). Co zobrazit můžeš detekovat podle URL, pokud backend routy prefixuješ pomocí admin/, tak ti stačí vytáhnout z requestu URL a kouknout na začátek. Jednoduché, rychlé a jisté.

jEhLa
Člen | 70
+
0
-

Děkuji za rady. Pustím se tedy touto cestou. Ještě jednou dík za rychlou odpověď.

Jan Suchánek
Člen | 404
+
0
-

Jak dostaneš do renderDefault AppRequest $request?

Jan Tvrdík
Nette guru | 2595
+
0
-

Nette ho automaticky předává, viz https://github.com/…lication.php#L170

Jan Suchánek
Člen | 404
+
0
-

AppRequest $request mi vrací vždy NULL

Jan Tvrdík
Nette guru | 2595
+
0
-

A jsi si jist, že proběhl ten předchozí požadavek? Nenastala chyba třeba už při routování? Případně jak vypadá $this->parameters?

Jan Suchánek
Člen | 404
+
0
-

Router:

	/**
	 * @return \Nette\Application\IRouter
	 */
	public function createRouter()
	{
	$router = new RouteList();

	        $backRoutes = new RouteList('Back');
	$route = new Route('admin/<presenter>[/<action>[/<id>]]', array(
	            'presenter' => 'Default',
	    'action' => 'default',
	            'id' => NULL,
	));
	        $backRoutes[] = $route;

	$frontRoutes = new RouteList('Front');
	        $route = new Route('[<lang [cs|en|de]{2} >/]<presenter>[/<action>][/<id [0-9]+>]', array(
	    'presenter' => 'Default',
	            'action' => 'default',
	    'lang' => 'cs',
	            'id' => NULL,
	));
	        $frontRoutes[] = $route;

	$router[] = $backRoutes;
	        $router[] = $frontRoutes;

	return $router;
	}

dump $this->parameters v ErrorPresenteru

array (2)
exception => Nette\Application\BadRequestException #f578
	defaultCode protected => 404
	message protected => "Cannot load presenter 'Front:Aaa', class 'App\FrontModule\Presenters\AaaPresenter' was not found in '/Users/example/Sites/test/app/FrontModu ... " (182)
	string private => ""
	code protected => 404
	file protected => "/Users/example/Sites/test/vendor/nette/application/src/Application/Application.php" (92)
	line protected => 128
	trace private => array (2)
		0 => array (6) [ ... ] 1 => array (6) [ ... ]
	previous private => Nette\Application\InvalidPresenterException #97cc
	xdebug_message => "Nette\Application\InvalidPresenterException: Cannot load presenter 'Front:Aaa', class 'App\FrontModule\Presenters\AaaPresenter' was not found in '/Us ... " (1640)
request => NULL

Editoval jenicek (12. 6. 2014 11:42)

Šaman
Člen | 2635
+
0
-

Teď to zkouším a když se presenter nenajde, tak je v objektu application nastavený request na NULL. Jediný, kdo může v takovém případě rozhodnout, který ErrorPresenter použít, je IMHO router podle toho, která routa se matchne.
Je docela mrzuté, že tenhle problém nemá rozumné řešení. Aspoň předpokládám, že řešení od @enumag trpí stejným neduhem, protože ten request se získává ze stejného objektu (Application).

Běžně tohle nepotřebuji, ale teď řeším modul pro exporty, který komunikuje přes API a má vracet JSON. A v něm bych rád vracel i všechny chyby, které nastanou, pokud se matchne routa toho modulu.

Šaman
Člen | 2635
+
0
-

Místo navrhovaného requestu z parametru mi funguje vytáhnout si HttpRequest přímo z presenteru. Jen se asi nepozná, jesli zachytávám chybu v modulu Export, nebo v presenteru ExportPresenter na frontendu.

<?php
public function renderDefault($exception)
{
	# ExportModule
	$relativeUrl = Strings::lower($this->getHttpRequest()->getUrl()->getRelativeUrl());
	if ( $relativeUrl === 'export' || Strings::startsWith($relativeUrl, 'export/') ) {
		# já tu třeba posílám JsonResponse
	}

	# původní kód
}
?>

Editoval Šaman (3. 5. 2015 1:37)

the_ress
Člen | 1
+
0
-

Pro případy, kdy se presenter nenajde, jsem použila toto:

if ($request === null){ // When presenter doesn't exist, but we can still get a match
  $request = $this->context->router->match($this->getHttpRequest());
}
if ($request && Nette\Utils\Strings::startsWith($request->presenterName, 'Admin:')){
  ...

Route vypadá nějak takto: /admin/<presenter=default>[/<action=default>]

enumag
Člen | 2118
+
0
-

@Šaman Máš pravdu že mé řešení trpí stejným problémem, ale myslím že by se to dalo vyřešit vlastní implementací Application::createInitialRequest().