Compare commits
No commits in common. "develop" and "main" have entirely different histories.
24 changed files with 0 additions and 751 deletions
|
@ -1,7 +0,0 @@
|
||||||
steps:
|
|
||||||
code-style:
|
|
||||||
image: git.digital-competence.de/woodpecker-ci/plugin-phpcs
|
|
||||||
settings:
|
|
||||||
args: Classes/ Tests/
|
|
||||||
when:
|
|
||||||
- event: [push, pull_request, manual]
|
|
|
@ -1,22 +0,0 @@
|
||||||
workspace:
|
|
||||||
base: /woodpecker
|
|
||||||
path: package
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- FLOW_VERSION: 6.3
|
|
||||||
PHP_VERSION: 7.4
|
|
||||||
- FLOW_VERSION: 7.3
|
|
||||||
PHP_VERSION: 7.4
|
|
||||||
- FLOW_VERSION: 7.3
|
|
||||||
PHP_VERSION: 8.2
|
|
||||||
- FLOW_VERSION: 8.2
|
|
||||||
PHP_VERSION: 8.2
|
|
||||||
|
|
||||||
steps:
|
|
||||||
functional-tests:
|
|
||||||
image: git.digital-competence.de/woodpecker-ci/plugin-phpunit-flow:${PHP_VERSION}
|
|
||||||
settings:
|
|
||||||
flow_version: ${FLOW_VERSION}
|
|
||||||
when:
|
|
||||||
- event: [ push, pull_request, manual ]
|
|
|
@ -1,69 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\FormExtensions;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\InvokeRenderFunctionInterface;
|
|
||||||
use DigiComp\FluidRenderFunctions\Utils\GeneratorClosureIterator;
|
|
||||||
use DigiComp\FluidRenderFunctions\Utils\RenderableProxy;
|
|
||||||
use DigiComp\FluidRenderFunctions\ViewHelpers\RegisterRenderFunctionViewHelper;
|
|
||||||
use Neos\Flow\Annotations as Flow;
|
|
||||||
use Neos\Flow\Aop\JoinPointInterface;
|
|
||||||
use Neos\FluidAdaptor\ViewHelpers\Form\SelectViewHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Flow\Aspect
|
|
||||||
*/
|
|
||||||
class SelectAspect extends SelectViewHelper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @Flow\After("setting(DigiComp.FluidRenderFunctions.enableAspects.select) && method(Neos\FluidAdaptor\ViewHelpers\Form\SelectViewHelper->initializeArguments())")
|
|
||||||
*/
|
|
||||||
public function introduceRenderFuncArgument(JoinPointInterface $joinPoint): void
|
|
||||||
{
|
|
||||||
$proxy = $joinPoint->getProxy();
|
|
||||||
if (!($proxy instanceof SelectViewHelper)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$proxy->registerArgument('renderFunction', 'string', 'callabe to use to render single object');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Flow\After("setting(DigiComp.FluidRenderFunctions.enableAspects.select) && method(Neos\FluidAdaptor\ViewHelpers\Form\SelectViewHelper->validateArguments())")
|
|
||||||
*/
|
|
||||||
public function validateRenderFunction(JoinPointInterface $joinPoint): void
|
|
||||||
{
|
|
||||||
$proxy = $joinPoint->getProxy();
|
|
||||||
if (!($proxy instanceof SelectViewHelper)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isset($proxy->arguments['renderFunction'])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$renderFunction = $proxy->viewHelperVariableContainer->get(
|
|
||||||
RegisterRenderFunctionViewHelper::class,
|
|
||||||
$proxy->arguments['renderFunction']
|
|
||||||
);
|
|
||||||
if (!($renderFunction instanceof InvokeRenderFunctionInterface)) {
|
|
||||||
throw new \InvalidArgumentException(
|
|
||||||
'render function with name "' . $proxy->arguments['renderFunction'] . '" has not been registered.',
|
|
||||||
1717293038
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$proxy->arguments['renderFunction'] = $renderFunction;
|
|
||||||
$originalOptions = $proxy->arguments['options'];
|
|
||||||
if (!\is_iterable($originalOptions)) {
|
|
||||||
// Validation is left to the original view helper
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$proxy->arguments['options'] = new GeneratorClosureIterator(
|
|
||||||
static function () use ($originalOptions, $renderFunction) {
|
|
||||||
foreach ($originalOptions as $option) {
|
|
||||||
yield new RenderableProxy($renderFunction, $option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\FormExtensions;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\InvokeRenderFunctionInterface;
|
|
||||||
use DigiComp\FluidRenderFunctions\Utils\RenderableProxy;
|
|
||||||
use DigiComp\FluidRenderFunctions\ViewHelpers\RegisterRenderFunctionViewHelper;
|
|
||||||
use Neos\Flow\Annotations as Flow;
|
|
||||||
use Neos\Flow\Aop\JoinPointInterface;
|
|
||||||
use Neos\FluidAdaptor\ViewHelpers\Form\TextfieldViewHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Flow\Aspect
|
|
||||||
*/
|
|
||||||
class TextfieldAspect extends TextfieldViewHelper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @Flow\After("setting(DigiComp.FluidRenderFunctions.enableAspects.textfield) && method(Neos\FluidAdaptor\ViewHelpers\Form\TextfieldViewHelper->initializeArguments())")
|
|
||||||
*/
|
|
||||||
public function introduceRenderFuncArgument(JoinPointInterface $joinPoint): void
|
|
||||||
{
|
|
||||||
$proxy = $joinPoint->getProxy();
|
|
||||||
if (!($proxy instanceof TextfieldViewHelper)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$proxy->registerArgument('renderFunction', 'string', 'callabe to use to render single object');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Flow\Around("setting(DigiComp.FluidRenderFunctions.enableAspects.textfield) && method(Neos\FluidAdaptor\ViewHelpers\Form\TextfieldViewHelper->getValueAttribute())")
|
|
||||||
*/
|
|
||||||
public function applyRenderFunctionToValue(JoinPointInterface $joinPoint)
|
|
||||||
{
|
|
||||||
$proxy = $joinPoint->getProxy();
|
|
||||||
if (!($proxy instanceof TextfieldViewHelper)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isset($proxy->arguments['renderFunction'])) {
|
|
||||||
return $joinPoint->getAdviceChain()->proceed($joinPoint);
|
|
||||||
}
|
|
||||||
$renderFunction = $proxy->viewHelperVariableContainer->get(
|
|
||||||
RegisterRenderFunctionViewHelper::class,
|
|
||||||
$proxy->arguments['renderFunction']
|
|
||||||
);
|
|
||||||
if (!($renderFunction instanceof InvokeRenderFunctionInterface)) {
|
|
||||||
throw new \InvalidArgumentException(
|
|
||||||
'render function with name "' . $proxy->arguments['renderFunction'] . '" has not been registered.',
|
|
||||||
1717293038
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$originalObject = $joinPoint->getAdviceChain()->proceed($joinPoint);
|
|
||||||
if (\is_object($originalObject)) {
|
|
||||||
return new RenderableProxy($renderFunction, $originalObject);
|
|
||||||
}
|
|
||||||
return $originalObject;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions;
|
|
||||||
|
|
||||||
interface InvokeRenderFunctionInterface
|
|
||||||
{
|
|
||||||
public function __invoke($object): string;
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\Utils;
|
|
||||||
|
|
||||||
use TYPO3Fluid\Fluid\View\ViewInterface;
|
|
||||||
|
|
||||||
// @deprecated: drop this class, as soon as Flow 6.3 support drops
|
|
||||||
class DummyView implements ViewInterface
|
|
||||||
{
|
|
||||||
public function assign($key, $value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function assignMultiple(array $values)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderSection($sectionName, array $variables = [], $ignoreUnknown = false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderPartial($partialName, $sectionName, array $variables, $ignoreUnknown = false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\Utils;
|
|
||||||
|
|
||||||
class GeneratorClosureIterator implements \IteratorAggregate
|
|
||||||
{
|
|
||||||
private \Closure $closure;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Closure $closure
|
|
||||||
*/
|
|
||||||
public function __construct(\Closure $closure)
|
|
||||||
{
|
|
||||||
$this->closure = $closure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getIterator(): \Generator
|
|
||||||
{
|
|
||||||
return ($this->closure)();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\Utils;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\InvokeRenderFunctionInterface;
|
|
||||||
use Neos\Flow\ObjectManagement\Proxy\ProxyInterface;
|
|
||||||
use Neos\FluidAdaptor\Core\Rendering\RenderingContext;
|
|
||||||
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\NodeInterface;
|
|
||||||
|
|
||||||
class NodeRenderTransfer implements InvokeRenderFunctionInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var NodeInterface
|
|
||||||
*/
|
|
||||||
protected NodeInterface $node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected string $subjectName;
|
|
||||||
|
|
||||||
public function __construct(NodeInterface $node, string $subjectName = 'object')
|
|
||||||
{
|
|
||||||
$this->node = $node;
|
|
||||||
$this->subjectName = $subjectName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke($object): string
|
|
||||||
{
|
|
||||||
// @deprecated: drop the condition, if compatibility to Flow 6.3 can be dropped
|
|
||||||
$reflector = new \ReflectionClass(RenderingContext::class);
|
|
||||||
if ($reflector->implementsInterface(ProxyInterface::class)) {
|
|
||||||
try {
|
|
||||||
$reflector = new \ReflectionClass($reflector->getParentClass()->getName());
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
// nothing, go with the first one
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$constructor = $reflector->getConstructor();
|
|
||||||
|
|
||||||
if ($constructor->getNumberOfRequiredParameters() > 0) {
|
|
||||||
$context = new RenderingContext(new DummyView());
|
|
||||||
} else {
|
|
||||||
$context = new RenderingContext();
|
|
||||||
}
|
|
||||||
$context->getVariableProvider()->add($this->subjectName, $object);
|
|
||||||
return \trim($this->node->evaluate($context));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\Utils;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\InvokeRenderFunctionInterface;
|
|
||||||
use Neos\Utility\Exception\PropertyNotAccessibleException;
|
|
||||||
use Neos\Utility\ObjectAccess;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The RenderableProxy wraps original objects and provides a __toString() function
|
|
||||||
*
|
|
||||||
* Because of that, you could use it, to wrap your objects in it and pass the wrapped objects to SelectViewHelper
|
|
||||||
*/
|
|
||||||
class RenderableProxy
|
|
||||||
{
|
|
||||||
protected InvokeRenderFunctionInterface $renderFunction;
|
|
||||||
protected $object;
|
|
||||||
public ?string $Persistence_Object_Identifier = null;
|
|
||||||
|
|
||||||
public function __construct(InvokeRenderFunctionInterface $renderFunction, $object)
|
|
||||||
{
|
|
||||||
$this->renderFunction = $renderFunction;
|
|
||||||
$this->object = $object;
|
|
||||||
try {
|
|
||||||
$this->Persistence_Object_Identifier =
|
|
||||||
ObjectAccess::getProperty($object, 'Persistence_Object_Identifier', true);
|
|
||||||
} catch (PropertyNotAccessibleException $e) {
|
|
||||||
// ok. fine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
$render = $this->renderFunction;
|
|
||||||
return $render($this->object);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\ViewHelpers;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\InvokeRenderFunctionInterface;
|
|
||||||
use DigiComp\FluidRenderFunctions\Utils\GeneratorClosureIterator;
|
|
||||||
use DigiComp\FluidRenderFunctions\Utils\RenderableProxy;
|
|
||||||
use DigiComp\FluidRenderFunctions\ViewHelpers\Traits\ValidateRenderFunctionTrait;
|
|
||||||
use Neos\FluidAdaptor\Core\ViewHelper\AbstractViewHelper;
|
|
||||||
|
|
||||||
class ApplyRenderFunctionViewHelper extends AbstractViewHelper
|
|
||||||
{
|
|
||||||
use ValidateRenderFunctionTrait;
|
|
||||||
|
|
||||||
public function initializeArguments(): void
|
|
||||||
{
|
|
||||||
parent::initializeArguments();
|
|
||||||
$this->registerArgument('in', 'mixed', 'subject to apply the render function to');
|
|
||||||
$this->registerArgument('function', 'string', 'render function to use', true);
|
|
||||||
$this->registerArgument(
|
|
||||||
'force',
|
|
||||||
'bool',
|
|
||||||
'if set, it will be applied to the provided in, if not, it will be applied to each item for '
|
|
||||||
. 'iterables instead',
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validateArguments()
|
|
||||||
{
|
|
||||||
parent::validateArguments();
|
|
||||||
$this->validateRenderFunctionArgument('function');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
$in = $this->arguments['in'];
|
|
||||||
if ($in === null) {
|
|
||||||
$in = $this->renderChildren();
|
|
||||||
}
|
|
||||||
if (\is_iterable($in) && $this->arguments['force'] === false) {
|
|
||||||
return new GeneratorClosureIterator(fn () => $this->getProxyGenerator($this->arguments['function'], $in));
|
|
||||||
} else {
|
|
||||||
return new RenderableProxy($this->arguments['function'], $in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProxyGenerator(InvokeRenderFunctionInterface $function, iterable $objects): \Generator
|
|
||||||
{
|
|
||||||
foreach ($objects as $object) {
|
|
||||||
yield new RenderableProxy($function, $object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\ViewHelpers;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\Utils\NodeRenderTransfer;
|
|
||||||
use Neos\FluidAdaptor\Core\ViewHelper\AbstractViewHelper;
|
|
||||||
use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
|
|
||||||
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode;
|
|
||||||
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
|
|
||||||
|
|
||||||
class RegisterRenderFunctionViewHelper extends AbstractViewHelper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @throws \Neos\FluidAdaptor\Core\ViewHelper\Exception
|
|
||||||
*/
|
|
||||||
public function initializeArguments(): void
|
|
||||||
{
|
|
||||||
parent::initializeArguments();
|
|
||||||
$this->registerArgument('as', 'string', 'Name of the registered render function', false, 'renderFunc');
|
|
||||||
$this->registerArgument(
|
|
||||||
'subjectName',
|
|
||||||
'string',
|
|
||||||
'Name of the argument passed to render function',
|
|
||||||
false,
|
|
||||||
'subject'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function compile(
|
|
||||||
$argumentsName,
|
|
||||||
$closureName,
|
|
||||||
&$initializationPhpCode,
|
|
||||||
ViewHelperNode $node,
|
|
||||||
TemplateCompiler $compiler
|
|
||||||
) {
|
|
||||||
// we disable compiling, because we will need access to the AST, which is not available if compiled
|
|
||||||
// a cool improvement would be to drop the need to the AST and so become compilable
|
|
||||||
$compiler->disable();
|
|
||||||
return "''";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(): string
|
|
||||||
{
|
|
||||||
$transferNode = new RootNode();
|
|
||||||
foreach ($this->childNodes as $childNode) {
|
|
||||||
$transferNode->addChildNode($childNode);
|
|
||||||
}
|
|
||||||
$renderer = new NodeRenderTransfer($transferNode, $this->arguments['subjectName']);
|
|
||||||
$this->renderingContext->getViewHelperVariableContainer()
|
|
||||||
->add(static::class, $this->arguments['as'], $renderer);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\ViewHelpers\Traits;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\InvokeRenderFunctionInterface;
|
|
||||||
use DigiComp\FluidRenderFunctions\ViewHelpers\RegisterRenderFunctionViewHelper;
|
|
||||||
|
|
||||||
trait ValidateRenderFunctionTrait
|
|
||||||
{
|
|
||||||
public function validateRenderFunctionArgument(string $argumentName)
|
|
||||||
{
|
|
||||||
$renderFunction = $this->viewHelperVariableContainer->get(
|
|
||||||
RegisterRenderFunctionViewHelper::class,
|
|
||||||
$this->arguments[$argumentName]
|
|
||||||
);
|
|
||||||
if (!($renderFunction instanceof InvokeRenderFunctionInterface)) {
|
|
||||||
throw new \InvalidArgumentException(
|
|
||||||
'render function with name "' . $this->arguments[$argumentName] . '" has not been registered.',
|
|
||||||
1717293038
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$this->arguments[$argumentName] = $renderFunction;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
DigiComp:
|
|
||||||
FluidRenderFunctions:
|
|
||||||
enableAspects:
|
|
||||||
select: true
|
|
||||||
textfield: true
|
|
19
LICENSE
19
LICENSE
|
@ -1,19 +0,0 @@
|
||||||
Copyright (c) 2024 Ferdinand Kuhl <f.kuhl@digital-competence.de>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
25
README.md
25
README.md
|
@ -1,25 +0,0 @@
|
||||||
# DigiComp.FluidRenderFunctions
|
|
||||||
|
|
||||||
## Quickstart
|
|
||||||
|
|
||||||
This Package provides you with the possibility to register render functions, created from them, to use them dynamically else where.
|
|
||||||
|
|
||||||
Let me show you the idea:
|
|
||||||
```html
|
|
||||||
<f:form.select options="{books}" />
|
|
||||||
```
|
|
||||||
Assuming you know how the SelectViewHelper works, you know, you can provide an "optionLabelField"-argument to adivce the ViewHelper to use a property of your options.
|
|
||||||
But, what if you want to use a complete template, to display your books?
|
|
||||||
FluidRenderFunctions to the rescue:
|
|
||||||
```html
|
|
||||||
<rf:registerRenderFunction as="renderBook" subjectName="myBook">
|
|
||||||
{myBook.name} from {myBook.author.name}
|
|
||||||
</rf:registerRenderFunction>
|
|
||||||
<f:form.select options="{books -> rf:applyRenderFunction(function: 'renderBook')}" />
|
|
||||||
```
|
|
||||||
|
|
||||||
To make your live easier, FluidRenderFunctions augments the original `SelectViewHelper` and the `TextfieldViewHelper` with an optional `renderFunction` argument. That way, you can even use the usual Textfield to display formatted Datetime objects. Neat!
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
If - for whatever reason - you do not want FluidRenderFunctions to augment the original ViewHelpers you can opt out by setting `DigiComp.FluidRenderFunctions.enableAspects.select` or `DigiComp.FluidRenderFunctions.enableAspects.textfield` to `false`.
|
|
|
@ -1 +0,0 @@
|
||||||
<f:render section="Content" />
|
|
|
@ -1,6 +0,0 @@
|
||||||
{namespace rf=DigiComp\FluidRenderFunctions\ViewHelpers}
|
|
||||||
|
|
||||||
<rf:registerRenderFunction as="renderTag">
|
|
||||||
{subject.name} is cool
|
|
||||||
</rf:registerRenderFunction>
|
|
||||||
<f:form.select options="{tags}" renderFunction="renderTag" />
|
|
|
@ -1,11 +0,0 @@
|
||||||
{namespace rf=DigiComp\FluidRenderFunctions\ViewHelpers}
|
|
||||||
|
|
||||||
<rf:registerRenderFunction as="renderDate">
|
|
||||||
{subject -> f:format.date(format: 'd.m.Y')}
|
|
||||||
</rf:registerRenderFunction>
|
|
||||||
<f:form action="extendedTextField">
|
|
||||||
<f:form.textfield value="{now}" renderFunction="renderDate" />
|
|
||||||
</f:form>
|
|
||||||
<f:form object="{post}" objectName="post" action="extendedTextfield">
|
|
||||||
<f:form.textfield property="publishedAt" renderFunction="renderDate" />
|
|
||||||
</f:form>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<f:layout name="Test" />
|
|
||||||
{namespace rf=DigiComp\FluidRenderFunctions\ViewHelpers}
|
|
||||||
<f:section name="Content"><f:spaceless>
|
|
||||||
<rf:registerRenderFunction as="testFunc">
|
|
||||||
{subject.name} is cool
|
|
||||||
</rf:registerRenderFunction>
|
|
||||||
{test -> rf:applyRenderFunction(function: 'testFunc', force: true)}
|
|
||||||
</f:spaceless></f:section>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<f:layout name="Test" />
|
|
||||||
{namespace rf=DigiComp\FluidRenderFunctions\ViewHelpers}
|
|
||||||
<f:section name="Content"><f:spaceless>
|
|
||||||
<rf:registerRenderFunction as="testFunc">
|
|
||||||
{subject.name} is cool
|
|
||||||
</rf:registerRenderFunction>
|
|
||||||
<f:form.select options="{testEntities -> rf:applyRenderFunction(function: 'testFunc')}" />
|
|
||||||
</f:spaceless></f:section>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\Tests\Functional\Fixtures\Controller;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\Tests\Functional\Fixtures\Domain\Post;
|
|
||||||
use Neos\Flow\Mvc\Controller\ActionController;
|
|
||||||
use Neos\Flow\Persistence\Doctrine\Query;
|
|
||||||
use Neos\FluidAdaptor\Tests\Functional\Form\Fixtures\Domain\Model\Tag;
|
|
||||||
|
|
||||||
class TestController extends ActionController
|
|
||||||
{
|
|
||||||
public function indexAction()
|
|
||||||
{
|
|
||||||
$this->view->assign('test', ['name' => 'hallo']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function selectAction()
|
|
||||||
{
|
|
||||||
$this->view->assign('testEntities', (new Query(Tag::class))->execute());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function extendedSelectAction()
|
|
||||||
{
|
|
||||||
$this->view->assign('tags', (new Query(Tag::class))->execute());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function extendedTextfieldAction()
|
|
||||||
{
|
|
||||||
$this->view->assign('now', new \DateTimeImmutable('2024-06-02T15:03:00Z'));
|
|
||||||
$this->view->assign('post', (new Query(Post::class))->execute()->getFirst());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\Tests\Functional\Fixtures\Domain;
|
|
||||||
|
|
||||||
use Neos\Flow\Annotations as Flow;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Flow\Entity
|
|
||||||
*/
|
|
||||||
class Post
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
protected ?string $title = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \DateTimeImmutable|null
|
|
||||||
*/
|
|
||||||
protected ?\DateTimeImmutable $publishedAt = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|null $title
|
|
||||||
* @param \DateTimeImmutable|null $publishedAt
|
|
||||||
*/
|
|
||||||
public function __construct(?string $title, ?\DateTimeImmutable $publishedAt)
|
|
||||||
{
|
|
||||||
$this->title = $title;
|
|
||||||
$this->publishedAt = $publishedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle(): ?string
|
|
||||||
{
|
|
||||||
return $this->title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPublishedAt(): ?\DateTimeImmutable
|
|
||||||
{
|
|
||||||
return $this->publishedAt;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace DigiComp\FluidRenderFunctions\Tests\Functional;
|
|
||||||
|
|
||||||
use DigiComp\FluidRenderFunctions\Tests\Functional\Fixtures\Domain\Post;
|
|
||||||
use Neos\Flow\Mvc\Routing\Route;
|
|
||||||
use Neos\Flow\Tests\FunctionalTestCase;
|
|
||||||
use Neos\FluidAdaptor\Tests\Functional\Form\Fixtures\Domain\Model\Tag;
|
|
||||||
use Symfony\Component\DomCrawler\Crawler;
|
|
||||||
|
|
||||||
class RenderFunctionsTest extends FunctionalTestCase
|
|
||||||
{
|
|
||||||
protected static $testablePersistenceEnabled = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializer
|
|
||||||
*/
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$route = new Route();
|
|
||||||
$route->setUriPattern('test/fluidrenderfunctions/test(/{@action})');
|
|
||||||
$route->setDefaults([
|
|
||||||
'@package' => 'DigiComp.FluidRenderFunctions',
|
|
||||||
'@subpackage' => 'Tests\Functional\Fixtures',
|
|
||||||
'@controller' => 'Test',
|
|
||||||
'@action' => 'index',
|
|
||||||
]);
|
|
||||||
$route->setAppendExceedingArguments(true);
|
|
||||||
$this->router->addRoute($route);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function itAllowsFluidToRenderWrappedArray(): void
|
|
||||||
{
|
|
||||||
$response = $this->browser->request('http://localhost/test/fluidrenderfunctions/test');
|
|
||||||
static::assertEquals("hallo is cool\n", (string)$response->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function itAllowsToRenderTheOptionsArgumentOfSelectNicely(): void
|
|
||||||
{
|
|
||||||
$post1 = new Tag('hallo');
|
|
||||||
$this->persistenceManager->add($post1);
|
|
||||||
$post2 = new Tag('hallo 2');
|
|
||||||
$this->persistenceManager->add($post2);
|
|
||||||
$this->persistenceManager->persistAll();
|
|
||||||
|
|
||||||
$this->browser->request('http://localhost/test/fluidrenderfunctions/test/select');
|
|
||||||
$options = $this->browser->getCrawler()
|
|
||||||
->filterXPath('//select[1]/option')
|
|
||||||
->each(fn (Crawler $node) => $node->text());
|
|
||||||
static::assertEquals(['hallo is cool', 'hallo 2 is cool'], $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function itAllowsRenderFunctionOnStandardSelect(): void
|
|
||||||
{
|
|
||||||
$post1 = new Tag('hallo');
|
|
||||||
$this->persistenceManager->add($post1);
|
|
||||||
$post2 = new Tag('hallo 2');
|
|
||||||
$this->persistenceManager->add($post2);
|
|
||||||
$this->persistenceManager->persistAll();
|
|
||||||
|
|
||||||
$this->browser->request('http://localhost/test/fluidrenderfunctions/test/extendedselect');
|
|
||||||
$options = $this->browser->getCrawler()
|
|
||||||
->filterXPath('//select[1]/option')
|
|
||||||
->each(fn (Crawler $node) => $node->text());
|
|
||||||
static::assertEquals(['hallo is cool', 'hallo 2 is cool'], $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function itAllowsRenderFunctionOnStandardTextField(): void
|
|
||||||
{
|
|
||||||
$testDate = new \DateTimeImmutable('2024-06-02T15:03:00Z');
|
|
||||||
|
|
||||||
$post1 = new Post('hallo', $testDate);
|
|
||||||
$this->persistenceManager->add($post1);
|
|
||||||
$this->persistenceManager->persistAll();
|
|
||||||
|
|
||||||
$this->browser->request('http://localhost/test/fluidrenderfunctions/test/extendedtextfield');
|
|
||||||
$input1 = $this->browser->getCrawler()->filterXPath('//input[@type="text"][1]/@value')->text('input not found');
|
|
||||||
static::assertEquals('02.06.2024', $input1);
|
|
||||||
$input2 = $this->browser->getCrawler()->filterXPath('//input[@type="text"][2]/@value')->text('input not found');
|
|
||||||
static::assertEquals('02.06.2024', $input2);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
{
|
|
||||||
"name": "digicomp/fluid-render-functions",
|
|
||||||
"description": "define fluid based render functions and use them else where",
|
|
||||||
"type": "neos-package",
|
|
||||||
"require": {
|
|
||||||
"neos/flow": "^6.3.5 | ^7.3 | ^8.3",
|
|
||||||
"neos/fluid-adaptor": "^6.3.5 | ^7.3 | ^8.3",
|
|
||||||
"php": ">=7.4"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpunit/phpunit": "~8.5"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"DigiComp\\FluidRenderFunctions\\": "Classes/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"psr-4": {
|
|
||||||
"DigiComp\\FluidRenderFunctions\\Tests\\": "Tests/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"neos": {
|
|
||||||
"package-key": "DigiComp.FluidRenderFunctions"
|
|
||||||
},
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-develop": "1.0.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Ferdinand Kuhl",
|
|
||||||
"email": "f.kuhl@digital-competence.de",
|
|
||||||
"homepage": "https://www.digital-competence.de",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"homepage": "https://git.digital-competence.de/Packages/DigiComp.FluidRenderFunctions",
|
|
||||||
"keywords": [
|
|
||||||
"Neos",
|
|
||||||
"Flow",
|
|
||||||
"fluid"
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in a new issue