|
14 | 14 | use Illuminate\Foundation\Http\Attributes\WithoutInferringRules; |
15 | 15 | use Illuminate\Foundation\Http\TypedFormRequest; |
16 | 16 | use Illuminate\Http\Request; |
| 17 | +use Illuminate\Http\UploadedFile; |
17 | 18 | use Illuminate\Support\Collection; |
18 | 19 | use Illuminate\Validation\Rule; |
19 | 20 | use Illuminate\Validation\ValidationException; |
@@ -1436,6 +1437,103 @@ public function testStopOnFirstFailureAttributeReportsOnlyOneError() |
1436 | 1437 | $this->assertArrayNotHasKey('age', $e->errors()); |
1437 | 1438 | } |
1438 | 1439 | } |
| 1440 | + |
| 1441 | + public function testUploadedFileTypeAcceptsValidFile() |
| 1442 | + { |
| 1443 | + $file = UploadedFile::fake()->create('document.pdf', 100); |
| 1444 | + |
| 1445 | + $request = Request::create('', 'POST', ['title' => 'My Doc'], files: ['attachment' => $file]); |
| 1446 | + $this->app->instance('request', $request); |
| 1447 | + |
| 1448 | + $actual = $this->app->make(FileUploadRequest::class); |
| 1449 | + |
| 1450 | + $this->assertSame('My Doc', $actual->title); |
| 1451 | + $this->assertInstanceOf(UploadedFile::class, $actual->attachment); |
| 1452 | + $this->assertSame('document.pdf', $actual->attachment->getClientOriginalName()); |
| 1453 | + } |
| 1454 | + |
| 1455 | + public function testUploadedFileTypeRejectsNonFileInput() |
| 1456 | + { |
| 1457 | + $request = Request::create('', 'POST', [ |
| 1458 | + 'title' => 'My Doc', |
| 1459 | + 'attachment' => 'not-a-file', |
| 1460 | + ]); |
| 1461 | + $this->app->instance('request', $request); |
| 1462 | + |
| 1463 | + try { |
| 1464 | + $this->app->make(FileUploadRequest::class); |
| 1465 | + self::fail('No exception thrown!'); |
| 1466 | + } catch (ValidationException $e) { |
| 1467 | + $this->assertArrayHasKey('attachment', $e->errors()); |
| 1468 | + } |
| 1469 | + } |
| 1470 | + |
| 1471 | + public function testUploadedFileTypeMissingRequiredFileFails() |
| 1472 | + { |
| 1473 | + $request = Request::create('', 'POST', ['title' => 'My Doc']); |
| 1474 | + $this->app->instance('request', $request); |
| 1475 | + |
| 1476 | + try { |
| 1477 | + $this->app->make(FileUploadRequest::class); |
| 1478 | + self::fail('No exception thrown!'); |
| 1479 | + } catch (ValidationException $e) { |
| 1480 | + $this->assertArrayHasKey('attachment', $e->errors()); |
| 1481 | + } |
| 1482 | + } |
| 1483 | + |
| 1484 | + public function testNullableUploadedFileAcceptsNull() |
| 1485 | + { |
| 1486 | + $request = Request::create('', 'POST', [ |
| 1487 | + 'title' => 'My Doc', |
| 1488 | + 'attachment' => null, |
| 1489 | + ]); |
| 1490 | + $this->app->instance('request', $request); |
| 1491 | + |
| 1492 | + $actual = $this->app->make(NullableFileUploadRequest::class); |
| 1493 | + |
| 1494 | + $this->assertInstanceOf(NullableFileUploadRequest::class, $actual); |
| 1495 | + $this->assertSame('My Doc', $actual->title); |
| 1496 | + $this->assertNull($actual->attachment); |
| 1497 | + } |
| 1498 | + |
| 1499 | + public function testOptionalUploadedFileUsesDefaultWhenOmitted() |
| 1500 | + { |
| 1501 | + $request = Request::create('', 'POST', ['title' => 'My Doc']); |
| 1502 | + $this->app->instance('request', $request); |
| 1503 | + |
| 1504 | + $actual = $this->app->make(NullableFileUploadRequest::class); |
| 1505 | + |
| 1506 | + $this->assertInstanceOf(NullableFileUploadRequest::class, $actual); |
| 1507 | + $this->assertNull($actual->attachment); |
| 1508 | + } |
| 1509 | + |
| 1510 | + public function testUploadedFileMapFromUseMappedFieldName() |
| 1511 | + { |
| 1512 | + $file = UploadedFile::fake()->create('photo.jpg', 200); |
| 1513 | + |
| 1514 | + $request = Request::create('', 'POST', ['title' => 'My Photo'], files: ['user_avatar' => $file]); |
| 1515 | + $this->app->instance('request', $request); |
| 1516 | + |
| 1517 | + $actual = $this->app->make(MappedFileUploadRequest::class); |
| 1518 | + |
| 1519 | + $this->assertInstanceOf(MappedFileUploadRequest::class, $actual); |
| 1520 | + $this->assertInstanceOf(UploadedFile::class, $actual->avatar); |
| 1521 | + $this->assertSame('photo.jpg', $actual->avatar->getClientOriginalName()); |
| 1522 | + } |
| 1523 | + |
| 1524 | + public function testUploadedFileMapFromMissingFileUsesMappedValidationKey() |
| 1525 | + { |
| 1526 | + $request = Request::create('', 'POST', ['title' => 'My Photo']); |
| 1527 | + $this->app->instance('request', $request); |
| 1528 | + |
| 1529 | + try { |
| 1530 | + $this->app->make(MappedFileUploadRequest::class); |
| 1531 | + self::fail('No exception thrown!'); |
| 1532 | + } catch (ValidationException $e) { |
| 1533 | + $this->assertArrayHasKey('user_avatar', $e->errors()); |
| 1534 | + $this->assertArrayNotHasKey('avatar', $e->errors()); |
| 1535 | + } |
| 1536 | + } |
1439 | 1537 | } |
1440 | 1538 |
|
1441 | 1539 | class MyTypedForm extends TypedFormRequest |
@@ -1992,3 +2090,31 @@ public function __construct( |
1992 | 2090 | ) { |
1993 | 2091 | } |
1994 | 2092 | } |
| 2093 | + |
| 2094 | +class FileUploadRequest extends TypedFormRequest |
| 2095 | +{ |
| 2096 | + public function __construct( |
| 2097 | + public string $title, |
| 2098 | + public UploadedFile $attachment, |
| 2099 | + ) { |
| 2100 | + } |
| 2101 | +} |
| 2102 | + |
| 2103 | +class NullableFileUploadRequest extends TypedFormRequest |
| 2104 | +{ |
| 2105 | + public function __construct( |
| 2106 | + public string $title, |
| 2107 | + public ?UploadedFile $attachment = null, |
| 2108 | + ) { |
| 2109 | + } |
| 2110 | +} |
| 2111 | + |
| 2112 | +class MappedFileUploadRequest extends TypedFormRequest |
| 2113 | +{ |
| 2114 | + public function __construct( |
| 2115 | + public string $title, |
| 2116 | + #[MapFrom('user_avatar')] |
| 2117 | + public UploadedFile $avatar, |
| 2118 | + ) { |
| 2119 | + } |
| 2120 | +} |
0 commit comments