Skip to content

Commit ee72505

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

File tree

8 files changed

+125
-384
lines changed

8 files changed

+125
-384
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: 75 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
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\MethodNotAllowedException;
18+
use Symfony\Component\Routing\Exception\NoConfigurationException;
19+
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
20+
use Symfony\Component\Routing\Exception\RouteNotFoundException;
21+
use Symfony\Component\Routing\RouterInterface;
1722
use Symfony\UX\LiveComponent\LiveComponentHydrator;
1823
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
19-
use Symfony\UX\LiveComponent\Util\UrlFactory;
2024
use Symfony\UX\TwigComponent\MountedComponent;
2125

2226
/**
@@ -29,35 +33,28 @@ class LiveUrlSubscriber implements EventSubscriberInterface
2933
public function __construct(
3034
private LiveComponentMetadataFactory $metadataFactory,
3135
private LiveComponentHydrator $liveComponentHydrator,
32-
private UrlFactory $urlFactory,
36+
private RouterInterface $router,
3337
) {
3438
}
3539

3640
public function onKernelResponse(ResponseEvent $event): void
3741
{
38-
if (!$event->isMainRequest()) {
39-
return;
40-
}
41-
4242
$request = $event->getRequest();
43-
if (!$request->attributes->has('_live_component')) {
43+
if (!$event->isMainRequest()
44+
|| !$event->getResponse()->isSuccessful()
45+
|| !$request->attributes->has('_live_component')
46+
|| !$request->attributes->has('_mounted_component')
47+
|| !($previousLiveUrl = $request->headers->get(self::URL_HEADER))
48+
) {
4449
return;
4550
}
4651

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

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-
}
55+
[$pathProps, $queryProps] = $this->extractUrlLiveProps($mounted);
5756

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

6360
public static function getSubscribedEvents(): array
@@ -68,34 +65,73 @@ public static function getSubscribedEvents(): array
6865
}
6966

7067
/**
71-
* @return array{
72-
* path: array<string, mixed>,
73-
* query: array<string, mixed>
74-
* }
68+
* @return array{ array<string, mixed>, array<string, mixed> }
7569
*/
76-
private function getLiveProps(MountedComponent $mounted): array
70+
private function extractUrlLiveProps(MountedComponent $mounted): array
7771
{
78-
$metadata = $this->metadataFactory->getMetadata($mounted->getName());
72+
$pathProps = $queryProps = [];
7973

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

86-
$values = $dehydratedProps->getProps();
76+
$dehydratedProps = $this->liveComponentHydrator->dehydrate($mounted->getComponent(), $mounted->getAttributes(), $mountedMetadata);
77+
$props = $dehydratedProps->getProps();
8778

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

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];
92+
private function generateNewLiveUrl(string $previousUrl, array $pathProps, array $queryProps): string
93+
{
94+
$previousUrlParsed = parse_url($previousUrl);
95+
$newUrl = $previousUrlParsed['path'];
96+
$newQueryString = $previousUrlParsed['query'] ?? '';
97+
98+
if ([] !== $pathProps) {
99+
$context = $this->router->getContext();
100+
try {
101+
// Create a temporary RouterContext pointing to the current controller
102+
$tmpContext = clone $context;
103+
$tmpContext->setMethod('GET');
104+
$this->router->setContext($tmpContext);
105+
106+
$matched = $this->router->match($previousUrlParsed['path']);
107+
108+
$route = $matched['_route'];
109+
unset($matched['_route']);
110+
111+
$parameters = $matched;
112+
foreach ($pathProps as $name => $value) {
113+
if ($value) {
114+
$parameters[$name] = $value;
115+
}
116+
}
117+
$newUrl = $this->router->generate($route, $parameters);
118+
} catch (ResourceNotFoundException) {
119+
// re-use the previous URL path
120+
} finally {
121+
$this->router->setContext($context);
96122
}
97123
}
98124

99-
return $urlLiveProps;
125+
if ([] !== $queryProps) {
126+
$previousQueryString = [];
127+
128+
if (isset($previousUrlParsed['query'])) {
129+
parse_str($previousUrlParsed['query'], $previousQueryString);
130+
}
131+
132+
$newQueryString = http_build_query([...$previousQueryString, ...$queryProps]);
133+
}
134+
135+
return $newUrl . ($newQueryString ? '?' . $newQueryString : '');
100136
}
101137
}

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ 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']);
220+
$routes->add('route_with_prop', '/route_with_prop/{pathProp}')->methods(['GET'])->requirements(['pathProp' => '\w+']);
221221
$routes->add('route_with_alias_prop', '/route_with_alias_prop/{pathAlias}');
222222
}
223223
}

0 commit comments

Comments
 (0)