2929use Symfony \Contracts \Service \ServiceSubscriberInterface ;
3030use Symfony \UX \LiveComponent \Attribute \AsLiveComponent ;
3131use Symfony \UX \LiveComponent \Attribute \LiveArg ;
32+ use Symfony \UX \LiveComponent \Controller \BatchActionController ;
3233use Symfony \UX \LiveComponent \LiveComponentHydrator ;
3334use Symfony \UX \TwigComponent \ComponentFactory ;
3435use Symfony \UX \TwigComponent \ComponentMetadata ;
@@ -69,6 +70,11 @@ public function onKernelRequest(RequestEvent $event): void
6970 return ;
7071 }
7172
73+ if (!$ event ->isMainRequest ()) {
74+ // sub request
75+ return ;
76+ }
77+
7278 // the default "action" is get, which does nothing
7379 $ action = $ request ->get ('action ' , 'get ' );
7480 $ componentName = (string ) $ request ->get ('component ' );
@@ -107,6 +113,22 @@ public function onKernelRequest(RequestEvent $event): void
107113 throw new BadRequestHttpException ('Invalid CSRF token. ' );
108114 }
109115
116+ if ('_batch ' === $ action ) {
117+ // use batch controller
118+ $ data = $ this ->parseDataFor ($ request );
119+
120+ $ request ->attributes ->set ('_controller ' , 'ux.live_component.batch_action_controller ' );
121+ $ request ->attributes ->set ('serviceId ' , $ metadata ->getServiceId ());
122+ $ request ->attributes ->set ('actions ' , $ data ['actions ' ]);
123+ $ request ->attributes ->set ('mounted ' , $ this ->container ->get (LiveComponentHydrator::class)->hydrate (
124+ $ this ->container ->get (ComponentFactory::class)->get ($ componentName ),
125+ $ data ['data ' ],
126+ $ componentName ,
127+ ));
128+
129+ return ;
130+ }
131+
110132 $ request ->attributes ->set ('_controller ' , sprintf ('%s::%s ' , $ metadata ->getServiceId (), $ action ));
111133 }
112134
@@ -118,18 +140,13 @@ public function onKernelController(ControllerEvent $event): void
118140 return ;
119141 }
120142
121- $ actionArguments = [];
122- if ($ request ->query ->has ('data ' )) {
123- // ?data=
124- $ data = json_decode ($ request ->query ->get ('data ' ), true , 512 , \JSON_THROW_ON_ERROR );
125- } else {
126- // OR body of the request is JSON
127- $ requestData = json_decode ($ request ->getContent (), true , 512 , \JSON_THROW_ON_ERROR );
128- $ data = $ requestData ['data ' ] ?? [];
129- $ actionArguments = $ requestData ['args ' ] ?? [];
143+ $ controller = $ event ->getController ();
144+
145+ if ($ controller instanceof BatchActionController) {
146+ return ;
130147 }
131148
132- if (!\is_array ($ controller = $ event -> getController () ) || 2 !== \count ($ controller )) {
149+ if (!\is_array ($ controller ) || 2 !== \count ($ controller )) {
133150 throw new \RuntimeException ('Not a valid live component. ' );
134151 }
135152
@@ -143,13 +160,21 @@ public function onKernelController(ControllerEvent $event): void
143160 throw new NotFoundHttpException (sprintf ('The action "%s" either doesn \'t exist or is not allowed in "%s". Make sure it exist and has the LiveAction attribute above it. ' , $ action , \get_class ($ component )));
144161 }
145162
146- $ mounted = $ this ->container ->get (LiveComponentHydrator::class)->hydrate (
147- $ component ,
148- $ data ,
149- $ request ->attributes ->get ('_component_name ' )
150- );
163+ if ($ event ->isMainRequest ()) {
164+ $ data = $ this ->parseDataFor ($ request );
165+
166+ $ request ->attributes ->set ('_component_action_args ' , $ data ['args ' ]);
167+ $ request ->attributes ->set ('_mounted_component ' , $ this ->container ->get (LiveComponentHydrator::class)->hydrate (
168+ $ component ,
169+ $ data ['data ' ],
170+ $ request ->attributes ->get ('_component_name ' )
171+ ));
172+ } else {
173+ // sub-request
174+ $ event ->setController ([$ request ->attributes ->get ('_mounted_component ' )->getComponent (), $ action ]);
175+ }
151176
152- $ request ->attributes ->set ( ' _mounted_component ' , $ mounted );
177+ $ actionArguments = $ request ->attributes ->get ( ' _component_action_args ' , [] );
153178
154179 // extra variables to be made available to the controller
155180 // (for "actions" only)
@@ -160,12 +185,45 @@ public function onKernelController(ControllerEvent $event): void
160185 }
161186 }
162187
188+ /**
189+ * @return array{
190+ * data: array,
191+ * args: array,
192+ * actions: array
193+ * }
194+ */
195+ private function parseDataFor (Request $ request ): array
196+ {
197+ if ($ request ->query ->has ('data ' )) {
198+ return [
199+ 'data ' => json_decode ($ request ->query ->get ('data ' ), true , 512 , \JSON_THROW_ON_ERROR ),
200+ 'args ' => [],
201+ 'actions ' => [],
202+ ];
203+ }
204+
205+ $ requestData = json_decode ($ request ->getContent (), true , 512 , \JSON_THROW_ON_ERROR );
206+
207+ return [
208+ 'data ' => $ requestData ['data ' ] ?? [],
209+ 'args ' => $ requestData ['args ' ] ?? [],
210+ 'actions ' => $ requestData ['actions ' ] ?? [],
211+ ];
212+ }
213+
163214 public function onKernelView (ViewEvent $ event ): void
164215 {
165216 if (!$ this ->isLiveComponentRequest ($ request = $ event ->getRequest ())) {
166217 return ;
167218 }
168219
220+ if (!$ event ->isMainRequest ()) {
221+ // sub-request, so skip rendering
222+ $ event ->setResponse (new Response ());
223+
224+ return ;
225+ }
226+
169227 $ event ->setResponse ($ this ->createResponse ($ request ->attributes ->get ('_mounted_component ' )));
170228 }
171229
0 commit comments