adding augmentations for select and textfield ViewHelpers
This commit is contained in:
parent
050e6e119f
commit
098d5c09a6
10 changed files with 257 additions and 4 deletions
69
Classes/FormExtensions/SelectAspect.php
Normal file
69
Classes/FormExtensions/SelectAspect.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
59
Classes/FormExtensions/TextfieldAspect.php
Normal file
59
Classes/FormExtensions/TextfieldAspect.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
5
Configuration/Settings.yaml
Normal file
5
Configuration/Settings.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
DigiComp:
|
||||
FluidRenderFunctions:
|
||||
enableAspects:
|
||||
select: true
|
||||
textfield: true
|
|
@ -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:
|
|||
</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`.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{namespace rf=DigiComp\FluidRenderFunctions\ViewHelpers}
|
||||
|
||||
<rf:registerRenderFunction as="renderTag">
|
||||
{subject.name} is cool
|
||||
</rf:registerRenderFunction>
|
||||
<f:form.select options="{tags}" renderFunction="renderTag" />
|
|
@ -0,0 +1,11 @@
|
|||
{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>
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
43
Tests/Functional/Fixtures/Domain/Post.php
Normal file
43
Tests/Functional/Fixtures/Domain/Post.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue