Skip to content

Commit c9ce9a7

Browse files
committed
[LiveComponent] Refactor and fix (again) the new URL (with path and query LiveProps) generation
1 parent c6ad4f7 commit c9ce9a7

File tree

8 files changed

+156
-389
lines changed

8 files changed

+156
-389
lines changed

src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
use Symfony\UX\LiveComponent\Util\LiveControllerAttributesCreator;
5454
use Symfony\UX\LiveComponent\Util\RequestPropsExtractor;
5555
use Symfony\UX\LiveComponent\Util\TwigAttributeHelperFactory;
56-
use Symfony\UX\LiveComponent\Util\UrlFactory;
5756
use Symfony\UX\TwigComponent\ComponentFactory;
5857
use Symfony\UX\TwigComponent\ComponentRenderer;
5958

@@ -142,7 +141,7 @@ function (ChildDefinition $definition, AsLiveComponent $attribute) {
142141
->setArguments([
143142
new Reference('ux.live_component.metadata_factory'),
144143
new Reference('ux.live_component.component_hydrator'),
145-
new Reference('ux.live_component.url_factory'),
144+
new Reference('router'),
146145
])
147146
->addTag('kernel.event_subscriber')
148147
;
@@ -213,9 +212,6 @@ function (ChildDefinition $definition, AsLiveComponent $attribute) {
213212

214213
$container->register('ux.live_component.attribute_helper_factory', TwigAttributeHelperFactory::class);
215214

216-
$container->register('ux.live_component.url_factory', UrlFactory::class)
217-
->setArguments([new Reference('router')]);
218-
219215
$container->register('ux.live_component.live_controller_attributes_creator', LiveControllerAttributesCreator::class)
220216
->setArguments([
221217
new Reference('ux.live_component.metadata_factory'),

src/LiveComponent/src/EventListener/LiveUrlSubscriber.php

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1515
use Symfony\Component\HttpKernel\Event\ResponseEvent;
1616
use Symfony\Component\HttpKernel\KernelEvents;
17+
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
18+
use Symfony\Component\Routing\RouterInterface;
1719
use Symfony\UX\LiveComponent\LiveComponentHydrator;
1820
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
19-
use Symfony\UX\LiveComponent\Util\UrlFactory;
2021
use Symfony\UX\TwigComponent\MountedComponent;
2122

2223
/**
@@ -29,35 +30,28 @@ class LiveUrlSubscriber implements EventSubscriberInterface
2930
public function __construct(
3031
private LiveComponentMetadataFactory $metadataFactory,
3132
private LiveComponentHydrator $liveComponentHydrator,
32-
private UrlFactory $urlFactory,
33+
private RouterInterface $router,
3334
) {
3435
}
3536

3637
public function onKernelResponse(ResponseEvent $event): void
3738
{
38-
if (!$event->isMainRequest()) {
39-
return;
40-
}
41-
4239
$request = $event->getRequest();
43-
if (!$request->attributes->has('_live_component')) {
40+
if (!$event->isMainRequest()
41+
|| !$event->getResponse()->isSuccessful()
42+
|| !$request->attributes->has('_live_component')
43+
|| !$request->attributes->has('_mounted_component')
44+
|| !($previousLiveUrl = $request->headers->get(self::URL_HEADER))
45+
) {
4446
return;
4547
}
4648

47-
if (!$request->attributes->has('_mounted_component')) {
48-
return;
49-
}
49+
/** @var MountedComponent $mounted */
50+
$mounted = $request->attributes->get('_mounted_component');
5051

51-
$newLiveUrl = null;
52-
if ($previousLiveUrl = $request->headers->get(self::URL_HEADER)) {
53-
$mounted = $request->attributes->get('_mounted_component');
54-
$liveProps = $this->getLiveProps($mounted);
55-
$newLiveUrl = $this->urlFactory->createFromPreviousAndProps($previousLiveUrl, $liveProps['path'], $liveProps['query']);
56-
}
52+
[$pathProps, $queryProps] = $this->extractUrlLiveProps($mounted);
5753

58-
if ($newLiveUrl) {
59-
$event->getResponse()->headers->set(self::URL_HEADER, $newLiveUrl);
60-
}
54+
$event->getResponse()->headers->set(self::URL_HEADER, $this->generateNewLiveUrl($previousLiveUrl, $pathProps, $queryProps));
6155
}
6256

6357
public static function getSubscribedEvents(): array
@@ -68,34 +62,67 @@ public static function getSubscribedEvents(): array
6862
}
6963

7064
/**
71-
* @return array{
72-
* path: array<string, mixed>,
73-
* query: array<string, mixed>
74-
* }
65+
* @return array{ array<string, mixed>, array<string, mixed> }
7566
*/
76-
private function getLiveProps(MountedComponent $mounted): array
67+
private function extractUrlLiveProps(MountedComponent $mounted): array
7768
{
78-
$metadata = $this->metadataFactory->getMetadata($mounted->getName());
69+
$pathProps = $queryProps = [];
7970

80-
$dehydratedProps = $this->liveComponentHydrator->dehydrate(
81-
$mounted->getComponent(),
82-
$mounted->getAttributes(),
83-
$metadata
84-
);
71+
$mountedMetadata = $this->metadataFactory->getMetadata($mounted->getName());
8572

86-
$values = $dehydratedProps->getProps();
73+
$dehydratedProps = $this->liveComponentHydrator->dehydrate($mounted->getComponent(), $mounted->getAttributes(), $mountedMetadata);
74+
$props = $dehydratedProps->getProps();
8775

88-
$urlLiveProps = [
89-
'path' => [],
90-
'query' => [],
91-
];
76+
foreach ($mountedMetadata->getAllUrlMappings($mounted->getComponent()) as $name => $urlMapping) {
77+
if (\array_key_exists($name, $props)) {
78+
if ($urlMapping->mapPath) {
79+
$pathProps[$urlMapping->as ?? $name] = $props[$name];
80+
} else {
81+
$queryProps[$urlMapping->as ?? $name] = $props[$name];
82+
}
83+
}
84+
}
85+
86+
return [$pathProps, $queryProps];
87+
}
9288

93-
foreach ($metadata->getAllUrlMappings($mounted->getComponent()) as $name => $urlMapping) {
94-
if (isset($values[$name]) && $urlMapping) {
95-
$urlLiveProps[$urlMapping->mapPath ? 'path' : 'query'][$urlMapping->as ?? $name] = $values[$name];
89+
private function generateNewLiveUrl(string $previousUrl, array $pathProps, array $queryProps): string
90+
{
91+
$previousUrlParsed = parse_url($previousUrl);
92+
$newUrl = $previousUrlParsed['path'];
93+
$newQueryString = $previousUrlParsed['query'] ?? '';
94+
95+
if ([] !== $pathProps) {
96+
$context = $this->router->getContext();
97+
try {
98+
// Create a temporary RouterContext pointing to the current controller
99+
$tmpContext = clone $context;
100+
$tmpContext->setMethod('GET');
101+
$this->router->setContext($tmpContext);
102+
103+
$matched = $this->router->match($previousUrlParsed['path']);
104+
105+
$route = $matched['_route'];
106+
unset($matched['_route']);
107+
108+
$newUrl = $this->router->generate($route, $pathProps + $matched);
109+
} catch (ResourceNotFoundException) {
110+
// reuse the previous URL path
111+
} finally {
112+
$this->router->setContext($context);
96113
}
97114
}
98115

99-
return $urlLiveProps;
116+
if ([] !== $queryProps) {
117+
$previousQueryString = [];
118+
119+
if (isset($previousUrlParsed['query'])) {
120+
parse_str($previousUrlParsed['query'], $previousQueryString);
121+
}
122+
123+
$newQueryString = http_build_query([...$previousQueryString, ...$queryProps]);
124+
}
125+
126+
return $newUrl.($newQueryString ? '?'.$newQueryString : '');
100127
}
101128
}

src/LiveComponent/src/Metadata/LiveComponentMetadata.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public function getAllUrlMappings(object $component): iterable
7474
$urlMappings = [];
7575
foreach ($this->getAllLivePropsMetadata($component) as $livePropMetadata) {
7676
if ($livePropMetadata->urlMapping()) {
77-
$urlMappings[$livePropMetadata->getName()] = $livePropMetadata->urlMapping();
77+
yield $livePropMetadata->getName() => $livePropMetadata->urlMapping();
7878
}
7979
}
8080

src/LiveComponent/src/Util/UrlFactory.php

Lines changed: 0 additions & 108 deletions
This file was deleted.

src/LiveComponent/tests/Fixtures/Kernel.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ protected function configureRoutes(RoutingConfigurator $routes): void
217217
$routes->add('homepage', '/')->controller('kernel::index');
218218
$routes->add('alternate_live_route', '/alt/{_live_component}/{_live_action}')->defaults(['_live_action' => 'get']);
219219
$routes->add('localized_route', '/locale/{_locale}/{_live_component}/{_live_action}')->defaults(['_live_action' => 'get']);
220-
$routes->add('route_with_prop', '/route_with_prop/{pathProp}')->methods(['GET']);
221-
$routes->add('route_with_alias_prop', '/route_with_alias_prop/{pathAlias}');
220+
$routes->add('route_with_prop', '/route_with_prop/{pathProp}')->methods(['GET'])->requirements(['pathProp' => '\w+']);
221+
$routes->add('route_with_alias_prop', '/route_with_alias_prop/{pathAlias}')->requirements(['pathAlias' => '\w+']);
222+
$routes->add('route_with_two_props', '/route_with_two_props/{pathProp}/{pathAlias}')->methods(['GET'])->requirements(['pathProp' => '\w+', 'pathAlias' => '\w+']);
223+
$routes->add('route_with_two_path_params_but_one_prop', '/route_with_two_path_params_but_one_prop/{pathProp}/{id}')->methods(['GET'])->requirements(['pathProp' => '\w+', 'id' => '\d+']);
222224
}
223225
}

0 commit comments

Comments
 (0)