Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Allow defining arguments for f:render with f:argument #429

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/Resources/Private/Singles/Variables.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
}
}"/>

<!-- Passing arguments to a sections/partials using f:render in tag content -->
<f:render section="Tertiary">
<f:argument name="arg1">First argument</f:argument>
<f:argument name="arg2">Second argument</f:argument>
</f:render>

</f:section>

<f:section name="Secondary">
Expand All @@ -49,3 +55,8 @@
Received $array.xyz.foobar with value {array.xyz.foobar}
Received $myVariable with value {myVariable}
</f:section>

<f:section name="Tertiary">
Input argument "arg1" was: {arg1}
Input argument "arg2" was: {arg2}
</f:section>
64 changes: 64 additions & 0 deletions src/Core/ViewHelper/ViewHelperVariableContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* See LICENSE.txt that was shipped with this package.
*/

use TYPO3Fluid\Fluid\Core\Variables\VariableProviderInterface;
use TYPO3Fluid\Fluid\View\ViewInterface;

/**
Expand All @@ -29,6 +30,69 @@ class ViewHelperVariableContainer
*/
protected $view;

/**
* @var VariableProviderInterface[]
*/
protected $delegates = [];

/**
* Push a new delegate variable container to a stack.
*
* If a ViewHelper requires a storage to collect variables which, for
* example, are filled by evaluating the child (tag content) closure,
* this method can be used to add a special delegate variable container
* stored in a stack. Once the variables you need to collect have been
* collected, calling `popDelegateVariableProvider` removes the delegate
* from the stack.
*
* The point of a stack is to avoid resetting a storage every time a
* ViewHelper is rendered. In the case of `f:render` it means one storage
* is created and filled for every one call to the ViewHelper.
*
* It is VITAL that you also "pop" any delegate you push to this stack!
*
* @param VariableProviderInterface $variableProvider
*/
public function pushDelegateVariableProvider(VariableProviderInterface $variableProvider)
{
$this->delegates[] = $variableProvider;
}

/**
* Get the topmost delegate variable container that was previously pushed
* onto the stack by pushDelegateVariableContainer(). This method returns
* a reference to the storage that was last added to the stack without
* removing the variable provider from the stack.
*
* Is used in ViewHelpers that assign variables in variable providers in
* the stack - as a means to get the variable storage used by the "closest
* parent", e.g. when called in `f:argument` used inside `f:render`, will
* read the delegate variable provider inserted by that parent `f:render`.
*
* @return VariableProviderInterface|null
*/
public function getTopmostDelegateVariableProvider()
{
return end($this->delegates) ?: null;
mbrodala marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Return and REMOVE the topmost delegate variable provider. This method
* must be called after you finish sub-rendering with a delegated variable
* provider that was added with `pushDelegateVariableProvider`. Calling
* the method removes the delegate and returns the stack to the previous
* state it was in.
*
* To avoid removing from the stack, use `getTopmostDelegateVariableProvider`.
*
* @param string $viewHelperClassName
* @return VariableProviderInterface|null
*/
public function popDelegateVariableProvider()
{
return array_pop($this->delegates);
}

/**
* Add a variable to the Variable Container. Make sure that $viewHelperName is ALWAYS set
* to your fully qualified ViewHelper Class Name
Expand Down
68 changes: 68 additions & 0 deletions src/ViewHelpers/ArgumentViewHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
namespace TYPO3Fluid\Fluid\ViewHelpers;

/*
* This file belongs to the package "TYPO3 Fluid".
* See LICENSE.txt that was shipped with this package.
*/

use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;

/**
* Argument assigning ViewHelper
*
* Assigns an argument for a parent ViewHelper call when
* the parent ViewHelper supports it.
*
* Alternative to declaring an array to pass as "arguments".
*
* Usages:
*
* <f:render partial="Foo">
* <f:argument name="arg1">Value1</f:argument>
* <f:argument name="arg2">Value2</f:argument>
* </f:render>
*
* Which is the equivalent of:
*
* <f:render partial="Foo" arguments="{arg1: 'Value1', arg2: 'Value2'}'" />
*
* But has the benefit that writing ViewHelper expressions or
* other more complex syntax becomes much easier because you
* can use tag syntax (tag content becomes argument value).
*
* @api
*/
class ArgumentViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;

/**
* @return void
*/
public function initializeArguments()
{
$this->registerArgument('value', 'mixed', 'Value to assign. If not in arguments then taken from tag content');
mbrodala marked this conversation as resolved.
Show resolved Hide resolved
$this->registerArgument('name', 'string', 'Name of variable to create', true);
}

/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return null
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$delegateVariableProvider = $renderingContext->getViewHelperVariableContainer()->getTopmostDelegateVariableProvider();
if ($delegateVariableProvider) {
mbrodala marked this conversation as resolved.
Show resolved Hide resolved
$delegateVariableProvider->add($arguments['name'], $renderChildrenClosure());
}
}

}
15 changes: 14 additions & 1 deletion src/ViewHelpers/RenderViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
class RenderViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;

/**
* @var boolean
*/
Expand Down Expand Up @@ -121,7 +121,20 @@ public static function renderStatic(array $arguments, \Closure $renderChildrenCl
$delegate = $arguments['delegate'];
/** @var RenderableInterface $renderable */
$renderable = $arguments['renderable'];

// Prepare a delegate variable provider that will be possible to extract after rendering the child closure.
// Any variable defined therein gets used as argument and overrides any argument of the same name.
// Note: not using late static binding here is a conscious decision: if late static binding had been used
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed that this comment is no longer correct, since removing the VH scope argument from the methods it relates to.

// then f:variable would not be able to reference this ViewHelper class' stack variable correctly.
$viewHelperVariableContainer = $renderingContext->getViewHelperVariableContainer();
$collector = $renderingContext->getVariableProvider()->getScopeCopy($variables);

$viewHelperVariableContainer->pushDelegateVariableProvider($collector);

$tagContent = $renderChildrenClosure();

$variables = $viewHelperVariableContainer->popDelegateVariableProvider()->getAll();

if ($arguments['contentAs']) {
$variables[$arguments['contentAs']] = $tagContent;
}
Expand Down
4 changes: 3 additions & 1 deletion tests/Functional/ExamplesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ public function getExampleScriptTestValues()
'Received $array.printf with formatted string Formatted string, value: formatted',
'Received $array.baz with value 42',
'Received $array.xyz.foobar with value Escaped sub-string',
'Received $myVariable with value Nice string'
'Received $myVariable with value Nice string',
'Input argument "arg1" was: First argument',
'Input argument "arg2" was: Second argument',
]
],
'example_variableprovider.php' => [
Expand Down