Skip to content

Commit 3a0f21b

Browse files
Recursive array argument handling on redirect parameters (#1054)
| Q | A | --------------- | ----- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Related tickets | | License | MIT I'd like to be able to configure those redirect arguments: ```php new Update( methods: ['PATCH', 'POST'], routeName: '_sylius_admin_promotion_archive', shortName: 'archive', formType: ArchivableType::class, notificationMessage: 'sylius.resource.update', redirectToRoute: 'sylius_admin_promotion_index', redirectArguments: [ 'criteria' => [ 'archival' => 0, ], ], ) ``` for that Sylius feature: ```gherkin @api @ui Scenario: Restoring an archival promotion Given the promotion "Christmas sale" is archived When I browse promotions And I filter archival promotions And I restore the "Christmas sale" promotion Then I should be viewing non archival promotions And I should see 2 promotions on the list ```
2 parents 9ab125b + d75efd9 commit 3a0f21b

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

src/Component/spec/Symfony/Routing/RedirectHandlerSpec.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PhpSpec\ObjectBehavior;
1717
use Prophecy\Argument;
1818
use Sylius\Bundle\GridBundle\Storage\FilterStorageInterface;
19+
use Sylius\Resource\Exception\InvalidArgumentException;
1920
use Sylius\Resource\Metadata\BulkUpdate;
2021
use Sylius\Resource\Metadata\Create;
2122
use Sylius\Resource\Metadata\Delete;
@@ -104,16 +105,32 @@ function it_redirects_to_resource_with_custom_arguments(
104105
FilterStorageInterface $filterStorage,
105106
): void {
106107
$data->code = 'xyz';
107-
$operation = new Create(redirectToRoute: 'app_dummy_index', redirectArguments: ['code' => 'resource.code']);
108+
$operation = new Create(redirectToRoute: 'app_dummy_index', redirectArguments: ['code' => 'resource.code', 'criteria' => ['foo' => 'resource.code', 'bar' => 1]]);
108109
$resource = new ResourceMetadata(alias: 'app.book');
109110
$operation = $operation->withResource($resource);
110111

111112
$filterStorage->all()->willReturn([]);
112-
$router->generate('app_dummy_index', ['code' => 'xyz'])->willReturn('/dummies')->shouldBeCalled();
113+
$router->generate('app_dummy_index', ['code' => 'xyz', 'criteria' => ['foo' => 'xyz', 'bar' => 1]])->willReturn('/dummies')->shouldBeCalled();
113114

114115
$this->redirectToResource($data, $operation, $request);
115116
}
116117

118+
function it_throws_an_exception_when_trying_redirect_with_custom_arguments_that_are_not_scalar_ones(
119+
\stdClass $data,
120+
Request $request,
121+
RouterInterface $router,
122+
FilterStorageInterface $filterStorage,
123+
): void {
124+
$data->code = 'xyz';
125+
$operation = new Create(redirectToRoute: 'app_dummy_index', redirectArguments: ['code' => 'resource.code', 'criteria' => ['foo' => 'resource.code', 'bar' => new \stdClass()]]);
126+
$resource = new ResourceMetadata(alias: 'app.book');
127+
$operation = $operation->withResource($resource);
128+
129+
$this->shouldThrow(
130+
new InvalidArgumentException('Parameter "bar" should be a scalar or an array.'),
131+
)->during('redirectToResource', [$data, $operation, $request]);
132+
}
133+
117134
function it_redirects_to_resource_with_id_via_the_getter(
118135
Request $request,
119136
RouterInterface $router,
@@ -167,6 +184,23 @@ function it_uses_filters_from_grid_storage_when_redirecting_to_an_index_operatio
167184
$this->redirectToResource($data, $operation, $request);
168185
}
169186

187+
function it_does_not_use_grid_filters_when_redirect_parameters_are_defined_on_the_operation(
188+
\stdClass $data,
189+
Request $request,
190+
RouterInterface $router,
191+
FilterStorageInterface $filterStorage,
192+
): void {
193+
$data->id = 'xyz';
194+
$operation = new Delete(redirectToRoute: 'app_dummy_index', redirectArguments: ['criteria' => ['archival' => true]]);
195+
$resource = new ResourceMetadata(alias: 'app.book');
196+
$operation = $operation->withResource($resource);
197+
198+
$filterStorage->all()->willReturn(['criteria' => ['enabled' => true]]);
199+
$router->generate('app_dummy_index', ['criteria' => ['archival' => true]])->willReturn('/dummies')->shouldBeCalled();
200+
201+
$this->redirectToResource($data, $operation, $request);
202+
}
203+
170204
function it_do_not_use_filters_from_grid_storage_when_redirecting_to_an_update_operation(
171205
\stdClass $data,
172206
Request $request,

src/Component/src/Symfony/Routing/RedirectHandler.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace Sylius\Resource\Symfony\Routing;
1515

1616
use Sylius\Bundle\GridBundle\Storage\FilterStorageInterface;
17+
use Sylius\Resource\Exception\InvalidArgumentException;
1718
use Sylius\Resource\Metadata\BulkOperationInterface;
1819
use Sylius\Resource\Metadata\DeleteOperationInterface;
1920
use Sylius\Resource\Metadata\HttpOperation;
@@ -62,8 +63,8 @@ public function redirectToOperation(mixed $data, HttpOperation $operation, Reque
6263

6364
public function redirectToRoute(mixed $data, string $route, array $parameters = []): RedirectResponse
6465
{
65-
if (\str_ends_with($route, '_index')) {
66-
$parameters = array_merge($parameters, $this->filterStorage?->all() ?? []);
66+
if (\str_ends_with($route, '_index') && [] === $parameters) {
67+
$parameters = $this->filterStorage?->all() ?? [];
6768
}
6869

6970
return new RedirectResponse($this->router->generate($route, $parameters));
@@ -97,7 +98,17 @@ private function parseResourceValues(ResourceMetadata $resource, array $paramete
9798
$accessor = PropertyAccess::createPropertyAccessor();
9899

99100
foreach ($parameters as $key => $value) {
100-
if (str_contains($value, 'resource.')) {
101+
if (\is_array($value)) {
102+
$parameters[$key] = $this->parseResourceValues($resource, $value, $data);
103+
104+
continue;
105+
}
106+
107+
if (!\is_scalar($value)) {
108+
throw new InvalidArgumentException(sprintf('Parameter "%s" should be a scalar or an array.', $key));
109+
}
110+
111+
if (\is_string($value) && str_contains($value, 'resource.')) {
101112
$propertyPath = substr($value, 9);
102113

103114
if (\is_object($data) && $accessor->isReadable($data, $propertyPath)) {
@@ -114,7 +125,8 @@ private function parseResourceValues(ResourceMetadata $resource, array $paramete
114125
$variables[$resourceName] = $data;
115126
}
116127

117-
$parameters[$key] = $this->argumentParser->parseExpression($value, $variables);
128+
// TODO, the best way to detect if we need to parse an expression will need to add "@=" syntax.
129+
$parameters[$key] = \is_string($value) ? $this->argumentParser->parseExpression($value, $variables) : $value;
118130
}
119131

120132
return $parameters;

0 commit comments

Comments
 (0)