Skip to content

Commit 0ae2d37

Browse files
committed
replace FormRequest magic field access with explicit validated accessors
1 parent a6c3eb8 commit 0ae2d37

File tree

4 files changed

+95
-37
lines changed

4 files changed

+95
-37
lines changed

system/HTTP/FormRequest.php

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -174,30 +174,33 @@ public function validated(): array
174174
}
175175

176176
/**
177-
* Returns a single validated field value by name, or null if the field
178-
* is not present in the validated data (either not declared in rules() or
179-
* validation has not yet run).
177+
* Returns a single validated field value by name, or the default value
178+
* if the field is not present in the validated data.
180179
*
181-
* Allows accessing individual fields as object properties:
182-
*
183-
* $title = $request->title;
184-
*
185-
* @return mixed
180+
* Supports dot-array syntax for nested validated data.
186181
*/
187-
public function __get(string $name)
182+
public function getValidated(string $key, mixed $default = null): mixed
188183
{
189-
return $this->validatedData[$name] ?? null;
184+
helper('array');
185+
186+
if (! dot_array_has($key, $this->validatedData)) {
187+
return $default;
188+
}
189+
190+
return dot_array_search($key, $this->validatedData);
190191
}
191192

192193
/**
193-
* Returns true when the named field exists in the validated data and its
194-
* value is not null. Mirrors standard PHP isset() semantics on properties:
194+
* Returns true when the named field exists in the validated data, even if
195+
* its value is null.
195196
*
196-
* if (isset($request->title)) { ... }
197+
* Supports dot-array syntax for nested validated data.
197198
*/
198-
public function __isset(string $name): bool
199+
public function hasValidated(string $key): bool
199200
{
200-
return isset($this->validatedData[$name]);
201+
helper('array');
202+
203+
return dot_array_has($key, $this->validatedData);
201204
}
202205

203206
/**

tests/system/HTTP/FormRequestTest.php

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,59 +164,115 @@ public function testValidatedReturnsOnlyFieldsCoveredByRules(): void
164164
}
165165

166166
// -------------------------------------------------------------------------
167-
// __get / __isset - property-style access to validated fields
167+
// Explicit access to validated fields
168168
// -------------------------------------------------------------------------
169169

170-
public function testMagicGetReturnsValidatedFieldValue(): void
170+
public function testGetValidatedReturnsValidatedFieldValue(): void
171171
{
172172
service('superglobals')->setPost('title', 'Hello World');
173173
service('superglobals')->setPost('body', 'Some body text');
174174

175175
$formRequest = new ValidPostFormRequest($this->makeRequest());
176176
$formRequest->resolveRequest();
177177

178-
$this->assertSame('Hello World', $formRequest->title);
179-
$this->assertSame('Some body text', $formRequest->body);
178+
$this->assertSame('Hello World', $formRequest->getValidated('title'));
179+
$this->assertSame('Some body text', $formRequest->getValidated('body'));
180180
}
181181

182-
public function testMagicGetReturnsNullForMissingField(): void
182+
public function testGetValidatedReturnsNullForMissingField(): void
183183
{
184184
service('superglobals')->setPost('title', 'Hello World');
185185
service('superglobals')->setPost('body', 'Some body text');
186186

187187
$formRequest = new ValidPostFormRequest($this->makeRequest());
188188
$formRequest->resolveRequest();
189189

190-
$this->assertNull($formRequest->nonexistent); // @phpstan-ignore property.notFound
190+
$this->assertNull($formRequest->getValidated('nonexistent'));
191191
}
192192

193-
public function testMagicGetReturnsNullBeforeValidationRuns(): void
193+
public function testGetValidatedReturnsDefaultForMissingField(): void
194+
{
195+
service('superglobals')->setPost('title', 'Hello World');
196+
service('superglobals')->setPost('body', 'Some body text');
197+
198+
$formRequest = new ValidPostFormRequest($this->makeRequest());
199+
$formRequest->resolveRequest();
200+
201+
$this->assertSame('fallback', $formRequest->getValidated('nonexistent', 'fallback'));
202+
}
203+
204+
public function testGetValidatedReturnsNullBeforeValidationRuns(): void
194205
{
195206
$formRequest = new ValidPostFormRequest($this->makeRequest());
196207

197-
$this->assertNull($formRequest->title);
208+
$this->assertNull($formRequest->getValidated('title'));
198209
}
199210

200-
public function testMagicIssetReturnsTrueForValidatedField(): void
211+
public function testHasValidatedReturnsTrueForValidatedField(): void
201212
{
202213
service('superglobals')->setPost('title', 'Hello World');
203214
service('superglobals')->setPost('body', 'Some body text');
204215

205216
$formRequest = new ValidPostFormRequest($this->makeRequest());
206217
$formRequest->resolveRequest();
207218

208-
$this->assertTrue(isset($formRequest->title));
219+
$this->assertTrue($formRequest->hasValidated('title'));
209220
}
210221

211-
public function testMagicIssetReturnsFalseForMissingField(): void
222+
public function testHasValidatedReturnsFalseForMissingField(): void
212223
{
213224
service('superglobals')->setPost('title', 'Hello World');
214225
service('superglobals')->setPost('body', 'Some body text');
215226

216227
$formRequest = new ValidPostFormRequest($this->makeRequest());
217228
$formRequest->resolveRequest();
218229

219-
$this->assertFalse(isset($formRequest->nonexistent)); // @phpstan-ignore property.notFound
230+
$this->assertFalse($formRequest->hasValidated('nonexistent'));
231+
}
232+
233+
public function testGetValidatedAndHasValidatedSupportDotSyntax(): void
234+
{
235+
service('superglobals')->setPost('post', [
236+
'title' => 'Hello World',
237+
'meta' => [
238+
'slug' => 'hello-world',
239+
],
240+
]);
241+
242+
$formRequest = new class ($this->makeRequest()) extends FormRequest {
243+
public function rules(): array
244+
{
245+
return [
246+
'post.title' => 'required',
247+
'post.meta.slug' => 'required',
248+
];
249+
}
250+
};
251+
252+
$formRequest->resolveRequest();
253+
254+
$this->assertSame('Hello World', $formRequest->getValidated('post.title'));
255+
$this->assertSame('hello-world', $formRequest->getValidated('post.meta.slug'));
256+
$this->assertTrue($formRequest->hasValidated('post.meta.slug'));
257+
}
258+
259+
public function testHasValidatedReturnsTrueForNullValidatedField(): void
260+
{
261+
service('superglobals')->setServer('CONTENT_TYPE', 'application/json');
262+
263+
$formRequest = new class ($this->makeRequest('{"note":null}')) extends FormRequest {
264+
public function rules(): array
265+
{
266+
return ['note' => 'permit_empty'];
267+
}
268+
};
269+
270+
$formRequest->resolveRequest();
271+
272+
$this->assertSame(['note' => null], $formRequest->validated());
273+
$this->assertNull($formRequest->getValidated('note'));
274+
$this->assertNull($formRequest->getValidated('note', 'fallback'));
275+
$this->assertTrue($formRequest->hasValidated('note'));
220276
}
221277

222278
// -------------------------------------------------------------------------

user_guide_src/source/incoming/form_requests.rst

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,13 @@ are silently discarded, protecting against mass-assignment.
5656
.. literalinclude:: form_requests/009.php
5757
:lines: 2-
5858

59-
Individual fields can also be read as object properties. This is equivalent to
60-
``$request->validated()['field'] ?? null``:
59+
Use ``getValidated()`` to read a single validated field and ``hasValidated()``
60+
to check whether a validated key exists, including keys whose value is
61+
``null``. Both methods support dot-array syntax for nested validated data:
6162

6263
.. literalinclude:: form_requests/014.php
6364
:lines: 2-
6465

65-
``isset($request->field)`` returns ``true`` when the field was validated and
66-
has a non-null value, following standard PHP ``isset()`` semantics.
67-
6866
Accessing Other Request Data
6967
============================
7068

@@ -230,7 +228,8 @@ whose type extends ``FormRequest``:
230228
rules are applied.
231229
#. ``run()`` executes the validation rules. If it fails, ``failedValidation()``
232230
is called, and its response is returned to the client.
233-
#. The validated data is stored internally and available via ``validated()``.
231+
#. The validated data is stored internally and available via ``validated()``,
232+
``getValidated()``, and ``hasValidated()``.
234233
#. The resolved FormRequest object is injected into the controller method or
235234
closure.
236235

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?php
22

3-
$title = $request->title; // same as $request->validated()['title'] ?? null
4-
$body = $request->body;
3+
$title = $request->getValidated('title');
4+
$slug = $request->getValidated('post.meta.slug', 'draft');
55

6-
if (isset($request->note)) {
7-
// 'note' was validated and has a non-null value
6+
if ($request->hasValidated('note')) {
7+
// 'note' was validated, even if its value is null
88
}

0 commit comments

Comments
 (0)