Skip to content

Commit b687b07

Browse files
committed
Change server to expect JSON data
1 parent c36162c commit b687b07

File tree

6 files changed

+38
-22
lines changed

6 files changed

+38
-22
lines changed

.php-cs-fixer.dist.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
->append([__FILE__])
2525
->notPath('#/Fixtures/#')
2626
->notPath('#/app/var/#')
27+
->notPath('#/var/cache/#')
2728
->notPath('Turbo/Attribute/Broadcast.php') // Need https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/4702
2829
)
2930
;

src/LiveComponent/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## 2.1.0
44

5+
- Your component's live "data" is now send over Ajax as a JSON string.
6+
Previously data was sent as pure query parameters or as pure POST data.
7+
However, this made it impossible to keep certain data types, like
8+
distinguishing between `null` and `''`. This has no impact on end-users.
9+
510
- Added `data-live-ignore` attribute. If included in an element, that element
611
will not be updated on re-render.
712

src/LiveComponent/src/ComponentWithFormTrait.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,13 @@ private function extractFormValues(FormView $formView): array
178178
$values = [];
179179
foreach ($formView->children as $child) {
180180
$name = $child->vars['name'];
181-
if (\count($child->children) > 0) {
181+
182+
// if there are children, expand their values recursively
183+
// UNLESS the field is "expanded": in that case the value
184+
// is already correct. For example, an expanded ChoiceType with
185+
// options "text" and "phone" would already have a value in the format
186+
// ["text"] (assuming "text" is checked and "phone" is not).
187+
if (!($child->vars['expanded'] ?? false) && \count($child->children) > 0) {
182188
$values[$name] = $this->extractFormValues($child);
183189

184190
continue;

src/LiveComponent/src/EventListener/LiveComponentSubscriber.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,13 @@ public function onKernelController(ControllerEvent $event): void
122122
return;
123123
}
124124

125-
$data = array_merge(
126-
$request->query->all(),
127-
$request->request->all()
128-
);
125+
if ($request->query->has('data')) {
126+
// ?data=
127+
$data = json_decode($request->query->get('data'), true, 512, \JSON_THROW_ON_ERROR);
128+
} else {
129+
// OR body of the request is JSON
130+
$data = json_decode($request->getContent(), true, 512, \JSON_THROW_ON_ERROR);
131+
}
129132

130133
if (!\is_array($controller = $event->getController()) || 2 !== \count($controller)) {
131134
throw new \RuntimeException('Not a valid live component.');

src/LiveComponent/tests/Functional/EventListener/LiveComponentSubscriberTest.php

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function testCanRenderComponentAsHtml(): void
5454

5555
$this->browser()
5656
->throwExceptions()
57-
->get('/_components/component1?'.http_build_query($dehydrated))
57+
->get('/_components/component1?data='.urlencode(json_encode($dehydrated)))
5858
->assertSuccessful()
5959
->assertHeaderContains('Content-Type', 'html')
6060
->assertContains('Prop1: '.$entity->id)
@@ -80,16 +80,17 @@ public function testCanExecuteComponentAction(): void
8080

8181
$this->browser()
8282
->throwExceptions()
83-
->get('/_components/component2?'.http_build_query($dehydrated))
83+
->get('/_components/component2?data='.urlencode(json_encode($dehydrated)))
8484
->assertSuccessful()
8585
->assertHeaderContains('Content-Type', 'html')
8686
->assertContains('Count: 1')
8787
->use(function (HtmlResponse $response) use (&$token) {
8888
// get a valid token to use for actions
8989
$token = $response->crawler()->filter('div')->first()->attr('data-live-csrf-value');
9090
})
91-
->post('/_components/component2/increase?'.http_build_query($dehydrated), [
91+
->post('/_components/component2/increase', [
9292
'headers' => ['X-CSRF-TOKEN' => $token],
93+
'body' => json_encode($dehydrated),
9394
])
9495
->assertSuccessful()
9596
->assertHeaderContains('Content-Type', 'html')
@@ -168,7 +169,7 @@ public function testBeforeReRenderHookOnlyExecutedDuringAjax(): void
168169
->visit('/render-template/template1')
169170
->assertSuccessful()
170171
->assertSee('BeforeReRenderCalled: No')
171-
->get('/_components/component2?'.http_build_query($dehydrated))
172+
->get('/_components/component2?data='.urlencode(json_encode($dehydrated)))
172173
->assertSuccessful()
173174
->assertSee('BeforeReRenderCalled: Yes')
174175
;
@@ -190,25 +191,27 @@ public function testCanRedirectFromComponentAction(): void
190191

191192
$this->browser()
192193
->throwExceptions()
193-
->get('/_components/component2?'.http_build_query($dehydrated))
194+
->get('/_components/component2?data='.urlencode(json_encode($dehydrated)))
194195
->assertSuccessful()
195196
->use(function (HtmlResponse $response) use (&$token) {
196197
// get a valid token to use for actions
197198
$token = $response->crawler()->filter('div')->first()->attr('data-live-csrf-value');
198199
})
199200
->interceptRedirects()
200201
// with no custom header, it redirects like a normal browser
201-
->post('/_components/component2/redirect?'.http_build_query($dehydrated), [
202+
->post('/_components/component2/redirect', [
202203
'headers' => ['X-CSRF-TOKEN' => $token],
204+
'body' => json_encode($dehydrated),
203205
])
204206
->assertRedirectedTo('/')
205207

206208
// with custom header, a special 204 is returned
207-
->post('/_components/component2/redirect?'.http_build_query($dehydrated), [
209+
->post('/_components/component2/redirect', [
208210
'headers' => [
209211
'Accept' => 'application/vnd.live-component+html',
210212
'X-CSRF-TOKEN' => $token,
211213
],
214+
'body' => json_encode($dehydrated),
212215
])
213216
->assertStatus(204)
214217
->assertHeaderEquals('Location', '/')
@@ -229,13 +232,10 @@ public function testInjectsLiveArgs(): void
229232
$dehydrated = $hydrator->dehydrate($component);
230233
$token = null;
231234

232-
$dehydratedWithArgs = array_merge($dehydrated, [
233-
'args' => http_build_query(['arg1' => 'hello', 'arg2' => 666, 'custom' => '33.3']),
234-
]);
235-
235+
$argsQueryParams = http_build_query(['args' => http_build_query(['arg1' => 'hello', 'arg2' => 666, 'custom' => '33.3'])]);
236236
$this->browser()
237237
->throwExceptions()
238-
->get('/_components/component6?'.http_build_query($dehydrated))
238+
->get('/_components/component6?data='.urlencode(json_encode($dehydrated)).'&'.$argsQueryParams)
239239
->assertSuccessful()
240240
->assertHeaderContains('Content-Type', 'html')
241241
->assertContains('Arg1: not provided')
@@ -245,8 +245,9 @@ public function testInjectsLiveArgs(): void
245245
// get a valid token to use for actions
246246
$token = $response->crawler()->filter('div')->first()->attr('data-live-csrf-value');
247247
})
248-
->post('/_components/component6/inject?'.http_build_query($dehydratedWithArgs), [
248+
->post('/_components/component6/inject?'.$argsQueryParams, [
249249
'headers' => ['X-CSRF-TOKEN' => $token],
250+
'body' => json_encode($dehydrated),
250251
])
251252
->assertSuccessful()
252253
->assertHeaderContains('Content-Type', 'html')

src/LiveComponent/tests/Functional/Form/ComponentWithFormTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function testFormValuesRebuildAfterFormChanges(): void
4646
$token = null;
4747

4848
$this->browser()
49-
->get('/_components/form_with_collection_type?'.http_build_query($dehydrated))
49+
->get('/_components/form_with_collection_type?data='.urlencode(json_encode($dehydrated)))
5050
->use(function (HtmlResponse $response) use (&$dehydrated, &$token) {
5151
// mimic user typing
5252
$dehydrated['blog_post_form']['content'] = 'changed description by user';
@@ -56,7 +56,7 @@ public function testFormValuesRebuildAfterFormChanges(): void
5656

5757
// post to action, which will add a new embedded comment
5858
->post('/_components/form_with_collection_type/addComment', [
59-
'body' => $dehydrated,
59+
'body' => json_encode($dehydrated),
6060
'headers' => ['X-CSRF-TOKEN' => $token],
6161
])
6262
->assertStatus(422)
@@ -92,7 +92,7 @@ public function testFormValuesRebuildAfterFormChanges(): void
9292

9393
// post to action, which will remove the original embedded comment
9494
->post('/_components/form_with_collection_type/removeComment?'.http_build_query(['args' => 'index=0']), [
95-
'body' => $dehydrated,
95+
'body' => json_encode($dehydrated),
9696
'headers' => ['X-CSRF-TOKEN' => $token],
9797
])
9898
->assertStatus(422)
@@ -136,7 +136,7 @@ public function testFormRemembersValidationFromInitialForm(): void
136136
$dehydrated['validatedFields'][] = 'blog_post_form.content';
137137

138138
$this->browser()
139-
->get('/_components/form_with_collection_type?'.http_build_query($dehydrated))
139+
->get('/_components/form_with_collection_type?data='.urlencode(json_encode($dehydrated)))
140140
// normal validation happened
141141
->assertContains('The content field is too short')
142142
// title is STILL validated as all fields should be validated

0 commit comments

Comments
 (0)