From 1573636972d78c892646e6f9d70654be23716467 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 17 Apr 2024 16:36:15 +0200 Subject: [PATCH] Presenter: added switch() --- src/Application/UI/Presenter.php | 34 +++++++++++++++++-- src/Application/exceptions.php | 5 +++ .../UI/LinkGenerator.parseDestination().phpt | 2 +- tests/UI/Requires.forward.phpt | 29 ++++++++++++++++ 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/Application/UI/Presenter.php b/src/Application/UI/Presenter.php index 68a78a748..635f88801 100644 --- a/src/Application/UI/Presenter.php +++ b/src/Application/UI/Presenter.php @@ -99,6 +99,7 @@ abstract class Presenter extends Control implements Application\IPresenter private ?array $globalStateSinces; private string $action = ''; private string $view = ''; + private bool $forwarded = false; private string|bool $layout = ''; private \stdClass $payload; private string $signalReceiver; @@ -188,7 +189,14 @@ public function run(Application\Request $request): Application\Response } // calls $this->action() - $this->tryCall(static::formatActionMethod($this->action), $this->params); + try { + actionMethod: + $this->tryCall(static::formatActionMethod($this->action), $this->params); + } catch (Application\SwitchException $e) { + $this->changeAction($e->getMessage()); + $this->autoCanonicalize = false; + goto actionMethod; + } // autoload components foreach ($this->globalParams as $id => $foo) { @@ -211,12 +219,20 @@ public function run(Application\Request $request): Application\Response $this->beforeRender(); Arrays::invoke($this->onRender, $this); // calls $this->render() - $this->tryCall(static::formatRenderMethod($this->view), $this->params); + try { + renderMethod: + $this->tryCall(static::formatRenderMethod($this->view), $this->params); + } catch (Application\SwitchException $e) { + $this->setView($e->getMessage()); + goto renderMethod; + } $this->afterRender(); // finish template rendering $this->sendTemplate(); + } catch (Application\SwitchException $e) { + throw new Nette\InvalidStateException('Switch is only supported inside action*() or render*() method.', 0, $e); } catch (Application\AbortException) { } @@ -327,7 +343,7 @@ public function checkRequirements(\ReflectionClass|\ReflectionMethod $element): if ( $attribute->forward && !$this->request->isMethod($this->request::FORWARD) - && $this->action === $this->view + && !$this->forwarded ) { $this->error('Forwarded request is required by ' . Reflection::toString($element)); } @@ -451,10 +467,20 @@ final public function getAction(bool $fullyQualified = false): string */ public function changeAction(string $action): void { + $this->forwarded = true; $this->action = $this->view = $action; } + /** + * Switch from current action or render method to another. + */ + public function switch(string $action): never + { + throw new Application\SwitchException($action); + } + + /** * Returns current view. */ @@ -469,6 +495,7 @@ final public function getView(): string */ public function setView(string $view): static { + $this->forwarded = true; $this->view = $view; return $this; } @@ -1072,6 +1099,7 @@ private function initGlobalParameters(): void } $this->changeAction($action); + $this->forwarded = false; // init $this->signalReceiver and key 'signal' in appropriate params array $this->signalReceiver = $this->getUniqueId(); diff --git a/src/Application/exceptions.php b/src/Application/exceptions.php index a23d83a10..11aea987d 100644 --- a/src/Application/exceptions.php +++ b/src/Application/exceptions.php @@ -20,6 +20,11 @@ class AbortException extends \LogicException { } +/** @internal */ +final class SwitchException extends AbortException +{ +} + /** * Application fatal error. diff --git a/tests/UI/LinkGenerator.parseDestination().phpt b/tests/UI/LinkGenerator.parseDestination().phpt index d520bb51c..5132e99a4 100644 --- a/tests/UI/LinkGenerator.parseDestination().phpt +++ b/tests/UI/LinkGenerator.parseDestination().phpt @@ -2,8 +2,8 @@ declare(strict_types=1); -use Nette\Application\UI\InvalidLinkException; use Nette\Application\LinkGenerator; +use Nette\Application\UI\InvalidLinkException; use Tester\Assert; diff --git a/tests/UI/Requires.forward.phpt b/tests/UI/Requires.forward.phpt index f54f030a3..63e1e289e 100644 --- a/tests/UI/Requires.forward.phpt +++ b/tests/UI/Requires.forward.phpt @@ -41,6 +41,22 @@ class TestForwardViewPresenter extends Nette\Application\UI\Presenter } +class TestSwitchViewPresenter extends Nette\Application\UI\Presenter +{ + public function actionDefault(): void + { + $this->switch('forward'); + } + + + #[Requires(forward: true)] + public function actionSwitch(): void + { + $this->terminate(); + } +} + + // forwarded request $presenter = createPresenter(TestForwardPresenter::class); Assert::noError( @@ -65,3 +81,16 @@ Assert::exception( Application\BadRequestException::class, 'Forwarded request is required by TestForwardViewPresenter::renderForward()', ); + + +// switched view +$presenter = createPresenter(TestSwitchViewPresenter::class); +Assert::noError( + fn() => $presenter->run(new Application\Request('', Http\Request::Get)), +); + +Assert::exception( + fn() => $presenter->run(new Application\Request('', Http\Request::Get, ['action' => 'forward'])), + Application\BadRequestException::class, + 'Forwarded request is required by TestSwitchViewPresenter::actionSwitch()', +);