|
13 | 13 |
|
14 | 14 | use Psr\Container\ContainerInterface;
|
15 | 15 | use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
| 16 | +use Symfony\Component\HttpFoundation\Exception\JsonException; |
16 | 17 | use Symfony\Component\HttpFoundation\Request;
|
17 | 18 | use Symfony\Component\HttpFoundation\Response;
|
18 | 19 | use Symfony\Component\HttpKernel\Event\ControllerEvent;
|
|
28 | 29 | use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
29 | 30 | use Symfony\Contracts\Service\ServiceSubscriberInterface;
|
30 | 31 | use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
|
| 32 | +use Symfony\UX\LiveComponent\Attribute\LiveArg; |
31 | 33 | use Symfony\UX\LiveComponent\LiveComponentHydrator;
|
32 | 34 | use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
|
33 | 35 | use Symfony\UX\LiveComponent\Util\LiveControllerAttributesCreator;
|
34 |
| -use Symfony\UX\LiveComponent\Util\LiveRequestDataParser; |
35 | 36 | use Symfony\UX\TwigComponent\ComponentFactory;
|
36 | 37 | use Symfony\UX\TwigComponent\ComponentMetadata;
|
37 | 38 | use Symfony\UX\TwigComponent\ComponentRenderer;
|
@@ -115,7 +116,7 @@ public function onKernelRequest(RequestEvent $event): void
|
115 | 116 |
|
116 | 117 | if ('_batch' === $action) {
|
117 | 118 | // use batch controller
|
118 |
| - $data = LiveRequestDataParser::parseDataFor($request); |
| 119 | + $data = $this->parseDataFor($request); |
119 | 120 |
|
120 | 121 | $request->attributes->set('_controller', 'ux.live_component.batch_action_controller');
|
121 | 122 | $request->attributes->set('serviceId', $metadata->getServiceId());
|
@@ -194,6 +195,61 @@ public function onKernelController(ControllerEvent $event): void
|
194 | 195 | $action,
|
195 | 196 | ]);
|
196 | 197 | }
|
| 198 | + |
| 199 | + // read the action arguments from the request, unless they're already set (batch sub-requests) |
| 200 | + $actionArguments = $request->attributes->get('_component_action_args', $this->parseDataFor($request)['args']); |
| 201 | + // extra variables to be made available to the controller |
| 202 | + // (for "actions" only) |
| 203 | + foreach (LiveArg::liveArgs($component, $action) as $parameter => $arg) { |
| 204 | + if (isset($actionArguments[$arg])) { |
| 205 | + $request->attributes->set($parameter, $actionArguments[$arg]); |
| 206 | + } |
| 207 | + } |
| 208 | + } |
| 209 | + |
| 210 | + /** |
| 211 | + * @return array{ |
| 212 | + * data: array, |
| 213 | + * args: array, |
| 214 | + * actions: array |
| 215 | + * // has "fingerprint" and "tag" string key, keyed by component id |
| 216 | + * children: array |
| 217 | + * propsFromParent: array |
| 218 | + * } |
| 219 | + */ |
| 220 | + private static function parseDataFor(Request $request): array |
| 221 | + { |
| 222 | + if (!$request->attributes->has('_live_request_data')) { |
| 223 | + if ($request->query->has('props')) { |
| 224 | + $liveRequestData = [ |
| 225 | + 'props' => self::parseJsonFromQuery($request, 'props'), |
| 226 | + 'updated' => self::parseJsonFromQuery($request, 'updated'), |
| 227 | + 'args' => [], |
| 228 | + 'actions' => [], |
| 229 | + 'children' => self::parseJsonFromQuery($request, 'children'), |
| 230 | + 'propsFromParent' => self::parseJsonFromQuery($request, 'propsFromParent'), |
| 231 | + ]; |
| 232 | + } else { |
| 233 | + try { |
| 234 | + $requestData = json_decode($request->request->get('data'), true, 512, \JSON_BIGINT_AS_STRING | \JSON_THROW_ON_ERROR); |
| 235 | + } catch (\JsonException $e) { |
| 236 | + throw new JsonException('Could not decode request body.', $e->getCode(), $e); |
| 237 | + } |
| 238 | + |
| 239 | + $liveRequestData = [ |
| 240 | + 'props' => $requestData['props'] ?? [], |
| 241 | + 'updated' => $requestData['updated'] ?? [], |
| 242 | + 'args' => $requestData['args'] ?? [], |
| 243 | + 'actions' => $requestData['actions'] ?? [], |
| 244 | + 'children' => $requestData['children'] ?? [], |
| 245 | + 'propsFromParent' => $requestData['propsFromParent'] ?? [], |
| 246 | + ]; |
| 247 | + } |
| 248 | + |
| 249 | + $request->attributes->set('_live_request_data', $liveRequestData); |
| 250 | + } |
| 251 | + |
| 252 | + return $request->attributes->get('_live_request_data'); |
197 | 253 | }
|
198 | 254 |
|
199 | 255 | public function onKernelView(ViewEvent $event): void
|
@@ -298,22 +354,34 @@ private function hydrateComponent(object $component, string $componentName, Requ
|
298 | 354 | $metadataFactory = $this->container->get(LiveComponentMetadataFactory::class);
|
299 | 355 | \assert($metadataFactory instanceof LiveComponentMetadataFactory);
|
300 | 356 |
|
301 |
| - $liveRequestData = LiveRequestDataParser::parseDataFor($request); |
302 | 357 | $componentAttributes = $hydrator->hydrate(
|
303 | 358 | $component,
|
304 |
| - $liveRequestData['props'], |
305 |
| - $liveRequestData['updated'], |
| 359 | + $this->parseDataFor($request)['props'], |
| 360 | + $this->parseDataFor($request)['updated'], |
306 | 361 | $metadataFactory->getMetadata($componentName),
|
307 |
| - $liveRequestData['propsFromParent'] |
| 362 | + $this->parseDataFor($request)['propsFromParent'] |
308 | 363 | );
|
309 | 364 |
|
310 | 365 | $mountedComponent = new MountedComponent($componentName, $component, $componentAttributes);
|
311 | 366 |
|
312 | 367 | $mountedComponent->addExtraMetadata(
|
313 | 368 | InterceptChildComponentRenderSubscriber::CHILDREN_FINGERPRINTS_METADATA_KEY,
|
314 |
| - $liveRequestData['children'] |
| 369 | + $this->parseDataFor($request)['children'] |
315 | 370 | );
|
316 | 371 |
|
317 | 372 | return $mountedComponent;
|
318 | 373 | }
|
| 374 | + |
| 375 | + private static function parseJsonFromQuery(Request $request, string $key): array |
| 376 | + { |
| 377 | + if (!$request->query->has($key)) { |
| 378 | + return []; |
| 379 | + } |
| 380 | + |
| 381 | + try { |
| 382 | + return json_decode($request->query->get($key), true, 512, \JSON_THROW_ON_ERROR); |
| 383 | + } catch (\JsonException $exception) { |
| 384 | + throw new JsonException(sprintf('Invalid JSON on query string "%s".', $key), 0, $exception); |
| 385 | + } |
| 386 | + } |
319 | 387 | }
|
0 commit comments