|
1 | 1 | # Ray.InputQuery |
2 | 2 |
|
3 | | -Structured input objects from HTTP. |
| 3 | +[](https://github.com/ray-di/Ray.InputQuery/actions/workflows/continuous-integration.yml) |
| 4 | +[](https://shepherd.dev/github/ray-di/Ray.InputQuery) |
| 5 | +[](https://codecov.io/gh/ray-di/Ray.InputQuery) |
| 6 | + |
| 7 | +Structured input objects from HTTP with 100% test coverage. |
4 | 8 |
|
5 | 9 | ## Overview |
6 | 10 |
|
@@ -44,10 +48,28 @@ public function createTodo(TodoInput $input) { |
44 | 48 | composer require ray/input-query |
45 | 49 | ``` |
46 | 50 |
|
| 51 | +## Demo |
| 52 | + |
| 53 | +To see file upload integration in action: |
| 54 | + |
| 55 | +```bash |
| 56 | +php -S localhost:8080 -t demo/ |
| 57 | +``` |
| 58 | + |
| 59 | +Then visit [http://localhost:8080](http://localhost:8080) in your browser. |
| 60 | + |
47 | 61 | ## Documentation |
48 | 62 |
|
49 | 63 | Comprehensive documentation including design philosophy, AI prompts for development assistance, and sample data examples can be found in the [docs/](docs/) directory. |
50 | 64 |
|
| 65 | +### Framework Integration |
| 66 | + |
| 67 | +For framework-specific integration examples, see the **[Framework Integration Guide](docs/framework_integration.md)** which covers: |
| 68 | + |
| 69 | +- Laravel, Symfony, CakePHP, Yii Framework 1.x, BEAR.Sunday, and Slim Framework |
| 70 | +- Three usage patterns (Reflection, Direct Object Creation, Spread Operator) |
| 71 | +- Testing examples and best practices |
| 72 | + |
51 | 73 | ## Usage |
52 | 74 |
|
53 | 75 | Ray.InputQuery converts flat query data into typed PHP objects automatically. |
@@ -89,11 +111,11 @@ echo $user->email; // john@example.com |
89 | 111 | // Method argument resolution from $_POST |
90 | 112 | $method = new ReflectionMethod(UserController::class, 'register'); |
91 | 113 | $args = $inputQuery->getArguments($method, $_POST); |
92 | | -$controller->register(...$args); |
| 114 | +$result = $method->invokeArgs($controller, $args); |
93 | 115 |
|
94 | | -// Or with PSR-7 Request |
| 116 | + // Or with PSR-7 Request |
95 | 117 | $args = $inputQuery->getArguments($method, $request->getParsedBody()); |
96 | | -$controller->register(...$args); |
| 118 | +$result = $method->invokeArgs($controller, $args); |
97 | 119 | ``` |
98 | 120 |
|
99 | 121 | ### Nested Objects |
@@ -185,8 +207,8 @@ $data = [ |
185 | 207 | ] |
186 | 208 | ]; |
187 | 209 |
|
188 | | -$args = $inputQuery->getArguments($method, $data); |
189 | | -// $args[0] will be an array of UserInput objects |
| 210 | +$result = $method->invokeArgs($controller, $inputQuery->getArguments($method, $data)); |
| 211 | +// Arguments automatically resolved as UserInput objects |
190 | 212 | ``` |
191 | 213 |
|
192 | 214 | #### Simple array values (e.g., checkboxes) |
@@ -286,13 +308,141 @@ All query keys are normalized to camelCase: |
286 | 308 | - `user-name` → `userName` |
287 | 309 | - `UserName` → `userName` |
288 | 310 |
|
| 311 | +## File Upload Integration |
| 312 | + |
| 313 | +Ray.InputQuery provides comprehensive file upload support through integration with [Koriym.FileUpload](https://github.com/koriym/Koriym.FileUpload): |
| 314 | + |
| 315 | +```bash |
| 316 | +composer require koriym/file-upload |
| 317 | +``` |
| 318 | + |
| 319 | +### Using #[InputFile] Attribute |
| 320 | + |
| 321 | +For file uploads, use the dedicated `#[InputFile]` attribute which provides validation options: |
| 322 | + |
| 323 | +```php |
| 324 | +use Koriym\FileUpload\FileUpload; |
| 325 | +use Koriym\FileUpload\ErrorFileUpload; |
| 326 | +use Ray\InputQuery\Attribute\InputFile; |
| 327 | + |
| 328 | +final class UserProfileInput |
| 329 | +{ |
| 330 | + public function __construct( |
| 331 | + #[Input] public readonly string $name, |
| 332 | + #[Input] public readonly string $email, |
| 333 | + #[InputFile( |
| 334 | + maxSize: 5 * 1024 * 1024, // 5MB |
| 335 | + allowedTypes: ['image/jpeg', 'image/png'], |
| 336 | + allowedExtensions: ['jpg', 'jpeg', 'png'] |
| 337 | + )] |
| 338 | + public readonly FileUpload|ErrorFileUpload $avatar, |
| 339 | + #[InputFile] public readonly FileUpload|ErrorFileUpload|null $banner = null, |
| 340 | + ) {} |
| 341 | +} |
| 342 | +``` |
| 343 | + |
| 344 | +// Method usage example - Direct attribute approach |
| 345 | + |
| 346 | +### Test-Friendly Design |
| 347 | + |
| 348 | +File upload handling is designed to be test-friendly: |
| 349 | + |
| 350 | +- **Production** - FileUpload library handles file uploads automatically |
| 351 | +- **Testing** - Direct FileUpload object injection for easy mocking |
| 352 | + |
| 353 | +```php |
| 354 | +// Production usage - FileUpload library handles file uploads automatically |
| 355 | +$input = $inputQuery->create(UserProfileInput::class, $_POST); |
| 356 | +// FileUpload objects are created automatically from uploaded files |
| 357 | + |
| 358 | +// Testing usage - inject mock FileUpload objects directly for easy testing |
| 359 | +$mockAvatar = FileUpload::create([ |
| 360 | + 'name' => 'test.jpg', |
| 361 | + 'type' => 'image/jpeg', |
| 362 | + 'size' => 1024, |
| 363 | + 'tmp_name' => '/tmp/test', |
| 364 | + 'error' => UPLOAD_ERR_OK, |
| 365 | +]); |
| 366 | + |
| 367 | +$input = $inputQuery->create(UserProfileInput::class, [ |
| 368 | + 'name' => 'Test User', |
| 369 | + 'email' => 'test@example.com', |
| 370 | + 'avatar' => $mockAvatar, |
| 371 | + 'banner' => null |
| 372 | +]); |
| 373 | +``` |
| 374 | + |
| 375 | +### Multiple File Uploads |
| 376 | + |
| 377 | +Support for multiple file uploads using array types with validation: |
| 378 | + |
| 379 | +```php |
| 380 | +final class GalleryInput |
| 381 | +{ |
| 382 | + /** |
| 383 | + * @param list<FileUpload|ErrorFileUpload> $images |
| 384 | + */ |
| 385 | + public function __construct( |
| 386 | + #[Input] public readonly string $title, |
| 387 | + #[InputFile( |
| 388 | + maxSize: 10 * 1024 * 1024, // 10MB per file |
| 389 | + allowedTypes: ['image/*'] |
| 390 | + )] |
| 391 | + public readonly array $images, |
| 392 | + ) {} |
| 393 | +} |
| 394 | + |
| 395 | +// Method usage example |
| 396 | +class GalleryController |
| 397 | +{ |
| 398 | + public function createGallery(GalleryInput $input): void |
| 399 | + { |
| 400 | + $savedImages = []; |
| 401 | + foreach ($input->images as $image) { |
| 402 | + if ($image instanceof FileUpload) { |
| 403 | + $savedImages[] = $this->saveFile($image, 'gallery/'); |
| 404 | + } elseif ($image instanceof ErrorFileUpload) { |
| 405 | + // Log error but continue with other images |
| 406 | + $this->logger->warning('Image upload failed: ' . $image->message); |
| 407 | + } |
| 408 | + } |
| 409 | + |
| 410 | + $this->galleryService->create($input->title, $savedImages); |
| 411 | + } |
| 412 | +} |
| 413 | + |
| 414 | +// Production usage - FileUpload library handles multiple files automatically |
| 415 | +$input = $inputQuery->create(GalleryInput::class, $_POST); |
| 416 | +// Array of FileUpload objects created automatically from uploaded files |
| 417 | + |
| 418 | +// Testing usage - inject array of mock FileUpload objects for easy testing |
| 419 | +$mockImages = [ |
| 420 | + FileUpload::create(['name' => 'image1.jpg', ...]), |
| 421 | + FileUpload::create(['name' => 'image2.png', ...]) |
| 422 | +]; |
| 423 | + |
| 424 | +$input = $inputQuery->create(GalleryInput::class, [ |
| 425 | + 'title' => 'My Gallery', |
| 426 | + 'images' => $mockImages |
| 427 | +]); |
| 428 | +``` |
| 429 | + |
289 | 430 | ## Integration |
290 | 431 |
|
291 | 432 | Ray.InputQuery is designed as a foundation library to be used by: |
292 | 433 |
|
293 | 434 | - [Ray.MediaQuery](https://github.com/ray-di/Ray.MediaQuery) - For database query integration |
294 | 435 | - [BEAR.Resource](https://github.com/bearsunday/BEAR.Resource) - For REST resource integration |
295 | 436 |
|
| 437 | +## Project Quality |
| 438 | + |
| 439 | +This project maintains high quality standards: |
| 440 | + |
| 441 | +- **100% Code Coverage** - Achieved through public interface tests only |
| 442 | +- **Static Analysis** - Psalm and PHPStan at maximum levels |
| 443 | +- **Test Design** - No private method tests, ensuring maintainability |
| 444 | +- **Type Safety** - Comprehensive Psalm type annotations |
| 445 | + |
296 | 446 | ## Requirements |
297 | 447 |
|
298 | 448 | - PHP 8.1+ |
|
0 commit comments