From f22182270b2f33c9e04f78fd480df70656fcd397 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Tue, 5 Sep 2017 14:34:04 +0200 Subject: [PATCH] [BUGFIX] Fix variables not passed to field layout view in TYPO3 8.6 Fluid variables were not passed correctly to the layout standalone view of a field. This commit fixes this issue, as well as a refactor for the older versions behaviour to make the code more readable. --- Classes/ViewHelpers/FieldViewHelper.php | 150 ++++++++++-------- ...LocalizationJavaScriptAssetHandlerTest.php | 2 +- .../Unit/ViewHelpers/FieldViewHelperTest.php | 16 +- 3 files changed, 95 insertions(+), 73 deletions(-) diff --git a/Classes/ViewHelpers/FieldViewHelper.php b/Classes/ViewHelpers/FieldViewHelper.php index dd9a680..57c1373 100644 --- a/Classes/ViewHelpers/FieldViewHelper.php +++ b/Classes/ViewHelpers/FieldViewHelper.php @@ -68,11 +68,6 @@ class FieldViewHelper extends AbstractViewHelper */ protected $slotService; - /** - * @var array - */ - protected $originalArguments = []; - /** * @inheritdoc */ @@ -88,8 +83,6 @@ public function initializeArguments() */ public function render() { - $viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer(); - /* * First, we check if this view helper is called from within the * `FormViewHelper`, because it would not make sense anywhere else. @@ -116,21 +109,11 @@ public function render() */ $this->renderChildren(); - /* - * We need to store original arguments declared for the current view - * context, because we may override them during the rendering of this - * view helper. - */ - $this->storeOriginalArguments(); - - /* - * We merge the arguments with the ones registered with the - * `OptionViewHelper`. - */ - $templateArguments = $this->arguments['arguments'] ?: []; - ArrayUtility::mergeRecursiveWithOverrule($templateArguments, $this->fieldService->getFieldOptions()); + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { + $restoreCallback = $this->storeViewDataLegacy(); + } - $currentView = $viewHelperVariableContainer->getView(); + $templateArguments = $this->getTemplateArguments(); $result = $this->renderLayoutView($templateArguments); @@ -140,14 +123,16 @@ public function render() $this->fieldService->removeCurrentField(); $this->slotService->resetState(); - $viewHelperVariableContainer->setView($currentView); - $this->restoreOriginalArguments($templateArguments); + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { + /** @noinspection PhpUndefinedVariableInspection */ + $restoreCallback($templateArguments); + } return $result; } /** - * Will render the associated Fluid view for this field (configured with the + * Will return the associated Fluid view for this field (configured with the * `layout` argument). * * @param array $templateArguments @@ -168,23 +153,34 @@ protected function renderLayoutView(array $templateArguments) $view = $this->fieldService->getView($layout); + /* + * Warning: we need to store the layouts/partials paths before + * manipulating the rendering context! + */ $layoutPaths = $this->getPaths('layout'); $partialPaths = $this->getPaths('partial'); if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { $view->setRenderingContext($this->renderingContext); } else { - $view->setControllerContext($this->controllerContext); + $renderingContext = $view->getRenderingContext(); - $variableProvider = $this->getVariableProvider(); + // Removing all variables previously added to the provider. + $provider = $renderingContext->getVariableProvider(); - foreach ($templateArguments as $key => $value) { - if ($variableProvider->exists($key)) { - $variableProvider->remove($key); - } - - $variableProvider->add($key, $value); + foreach ($provider->getAllIdentifiers() as $key) { + $provider->remove($key); } + + /* + * Updating the view dependencies: the variable container as well as + * the controller context must be injected in the view. + */ + $renderingContext->setViewHelperVariableContainer($this->viewHelperVariableContainer); + + $view->setControllerContext($this->controllerContext); + + $this->viewHelperVariableContainer->setView($view); } $view->setLayoutRootPaths($layoutPaths); @@ -194,6 +190,56 @@ protected function renderLayoutView(array $templateArguments) return $view->render(); } + /** + * Temporary solution for TYPO3 6.2 to 7.6 that will store the current view + * variables, to be able to restore them later. + * + * A callback function is returned; it will be called once the field layout + * view was processed, and will restore all the view data. + * + * @return \Closure + * + * @deprecated Will be deleted when TYPO3 7.6 is not supported anymore. + */ + protected function storeViewDataLegacy() + { + $originalArguments = []; + + $variableProvider = $this->getVariableProvider(); + + foreach (self::$reservedVariablesNames as $key) { + if ($variableProvider->exists($key)) { + $originalArguments[$key] = $variableProvider->get($key); + } + } + + $viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer(); + $currentView = $viewHelperVariableContainer->getView(); + + return function (array $templateArguments) use ($originalArguments, $variableProvider, $viewHelperVariableContainer, $currentView) { + $viewHelperVariableContainer->setView($currentView); + + /* + * Cleaning up the variables in the provider: the original + * values are restored to make the provider like it was before + * the field rendering started. + */ + foreach ($variableProvider->getAllIdentifiers() as $identifier) { + if (array_key_exists($identifier, $templateArguments)) { + $variableProvider->remove($identifier); + } + } + + foreach ($originalArguments as $key => $value) { + if ($variableProvider->exists($key)) { + $variableProvider->remove($key); + } + + $variableProvider->add($key, $value); + } + }; + } + /** * Will check that the given field exists in the current form definition and * inject it in the `FieldService` as `currentField`. @@ -257,43 +303,17 @@ protected function getLayout(View $viewConfiguration) } /** - * Stores some arguments which may already have been initialized, and could - * be overridden in the local scope. - */ - protected function storeOriginalArguments() - { - $this->originalArguments = []; - $variableProvider = $this->getVariableProvider(); - - foreach (self::$reservedVariablesNames as $key) { - if ($variableProvider->exists($key)) { - $this->originalArguments[$key] = $variableProvider->get($key); - } - } - } - - /** - * Will restore original arguments in the template variable container. + * Merging the arguments with the ones registered with the + * `OptionViewHelper`. * - * @param array $templateArguments + * @return array */ - protected function restoreOriginalArguments(array $templateArguments) + protected function getTemplateArguments() { - $variableProvider = $this->getVariableProvider(); - - foreach ($variableProvider->getAllIdentifiers() as $identifier) { - if (array_key_exists($identifier, $templateArguments)) { - $variableProvider->remove($identifier); - } - } - - foreach ($this->originalArguments as $key => $value) { - if ($variableProvider->exists($key)) { - $variableProvider->remove($key); - } + $templateArguments = $this->arguments['arguments'] ?: []; + ArrayUtility::mergeRecursiveWithOverrule($templateArguments, $this->fieldService->getFieldOptions()); - $variableProvider->add($key, $value); - } + return $templateArguments; } /** diff --git a/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php b/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php index eec8510..c647a86 100644 --- a/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php +++ b/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php @@ -30,7 +30,7 @@ public function checkLocalizationIsInitializedCorrectly() $validation->setClassName(RequiredValidator::class); $field->addValidation($validation); - /** @var FormzLocalizationJavaScriptAssetHandler|\PHPUnit_Framework_MockObject_MockObject $assetHandler */ + /** @var FormzLocalizationJavaScriptAssetHandler|\PHPUnit_Framework_MockObject_MockObject $assetHandler */ $assetHandler = $this->getMockBuilder(FormzLocalizationJavaScriptAssetHandler::class) ->setMethods(['handleRealTranslations', 'handleTranslationsBinding']) ->setConstructorArgs([$assetHandlerFactory]) diff --git a/Tests/Unit/ViewHelpers/FieldViewHelperTest.php b/Tests/Unit/ViewHelpers/FieldViewHelperTest.php index 02f73c1..a62f96a 100644 --- a/Tests/Unit/ViewHelpers/FieldViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/FieldViewHelperTest.php @@ -338,13 +338,15 @@ public function originalArgumentsAreRestoredAfterViewHelperIsRendered() ->will(function ($arguments) use ($templateVariableContainerProphecy, &$templateVariables) { $templateVariables[$arguments[0]] = $arguments[1]; - if (in_array($arguments[0], FieldViewHelper::$reservedVariablesNames)) { - $templateVariableContainerProphecy - ->remove($arguments[0]) - ->shouldBeCalled() - ->will(function () use ($arguments, &$templateVariables) { - unset($templateVariables[$arguments[0]]); - }); + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { + if (in_array($arguments[0], FieldViewHelper::$reservedVariablesNames)) { + $templateVariableContainerProphecy + ->remove($arguments[0]) + ->shouldBeCalled() + ->will(function () use ($arguments, &$templateVariables) { + unset($templateVariables[$arguments[0]]); + }); + } } $templateVariableContainerProphecy