TYPO3 版本7.6.2 - 条件 ViewHelpers 仅评估一次
TYPO3 ver. 7.6.2 - Condition ViewHelpers evaluated only once
问题: 我写了一个条件 VH(扩展 AbstractConditionViewHelper
)并且它像往常一样工作,无论如何我意识到在 non-cached版本它只被评估一次。最初我以为那是我的错误,但检查了 common <f:if>
并且问题是相同的 :S
一般来说,当我第一次访问我的页面时,会评估条件并给出有效结果,但是当我刷新页面时,不再调用 VH(通过在 VH 内设置断点进行检查)和VH 始终被视为 FALSE。只有视图代码中的任何更改都会导致 VH 被评估一次,并且下一次刷新将不再调用 VH。
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();
}
}
用法:
{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>
第一次点击我有 TRUE
但后来只有 FALSE
。
有什么建议吗?自 7.x- 以来,我是否错过了 ViewHelpers API 中的一些重要变化?
当然,如果扩展被缓存,它将不可见,因为第一次命中将以适当的 VH 保存在缓存中 return。
AbstractConditionViewHelper
实现了TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface
接口。这意味着它实现了一个 compile
方法,该方法实际上 returns PHP 代码将存储在已编译的 Fluid 视图中。
看看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);
}
编译后,render()
方法将不再被调用(它将在第一次调用时调用,此时模板尚未编译)。相反,将调用 renderStatic()
方法。
解决方法:你可以
- 还覆盖
renderStatic()
方法并在那里(再次)实现您的 ViewHelper 逻辑
不实现 render()
方法 并简单地覆盖静态 evaluateCondition($arguments)
方法。这个方法实际上是为了被覆盖而设计的——render()
和 renderStatic()
的默认实现都调用这个方法:
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));
}
最快的解决方案是像这样覆盖 class 渲染和评估条件:
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('yourArgument','array','',true);
}
public function render()
{
return self::evaluateCondition($this->arguments) ? $this->renderThenChild() : $this->renderElseChild();
}
/**
* @return bool
*/
protected static function evaluateCondition($arguments = null)
{
//do your stuff
return true;
}
问题: 我写了一个条件 VH(扩展 AbstractConditionViewHelper
)并且它像往常一样工作,无论如何我意识到在 non-cached版本它只被评估一次。最初我以为那是我的错误,但检查了 common <f:if>
并且问题是相同的 :S
一般来说,当我第一次访问我的页面时,会评估条件并给出有效结果,但是当我刷新页面时,不再调用 VH(通过在 VH 内设置断点进行检查)和VH 始终被视为 FALSE。只有视图代码中的任何更改都会导致 VH 被评估一次,并且下一次刷新将不再调用 VH。
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();
}
}
用法:
{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>
第一次点击我有 TRUE
但后来只有 FALSE
。
有什么建议吗?自 7.x- 以来,我是否错过了 ViewHelpers API 中的一些重要变化?
当然,如果扩展被缓存,它将不可见,因为第一次命中将以适当的 VH 保存在缓存中 return。
AbstractConditionViewHelper
实现了TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface
接口。这意味着它实现了一个 compile
方法,该方法实际上 returns PHP 代码将存储在已编译的 Fluid 视图中。
看看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);
}
编译后,render()
方法将不再被调用(它将在第一次调用时调用,此时模板尚未编译)。相反,将调用 renderStatic()
方法。
解决方法:你可以
- 还覆盖
renderStatic()
方法并在那里(再次)实现您的 ViewHelper 逻辑 不实现
render()
方法 并简单地覆盖静态evaluateCondition($arguments)
方法。这个方法实际上是为了被覆盖而设计的——render()
和renderStatic()
的默认实现都调用这个方法: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)); }
最快的解决方案是像这样覆盖 class 渲染和评估条件:
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('yourArgument','array','',true);
}
public function render()
{
return self::evaluateCondition($this->arguments) ? $this->renderThenChild() : $this->renderElseChild();
}
/**
* @return bool
*/
protected static function evaluateCondition($arguments = null)
{
//do your stuff
return true;
}