Skip to content

Commit

Permalink
FEATURE: Introduce routingArguments and queryParameters to Fusion…
Browse files Browse the repository at this point in the history
… link prototypes to replace `arguments` and `additionalParams`

The Fusion prototype `Neos.Fusion:ActionUri` has two additional properties:

- :routingArguments: (array) That are handled by the router
- :queryParameters: (array) Query parameters that are appended after routing

Those will eventually replace the properties `arguments` and `additionalParams` which are deprecated and will be removed with Neos 9.

The Fusion prototypes `Neos.Fusion:NodeUri` and `Neos.Fusion:NodeLink` have one additional property:

- :queryParameters: (array) Query parameters that are appended after routing

This will eventually replace the property `additionalParams` which is deprecated and will be removed with Neos 9.

Also this pr deprecates the fusion properties `addQueryString` and `argumentsToBeExcludedFromQueryString` from the `Neos.Fusion:ActionUri`, `Neos.Neos:NodeUri`, and `Neos.Neos:NodeLink`.
  • Loading branch information
mficzel committed Oct 9, 2022
1 parent a65cfc4 commit 711a525
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 19 deletions.
54 changes: 48 additions & 6 deletions Neos.Fusion/Classes/FusionObjects/ActionUriImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* source code.
*/

use GuzzleHttp\Psr7\Uri;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Routing\UriBuilder;

Expand Down Expand Up @@ -85,12 +86,24 @@ public function getAction(): ?string
return $this->fusionValue('action');
}

/**
* Controller arguments that are to be handled by the router
*
* @return array
*/
public function getRoutingArguments(): array
{
$arguments = $this->fusionValue('routingArguments');
return is_array($arguments) ? $arguments: [];
}

/**
* Controller arguments
*
* @return array|null
* @return array
* @deprecated to be removed with Neos 9
*/
public function getArguments(): ?array
public function getArguments(): array
{
$arguments = $this->fusionValue('arguments');
return is_array($arguments) ? $arguments: [];
Expand Down Expand Up @@ -120,16 +133,28 @@ public function getSection(): ?string
* Additional query parameters that won't be prefixed like $arguments (overrule $arguments)
*
* @return array|null
* @deprecated to be removed with Neos 9
*/
public function getAdditionalParams(): ?array
{
return $this->fusionValue('additionalParams');
}

/**
* Query parameters that are appended to the url
*
* @return array|null
*/
public function getQueryParameters(): ?array
{
return $this->fusionValue('queryParameters');
}

/**
* Arguments to be removed from the URI. Only active if addQueryString = true
*
* @return array|null
* @deprecated to be removed with Neos 9
*/
public function getArgumentsToBeExcludedFromQueryString(): ?array
{
Expand All @@ -140,6 +165,7 @@ public function getArgumentsToBeExcludedFromQueryString(): ?array
* If true, the current query parameters will be kept in the URI
*
* @return boolean
* @deprecated to be removed with Neos 9
*/
public function isAddQueryString(): bool
{
Expand All @@ -157,12 +183,21 @@ public function isAbsolute(): bool
}

/**
* @return string
* @return UriBuilder
*/
public function evaluate()
public function createUriBuilder(): UriBuilder
{
$uriBuilder = new UriBuilder();
$uriBuilder->setRequest($this->getRequest());
return $uriBuilder;
}

/**
* @return string
*/
public function evaluate()
{
$uriBuilder = $this->createUriBuilder();

$format = $this->getFormat();
if ($format !== null) {
Expand Down Expand Up @@ -195,13 +230,20 @@ public function evaluate()
}

try {
return $uriBuilder->uriFor(
$uriString = $uriBuilder->uriFor(
$this->getAction(),
$this->getArguments(),
[... $this->getArguments(), ...$this->getRoutingArguments()],
$this->getController(),
$this->getPackage(),
$this->getSubpackage()
);
$queryParameters = $this->getQueryParameters();
if (empty($queryParameters)) {
return $uriString;
}
$uri = new Uri($uriString);
parse_str($uri->getQuery(), $queryParametersFromRouting);
return (string)$uri->withQuery(http_build_query([...$queryParametersFromRouting, ...$queryParameters]));
} catch (\Exception $exception) {
return $this->runtime->handleRenderingException($this->path, $exception);
}
Expand Down
2 changes: 2 additions & 0 deletions Neos.Fusion/Resources/Private/Fusion/Root.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ prototype(Neos.Fusion:ResourceUri) {
prototype(Neos.Fusion:ActionUri) {
@class = 'Neos\\Fusion\\FusionObjects\\ActionUriImplementation'
request = ${request}
routingArguments = Neos.Fusion:DataStructure
queryParameters = Neos.Fusion:DataStructure
additionalParams = Neos.Fusion:DataStructure
arguments = Neos.Fusion:DataStructure
argumentsToBeExcludedFromQueryString = Neos.Fusion:DataStructure
Expand Down
227 changes: 227 additions & 0 deletions Neos.Fusion/Tests/Unit/FusionObjects/ActionUriImplementationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<?php
namespace Neos\Fusion\Tests\Unit\FusionObjects;

/*
* This file is part of the Neos.Fusion package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\Flow\Mvc\Routing\UriBuilder;
use Neos\Flow\Tests\UnitTestCase;
use Neos\Fusion\FusionObjects\ActionUriImplementation;

/**
* Testcase for the Fusion ActionUri object
*/
class ActionUriImplementationTest extends UnitTestCase
{
/**
* @var ActionUriImplementation
*/
protected $mockActionUriImplementation;

/**
* @var UriBuilder
*/
protected $mockUriBuilder;

public function setUp(): void
{
$this->mockUriBuilder = $this->getMockBuilder(UriBuilder::class)->disableOriginalConstructor()->getMock();

$methodsToMock = [
'getRequest',
'getPackage',
'getSubpackage',
'getController',
'getAction',
'getRoutingArguments',
'getArguments',
'getFormat',
'getSection',
'getAdditionalParams',
'getQueryParameters',
'isAbsolute',
'getArgumentsToBeExcludedFromQueryString',
'isAddQueryString',
'createUriBuilder'
];

$this->mockActionUriImplementation = $this->getMockBuilder(ActionUriImplementation::class)->disableOriginalConstructor()->onlyMethods($methodsToMock)->getMock();
$this->mockActionUriImplementation->expects($this->once())->method('createUriBuilder')->willReturn($this->mockUriBuilder);

}

/**
* @return void
* @test
*/
public function actionIsPassedToTheUriBuilder(){
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockUriBuilder->expects($this->once())->method('uriFor')->with('hello',[],null,null,null)->willReturn("http://example.com");
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function formatIsPassedToTheUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getFormat')->willReturn('square');
$this->mockUriBuilder->expects($this->once())->method('setFormat')->with('square');
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function additionalParamsArePassedToTheUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getAdditionalParams')->willReturn(['nudel' => 'suppe']);
$this->mockUriBuilder->expects($this->once())->method('setArguments')->with(['nudel' => 'suppe']);
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function argumentsToBeExcludedFromQueryStringArePassedToTheUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getArgumentsToBeExcludedFromQueryString')->willReturn(['nudel', 'suppe']);
$this->mockUriBuilder->expects($this->once())->method('setArgumentsToBeExcludedFromQueryString')->with(['nudel', 'suppe']);
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function absoluteIsPassedToTheUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('isAbsolute')->willReturn(true);
$this->mockUriBuilder->expects($this->once())->method('setCreateAbsoluteUri')->with(true);
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function sectionIsPassedToTheUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getSection')->willReturn('something');
$this->mockUriBuilder->expects($this->once())->method('setSection')->with('something');
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function addQueryStringIsPassedToTheUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('isAddQueryString')->willReturn(true);
$this->mockUriBuilder->expects($this->once())->method('setAddQueryString')->with(true);
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function actionPackageAndArgumentsArePassedToUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getArguments')->willReturn(['test' => 123]);
$this->mockActionUriImplementation->expects($this->once())->method('getRoutingArguments')->willReturn([]);
$this->mockActionUriImplementation->expects($this->once())->method('getController')->willReturn('Special');
$this->mockActionUriImplementation->expects($this->once())->method('getPackage')->willReturn('Vendor.Package');
$this->mockActionUriImplementation->expects($this->once())->method('getSubpackage')->willReturn('Stuff');

$this->mockUriBuilder->expects($this->once())->method('uriFor')->with('hello', ['test' => 123], 'Special', 'Vendor.Package', 'Stuff')->willReturn("http://example.com");
$this->mockActionUriImplementation->evaluate();
}

/**
* @return void
* @test
*/
public function actionPackageAndRoutingArgumentsArePassedToUriBuilder()
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getArguments')->willReturn([]);
$this->mockActionUriImplementation->expects($this->once())->method('getRoutingArguments')->willReturn(['test' => 123]);
$this->mockActionUriImplementation->expects($this->once())->method('getController')->willReturn('Special');
$this->mockActionUriImplementation->expects($this->once())->method('getPackage')->willReturn('Vendor.Package');
$this->mockActionUriImplementation->expects($this->once())->method('getSubpackage')->willReturn('Stuff');

$this->mockUriBuilder->expects($this->once())->method('uriFor')->with('hello', ['test' => 123], 'Special', 'Vendor.Package', 'Stuff')->willReturn("http://example.com");
$this->mockActionUriImplementation->evaluate();
}



public function routingArgumentAndArgumentMergingDataProvider(): array
{
return [
[[],[],[]],
[['foo' => 'bar'],[],['foo' => 'bar']],
[[],['foo' => 'bar'],['foo' => 'bar']],
[['foo' => 'bar'],['foo' => 'bam'],['foo' => 'bam']],
];
}

/**
* @return void
* @dataProvider routingArgumentAndArgumentMergingDataProvider
* @test
*/
public function actionUriMergesArgumentsAndRoutingArguments($arguments, $routingArguments, $expectedMergedArgumentsToBePassedToUriBuilder): void
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getArguments')->willReturn($arguments);
$this->mockActionUriImplementation->expects($this->once())->method('getRoutingArguments')->willReturn($routingArguments) ;
$this->mockUriBuilder->expects($this->once())->method('uriFor')->with('hello',$expectedMergedArgumentsToBePassedToUriBuilder,null,null,null)->willReturn("http://hostname");
$this->mockActionUriImplementation->evaluate();
}

public function queryParameterAppendingDataProvider(): array
{
return [
['https://example.com', ['foo' => 'bar'], 'https://example.com?foo=bar'],
['https://example.com?foo=bar', ['bar' => 'baz'], 'https://example.com?foo=bar&bar=baz'],
['https://example.com?foo=bar', ['foo' => 'bam'], 'https://example.com?foo=bam'],
['https://example.com', ['foo' => ['bar' => 'baz']], 'https://example.com?foo%5Bbar%5D=baz'],
['https://example.com?foo=bar', ['foo' => ['bar' => 'baz']], 'https://example.com?foo%5Bbar%5D=baz'],
['https://example.com?foo[bar]=baz', ['foo' => ['blah' => 'blubb']], 'https://example.com?foo%5Bblah%5D=blubb']
];
}

/**
* @return void
* @dataProvider queryParameterAppendingDataProvider
* @test
*/
public function actionUriAppendsQueryParametersToUri($uriFromLinking, $queryParameters, $expectedFinalUri): void
{
$this->mockActionUriImplementation->expects($this->once())->method('getAction')->willReturn('hello');
$this->mockActionUriImplementation->expects($this->once())->method('getQueryParameters')->willReturn($queryParameters);
$this->mockUriBuilder->expects($this->once())->method('uriFor')->with('hello',[],null,null,null)->willReturn($uriFromLinking);
$actualResult = $this->mockActionUriImplementation->evaluate();
$this->assertEquals($expectedFinalUri, $actualResult);
}
}

0 comments on commit 711a525

Please sign in to comment.