diff --git a/Classes/FormExtensions/SelectAspect.php b/Classes/FormExtensions/SelectAspect.php
new file mode 100644
index 0000000..43b87e6
--- /dev/null
+++ b/Classes/FormExtensions/SelectAspect.php
@@ -0,0 +1,69 @@
+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);
+ }
+ }
+ );
+ }
+}
diff --git a/Classes/FormExtensions/TextfieldAspect.php b/Classes/FormExtensions/TextfieldAspect.php
new file mode 100644
index 0000000..a116e64
--- /dev/null
+++ b/Classes/FormExtensions/TextfieldAspect.php
@@ -0,0 +1,59 @@
+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;
+ }
+}
diff --git a/Classes/Utils/RenderableProxy.php b/Classes/Utils/RenderableProxy.php
index 2ab78de..1540dcc 100644
--- a/Classes/Utils/RenderableProxy.php
+++ b/Classes/Utils/RenderableProxy.php
@@ -17,7 +17,7 @@ class RenderableProxy
{
protected InvokeRenderFunctionInterface $renderFunction;
protected $object;
- public string $Persistence_Object_Identifier = '';
+ public ?string $Persistence_Object_Identifier = null;
public function __construct(InvokeRenderFunctionInterface $renderFunction, $object)
{
diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml
new file mode 100644
index 0000000..763eab0
--- /dev/null
+++ b/Configuration/Settings.yaml
@@ -0,0 +1,5 @@
+DigiComp:
+ FluidRenderFunctions:
+ enableAspects:
+ select: true
+ textfield: true
diff --git a/README.md b/README.md
index f5f553a..5cdbaf6 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# 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:
@@ -15,3 +17,9 @@ FluidRenderFunctions to the rescue:
```
+
+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`.
diff --git a/Resources/Private/Templates/Tests/Functional/Fixtures/Test/ExtendedSelect.html b/Resources/Private/Templates/Tests/Functional/Fixtures/Test/ExtendedSelect.html
new file mode 100644
index 0000000..185c717
--- /dev/null
+++ b/Resources/Private/Templates/Tests/Functional/Fixtures/Test/ExtendedSelect.html
@@ -0,0 +1,6 @@
+{namespace rf=DigiComp\FluidRenderFunctions\ViewHelpers}
+
+
+ {subject.name} is cool
+
+
diff --git a/Resources/Private/Templates/Tests/Functional/Fixtures/Test/ExtendedTextfield.html b/Resources/Private/Templates/Tests/Functional/Fixtures/Test/ExtendedTextfield.html
new file mode 100644
index 0000000..548c86e
--- /dev/null
+++ b/Resources/Private/Templates/Tests/Functional/Fixtures/Test/ExtendedTextfield.html
@@ -0,0 +1,11 @@
+{namespace rf=DigiComp\FluidRenderFunctions\ViewHelpers}
+
+
+ {subject -> f:format.date(format: 'd.m.Y')}
+
+
+
+
+
+
+
diff --git a/Tests/Functional/Fixtures/Controller/TestController.php b/Tests/Functional/Fixtures/Controller/TestController.php
index 64f75b0..16238ae 100644
--- a/Tests/Functional/Fixtures/Controller/TestController.php
+++ b/Tests/Functional/Fixtures/Controller/TestController.php
@@ -4,6 +4,7 @@ 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;
@@ -19,4 +20,15 @@ class TestController extends ActionController
{
$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());
+ }
}
diff --git a/Tests/Functional/Fixtures/Domain/Post.php b/Tests/Functional/Fixtures/Domain/Post.php
new file mode 100644
index 0000000..6d18f51
--- /dev/null
+++ b/Tests/Functional/Fixtures/Domain/Post.php
@@ -0,0 +1,43 @@
+title = $title;
+ $this->publishedAt = $publishedAt;
+ }
+
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
+ public function getPublishedAt(): ?\DateTimeImmutable
+ {
+ return $this->publishedAt;
+ }
+}
diff --git a/Tests/Functional/RenderFunctionsTest.php b/Tests/Functional/RenderFunctionsTest.php
index 6452834..65f633c 100644
--- a/Tests/Functional/RenderFunctionsTest.php
+++ b/Tests/Functional/RenderFunctionsTest.php
@@ -4,9 +4,11 @@ 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
{
@@ -51,8 +53,46 @@ class RenderFunctionsTest extends FunctionalTestCase
$this->persistenceManager->add($post2);
$this->persistenceManager->persistAll();
- $response = $this->browser->request('http://localhost/test/fluidrenderfunctions/test/select');
- static::assertStringContainsString('hallo is cool', (string)$response->getBody());
- static::assertStringContainsString('hallo 2 is cool', (string)$response->getBody());
+ $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);
}
}