14
14
use Symfony \Component \EventDispatcher \EventSubscriberInterface ;
15
15
use Symfony \Component \HttpKernel \Event \ResponseEvent ;
16
16
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 ;
17
22
use Symfony \UX \LiveComponent \LiveComponentHydrator ;
18
23
use Symfony \UX \LiveComponent \Metadata \LiveComponentMetadataFactory ;
19
- use Symfony \UX \LiveComponent \Util \UrlFactory ;
20
24
use Symfony \UX \TwigComponent \MountedComponent ;
21
25
22
26
/**
@@ -29,35 +33,28 @@ class LiveUrlSubscriber implements EventSubscriberInterface
29
33
public function __construct (
30
34
private LiveComponentMetadataFactory $ metadataFactory ,
31
35
private LiveComponentHydrator $ liveComponentHydrator ,
32
- private UrlFactory $ urlFactory ,
36
+ private RouterInterface $ router ,
33
37
) {
34
38
}
35
39
36
40
public function onKernelResponse (ResponseEvent $ event ): void
37
41
{
38
- if (!$ event ->isMainRequest ()) {
39
- return ;
40
- }
41
-
42
42
$ 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
+ ) {
44
49
return ;
45
50
}
46
51
47
- if (!$ request ->attributes ->has ('_mounted_component ' )) {
48
- return ;
49
- }
52
+ /** @var MountedComponent $mounted */
53
+ $ mounted = $ request ->attributes ->get ('_mounted_component ' );
50
54
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 );
57
56
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 ));
61
58
}
62
59
63
60
public static function getSubscribedEvents (): array
@@ -68,34 +65,73 @@ public static function getSubscribedEvents(): array
68
65
}
69
66
70
67
/**
71
- * @return array{
72
- * path: array<string, mixed>,
73
- * query: array<string, mixed>
74
- * }
68
+ * @return array{ array<string, mixed>, array<string, mixed> }
75
69
*/
76
- private function getLiveProps (MountedComponent $ mounted ): array
70
+ private function extractUrlLiveProps (MountedComponent $ mounted ): array
77
71
{
78
- $ metadata = $ this -> metadataFactory -> getMetadata ( $ mounted -> getName ()) ;
72
+ $ pathProps = $ queryProps = [] ;
79
73
80
- $ dehydratedProps = $ this ->liveComponentHydrator ->dehydrate (
81
- $ mounted ->getComponent (),
82
- $ mounted ->getAttributes (),
83
- $ metadata
84
- );
74
+ $ mountedMetadata = $ this ->metadataFactory ->getMetadata ($ mounted ->getName ());
85
75
86
- $ values = $ dehydratedProps ->getProps ();
76
+ $ dehydratedProps = $ this ->liveComponentHydrator ->dehydrate ($ mounted ->getComponent (), $ mounted ->getAttributes (), $ mountedMetadata );
77
+ $ props = $ dehydratedProps ->getProps ();
87
78
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
+ }
92
91
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 );
96
122
}
97
123
}
98
124
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 : '' );
100
136
}
101
137
}
0 commit comments