From 41d521ed451578b1b8ccd3c27ffb4aa86c10530a Mon Sep 17 00:00:00 2001 From: Pascal Baljet Date: Wed, 23 Dec 2020 21:28:42 +0100 Subject: [PATCH] Support for nested data (#3) --- CHANGELOG.md | 4 ++ README.md | 19 ++++++ src/Request/ConvertsBase64ToFiles.php | 36 +++++++++- tests/Request/ConvertsBase64ToFilesTest.php | 73 +++++++++++++++++++-- 4 files changed, 125 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13dd7c0..ecb7fad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `laravel-mixins` will be documented in this file. +## 2.1.1 - 2020-12-23 + +- Support for nested request data when using the `ConvertsBase64ToFiles` trait + ## 2.1.0 - 2020-10-13 - Added `InKeys` rule. diff --git a/README.md b/README.md index f3f20ce..76c597b 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,25 @@ $jpgFile = $request->file('jpg_image'); $jpgFile->getClientOriginalName(); ``` +This trait supports nested data as well. You can either reference the keys by a nested array, or with a [dotted notation](https://laravel.com/docs/master/helpers#method-array-dot): + +```php +class ImageRequest extends FormRequest +{ + use ConvertsBase64ToFiles; + + protected function base64FileKeys(): array + { + return [ + 'company.logo' => 'Logo.jpg', + 'user' => [ + 'avatar' => 'Avatar.jpg', + ], + ]; + } +} +``` + Want to know more about this trait? Check out the [blog post](https://protone.media/blog/convert-and-store-base64-encoded-files-in-laravel-use-validation-rules-and-access-the-decoded-files-from-the-request-instance). ### Testing diff --git a/src/Request/ConvertsBase64ToFiles.php b/src/Request/ConvertsBase64ToFiles.php index d5730e6..e11df5e 100644 --- a/src/Request/ConvertsBase64ToFiles.php +++ b/src/Request/ConvertsBase64ToFiles.php @@ -3,8 +3,11 @@ namespace ProtoneMedia\LaravelMixins\Request; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Str; +use Symfony\Component\HttpFoundation\FileBag; +use Symfony\Component\HttpFoundation\ParameterBag; trait ConvertsBase64ToFiles { @@ -13,6 +16,26 @@ protected function base64FileKeys(): array return []; } + /** + * Helper method to get the body parameters bag. + * + * @return \Symfony\Component\HttpFoundation\ParameterBag + */ + private function bodyParametersBag(): ParameterBag + { + return $this->request; + } + + /** + * Helper method to get the uploaded files bag. + * + * @return FileBag + */ + private function uploadFilesBag(): FileBag + { + return $this->files; + } + /** * Pulls the Base64 contents for each image key and creates * an UploadedFile instance from it and sets it on the @@ -22,7 +45,9 @@ protected function base64FileKeys(): array */ protected function prepareForValidation() { - Collection::make($this->base64FileKeys())->each(function ($filename, $key) { + $flattened = Arr::dot($this->base64FileKeys()); + + Collection::make($flattened)->each(function ($filename, $key) { rescue(function () use ($key, $filename) { $base64Contents = $this->input($key); @@ -48,8 +73,13 @@ protected function prepareForValidation() $uploadedFile = new UploadedFile($tempFilePath, $filename, null, null, true); - $this->request->remove($key); - $this->files->set($key, $uploadedFile); + $body = $this->bodyParametersBag()->all(); + Arr::forget($body, $key); + $this->bodyParametersBag()->replace($body); + + $files = $this->uploadFilesBag()->all(); + Arr::set($files, $key, $uploadedFile); + $this->uploadFilesBag()->replace($files); }, null, false); }); } diff --git a/tests/Request/ConvertsBase64ToFilesTest.php b/tests/Request/ConvertsBase64ToFilesTest.php index 8d69e61..8b932a1 100644 --- a/tests/Request/ConvertsBase64ToFilesTest.php +++ b/tests/Request/ConvertsBase64ToFilesTest.php @@ -3,6 +3,7 @@ namespace ProtoneMedia\Mixins\Tests\Request; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Routing\Redirector; use Orchestra\Testbench\TestCase; use ProtoneMedia\LaravelMixins\Request\ConvertsBase64ToFiles; use ZipArchive; @@ -28,6 +29,30 @@ public function rules() } } +class NestedImageRequest extends FormRequest +{ + use ConvertsBase64ToFiles; + + protected function base64FileKeys(): array + { + return [ + 'company' => [ + 'logo' => 'company_logo.jpeg', + ], + + 'user.avatar' => 'user_avatar.png', + ]; + } + + public function rules() + { + return [ + 'company.logo' => ['required', 'file', 'image'], + 'user.avatar' => ['required', 'file', 'image'], + ]; + } +} + class ZipRequest extends FormRequest { use ConvertsBase64ToFiles; @@ -49,6 +74,13 @@ public function rules() class ConvertsBase64ToFilesTest extends TestCase { + private function validateRequest(FormRequest $request) + { + $request->setContainer($this->app); + $request->setRedirector($this->app->make(Redirector::class)); + $request->validateResolved(); + } + /** @test */ public function it_converts_the_base64_images_to_illuminate_file_uploads() { @@ -57,8 +89,7 @@ public function it_converts_the_base64_images_to_illuminate_file_uploads() 'jpeg_image' => file_get_contents(__DIR__ . '/base64_jpeg'), ]); - $request->setContainer($this->app); - $request->validateResolved(); + $this->validateRequest($request); $pngFile = $request->file('png_image'); $jpegFile = $request->file('jpeg_image'); @@ -81,6 +112,41 @@ public function it_converts_the_base64_images_to_illuminate_file_uploads() $this->assertEquals('width="300" height="300"', $jpegSize[3]); } + /** @test */ + public function it_handles_nested_files() + { + $request = NestedImageRequest::create('/', 'POST', [ + 'company' => [ + 'logo' => file_get_contents(__DIR__ . '/base64_jpeg'), + ], + 'user' => [ + 'avatar' => file_get_contents(__DIR__ . '/base64_png'), + ], + ]); + + $this->validateRequest($request); + + $pngFile = $request->file('user.avatar'); + $jpegFile = $request->file('company.logo'); + + $this->assertNotNull($pngFile); + $this->assertNotNull($jpegFile); + + $this->assertEquals($pngFile, $request->validated()['user']['avatar']); + $this->assertEquals($jpegFile, $request->validated()['company']['logo']); + + // + + $pngSize = getimagesize($pngFile->getRealPath()); + $jpegSize = getimagesize($jpegFile->getRealPath()); + + $this->assertEquals('user_avatar.png', $pngFile->getClientOriginalName()); + $this->assertEquals('company_logo.jpeg', $jpegFile->getClientOriginalName()); + + $this->assertEquals('width="300" height="300"', $pngSize[3]); + $this->assertEquals('width="300" height="300"', $jpegSize[3]); + } + /** @test */ public function it_converts_a_base64_zip_to_an_illuminate_file_upload() { @@ -88,8 +154,7 @@ public function it_converts_a_base64_zip_to_an_illuminate_file_upload() 'zip' => file_get_contents(__DIR__ . '/base64_zip'), ]); - $request->setContainer($this->app); - $request->validateResolved(); + $this->validateRequest($request); $zipFile = $request->file('zip');