Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TYPO3 ver. 7.6.2 - Condition ViewHelpers evaluated only once

Problem: I wrote a conditional VH (extending AbstractConditionViewHelper) and it works as usually, anyway I realized that in non-cached version it is evaluated only once. Initialy I thought that's my bug, but checked common <f:if> and the problem is identical :S

In general when I visit my page for the first time, condition is evaluated and valid result is given, but when I'll refresh the page VH isn't called anymore (checked by setting breakpoint inside the VH) and VH is always treated as FALSE. Only any change in view's code will cause that VH will be evaluated once, and again next refresh(es) won't call VH anymore.

typo3conf/ext/toolbox/Classes/ViewHelpers/IsFieldRequiredViewHelper.php:

<?php
namespace Vendor\Toolbox\ViewHelpers;

class IsFieldRequiredViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper {

    /**
     * @param string $fieldName      Current field name
     * @param string $requiredFields List of required names separated by commas
     *
     * @return string the rendered string
     */
    public function render($fieldName, $requiredFields) {

        $requiredArray = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $requiredFields, true);

        return (in_array($fieldName, $requiredArray))
            ? $this->renderThenChild()
            : $this->renderElseChild();
    }
}

Usage:

{namespace toolbox=Vendor\Toolbox\ViewHelpers}

<toolbox:isFieldRequired fieldName="foo" requiredFields="foo, bar, baz">
    <f:then>TRUE</f:then>
    <f:else>FALSE</f:else>
</toolbox:isFieldRequired>

For the first hit I have TRUE but later only FALSE.

Any suggestions? Did I missed some important change in ViewHelpers API since 7.x- ?

Of course if extension is cached it will be not visible, as the first hit will be saved in cache with proper VH return.

like image 992
biesior Avatar asked Jan 11 '16 18:01

biesior


1 Answers

The AbstractConditionViewHelper implements the TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface interface. This means that it implements a compile method that actually returns PHP code that will be stored in the compiled Fluid views.

Have a look at this method in the source code:

 public function compile($argumentsVariableName, $renderChildrenClosureVariableName, &$initializationPhpCode, \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode $syntaxTreeNode, \TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler $templateCompiler)
 {
     foreach ($syntaxTreeNode->getChildNodes() as $childNode) {
         if ($childNode instanceof ViewHelperNode
             && $childNode->getViewHelperClassName() === ThenViewHelper::class) {
             $childNodesAsClosure = $templateCompiler->wrapChildNodesInClosure($childNode);
             $initializationPhpCode .= sprintf('%s[\'__thenClosure\'] = %s;', $argumentsVariableName, $childNodesAsClosure) . LF;
         }
         if ($childNode instanceof ViewHelperNode
             && $childNode->getViewHelperClassName() === ElseViewHelper::class) {
             $childNodesAsClosure = $templateCompiler->wrapChildNodesInClosure($childNode);
             $initializationPhpCode .= sprintf('%s[\'__elseClosure\'] = %s;', $argumentsVariableName, $childNodesAsClosure) . LF;
         }
     }

     return sprintf('%s::renderStatic(%s, %s, $renderingContext)',
         get_class($this), $argumentsVariableName, $renderChildrenClosureVariableName);
 }

Once compiled, the render() method will not be called anymore (it will on the first invocation, when the template is not yet compiled). Instead, the renderStatic() method will be called.

Solution: You can either

  1. also override the renderStatic() method and implement your ViewHelper logic there (again)
  2. not implement the render() method and simply overwrite the static evaluateCondition($arguments) method. This method is actually designed to be overwritten -- the default implementations of both render() and renderStatic() call this method:

    This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.

    static protected function evaluateCondition($arguments = null)
    {
        $requiredArray = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $arguments['requiredFields'], true);
        return (in_array($arguments['fieldName'], $requiredArray));
    }
    
like image 116
helmbert Avatar answered Dec 07 '22 11:12

helmbert