From c4eb67c09f8e310d20ebfdb330b70a265e45b1a4 Mon Sep 17 00:00:00 2001 From: philipp lang Date: Fri, 12 Jan 2024 00:16:42 +0100 Subject: [PATCH] Add deferred uploads --- src/DefersUploads.php | 33 +++++++++++++++++++ src/Rules/ModelRule.php | 18 +++++----- tests/Feature/DeferredUploadTest.php | 49 ++++++++++++++++++---------- tests/Feature/UploadTest.php | 34 +++++++++---------- tests/workbench/app/Models/Post.php | 2 ++ 5 files changed, 92 insertions(+), 44 deletions(-) create mode 100644 src/DefersUploads.php diff --git a/src/DefersUploads.php b/src/DefersUploads.php new file mode 100644 index 0000000..8022175 --- /dev/null +++ b/src/DefersUploads.php @@ -0,0 +1,33 @@ +getMediaCollection($uploads['collection_name']); + $file = new MediaFile($this->storage()->path($uploads['file_name'])); + $file->setBasename($collection->runCallback('forceFileName', $this, $file->getBasename())); + + $adder = $this->addMediaFromDisk('media-library/' . $uploads['file_name'], config('media-library.temp_disk')) + ->usingName($file->getBasename()) + ->usingFileName($file->getFilename()) + ->withCustomProperties($collection->runCallback('withDefaultProperties', $file->getFilename())); + + tap( + $collection->runCallback('storing', $adder, $file->getFilename())->toMediaCollection($collection->name), + fn ($media) => $collection->runCallback('stored', $media) + ); + + $collection->runCallback('after', $this); + } + + protected function storage(): FilesystemAdapter + { + return Storage::disk(config('media-library.temp_disk')); + } +} diff --git a/src/Rules/ModelRule.php b/src/Rules/ModelRule.php index 1080a8b..bc3e7a7 100644 --- a/src/Rules/ModelRule.php +++ b/src/Rules/ModelRule.php @@ -23,13 +23,13 @@ class ModelRule implements InvokableRule app(Factory::class)->make([$attribute => $value], [ "{$attribute}.id" => 'nullable|integer|gt:0', "{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())], - "{$attribute}.collection" => 'required|string', + "{$attribute}.collection_name" => 'required|string', ])->validate(); $this->model = data_get($value, 'model'); $this->id = data_get($value, 'id'); - $this->collection = data_get($value, 'collection'); + $this->collection = data_get($value, 'collection_name'); if (is_null($this->id)) { $this->validateDeferred($attribute, $value); @@ -38,7 +38,7 @@ class ModelRule implements InvokableRule $model = app('media-library-helpers')->get($this->model); app(Factory::class)->make([$attribute => $value], [ - "{$attribute}.collection" => ['required', Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name'))], + "{$attribute}.collection_name" => ['required', Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name'))], "{$attribute}.id" => ['required', 'exists:' . (new $model)->getTable() . ',id'], ])->validate(); } @@ -47,19 +47,19 @@ class ModelRule implements InvokableRule { app(Factory::class)->make([$attribute => $value], [ "{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())], - "{$attribute}.collection" => 'required|string', + "{$attribute}.collection_name" => 'required|string', ])->validate(); $model = app('media-library-helpers')->get($this->model); app(Factory::class)->make([$attribute => $value], [ - "{$attribute}.collection" => ['required', Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name'))], + "{$attribute}.collection_name" => ['required', Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name'))], ])->validate(); } /** - * @param array{?id: int, ?collection: string, ?model: string} $modelParam + * @param array{?id: int, ?collection_name: string, ?model: string} $modelParam */ public static function getModel($modelParam): HasMedia { @@ -69,7 +69,7 @@ class ModelRule implements InvokableRule } /** - * @param array{?id: int, ?collection: string, ?model: string} $modelParam + * @param array{?id: int, ?collection_name: string, ?model: string} $modelParam * @return class-string */ public static function getModelClassName($modelParam): string @@ -78,12 +78,12 @@ class ModelRule implements InvokableRule } /** - * @param array{?id: int, ?collection: string, ?model: string} $modelParam + * @param array{?id: int, ?collection_name: string, ?model: string} $modelParam */ public static function getCollection($modelParam): MediaCollection { $className = static::getModelClassName($modelParam); - return (new $className)->getMediaCollection($modelParam['collection']); + return (new $className)->getMediaCollection($modelParam['collection_name']); } } diff --git a/tests/Feature/DeferredUploadTest.php b/tests/Feature/DeferredUploadTest.php index 9ca8d9b..c6aa72f 100644 --- a/tests/Feature/DeferredUploadTest.php +++ b/tests/Feature/DeferredUploadTest.php @@ -2,23 +2,18 @@ namespace Zoomyboy\MedialibraryHelper\Tests\Feature; +use Carbon\Carbon; use Illuminate\Support\Facades\Storage; test('it uploads a deferred file to a collection', function () { $this->auth()->registerModel()->withoutExceptionHandling(); - $content = base64_encode($this->pdfFile()->getContent()); - $payload = [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], - 'payload' => [ - 'content' => $content, - 'name' => 'beispiel.pdf', - ], - ]; - - $this->postJson('/mediaupload', $payload) + $this->postJson('/mediaupload', [ + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null], + 'payload' => ['content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel.pdf'], + ]) ->assertStatus(201) - ->assertJson([ + ->assertExactJson([ 'is_deferred' => true, 'original_url' => Storage::disk('temp')->url('media-library/beispiel.pdf'), 'name' => 'beispiel', @@ -30,12 +25,30 @@ test('it uploads a deferred file to a collection', function () { Storage::disk('temp')->assertExists('media-library/beispiel.pdf'); }); +test('it stores a file to media library after deferred upload', function () { + Carbon::setTestNow(Carbon::parse('2023-05-06 06:00:00')); + $this->auth()->registerModel()->withoutExceptionHandling(); + Storage::disk('temp')->put('media-library/beispiel.pdf', $this->pdfFile()->getContent()); + + $post = $this->newPost(); + + $post->setDeferredUploads([ + 'file_name' => 'beispiel.pdf', + 'collection_name' => 'singleForced', + ]); + + $media = $post->getMedia('singleForced')->first(); + $this->assertNotNull($media); + $this->assertEquals('beispiel 2023-05-06', $media->name); + Storage::disk('temp')->assertMissing('media-library/beispiel.pdf'); +}); + test('it uploads multiple files', function () { $this->auth()->registerModel()->withoutExceptionHandling(); $content = base64_encode($this->pdfFile()->getContent()); $payload = [ - 'parent' => ['model' => 'post', 'collection' => 'multipleForced', 'id' => null], + 'parent' => ['model' => 'post', 'collection_name' => 'multipleForced', 'id' => null], 'payload' => [ ['content' => $content, 'name' => 'beispiel.pdf'], ['content' => $content, 'name' => 'beispiel2.pdf'], @@ -72,7 +85,7 @@ test('it reduces file size', function () { $this->auth()->registerModel()->withoutExceptionHandling(); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null], 'payload' => [ 'content' => base64_encode($this->jpgFile()->getContent()), 'name' => 'beispiel bild.jpg', @@ -87,7 +100,7 @@ test('it handles authorization with collection', function () { $content = base64_encode($this->pdfFile()->getContent()); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -100,7 +113,7 @@ test('it handles authorization with collection correctly', function () { $content = base64_encode($this->pdfFile()->getContent()); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -124,9 +137,9 @@ test('it needs a collection', function ($key, $value) { $this->postJson('/mediaupload', $payload)->assertJsonValidationErrors($key); })->with(function () { - yield ['parent.collection', '']; - yield ['parent.collection', -1]; - yield ['parent.collection', 'missingcollection']; + yield ['parent.collection_name', '']; + yield ['parent.collection_name', -1]; + yield ['parent.collection_name', 'missingcollection']; yield ['parent.model', 'lalala']; yield ['parent.model', -1]; yield ['parent.model', '']; diff --git a/tests/Feature/UploadTest.php b/tests/Feature/UploadTest.php index 5c4e9a0..0367847 100644 --- a/tests/Feature/UploadTest.php +++ b/tests/Feature/UploadTest.php @@ -13,7 +13,7 @@ test('it uploads a single file to a single file collection', function () { $content = base64_encode($this->pdfFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -40,7 +40,7 @@ test('it uploads a single image to a single file collection', function () { $content = base64_encode($this->jpgFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -58,7 +58,7 @@ test('it forces a filename for a single collection', function () { $content = base64_encode($this->pdfFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'singleForced', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'singleForced', 'id' => $post->id], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -77,7 +77,7 @@ test('it sets custom title when storing', function () { $content = base64_encode($this->pdfFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'singleStoringHook', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'singleStoringHook', 'id' => $post->id], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -97,7 +97,7 @@ test('it sets custom properties from properties method', function () { $content = base64_encode($this->pdfFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'multipleProperties', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'multipleProperties', 'id' => $post->id], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -117,7 +117,7 @@ test('it forces a filename for multiple collections', function () { $content = base64_encode($this->pdfFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'multipleForced', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'multipleForced', 'id' => $post->id], 'payload' => [ [ 'content' => $content, @@ -138,7 +138,7 @@ test('it throws event when file has been uploaded', function () { $content = base64_encode($this->pdfFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'singleWithEvent', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'singleWithEvent', 'id' => $post->id], 'payload' => [ 'content' => $content, 'name' => 'beispiel bild.jpg', @@ -158,7 +158,7 @@ test('it throws event when multiple files uploaded', function () { $content = base64_encode($this->pdfFile()->getContent()); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'multipleFilesWithEvent', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'multipleFilesWithEvent', 'id' => $post->id], 'payload' => [ [ 'content' => $content, @@ -183,7 +183,7 @@ test('it uploads multiple files', function () { $post->addMedia($file->getPathname())->preservingOriginal()->toMediaCollection('images'); $response = $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'images', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'images', 'id' => $post->id], 'payload' => [ [ 'content' => base64_encode($file->getContent()), @@ -216,7 +216,7 @@ test('it returns 403 when not authorized', function () { $post = $this->newPost(); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id], 'payload' => [ 'content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel bild.jpg', @@ -230,7 +230,7 @@ test('it checks for model when running authorization', function () { $this->auth(['storeMedia' => ['id' => $post->id, 'collection' => 'defaultSingleFile']])->registerModel(); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id], 'payload' => [ 'content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel bild.jpg', @@ -238,7 +238,7 @@ test('it checks for model when running authorization', function () { ])->assertStatus(201); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $otherPost->id], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $otherPost->id], 'payload' => [ 'content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel bild.jpg', @@ -251,7 +251,7 @@ test('it checks for collection when running authorization', function () { $this->auth(['storeMedia' => ['id' => $post->id, 'collection' => 'defaultSingleFile']])->registerModel(); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id], 'payload' => [ 'content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel bild.jpg', @@ -259,7 +259,7 @@ test('it checks for collection when running authorization', function () { ])->assertStatus(201); $this->postJson('/mediaupload', [ - 'parent' => ['model' => 'post', 'collection' => 'singleWithEvent', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'singleWithEvent', 'id' => $post->id], 'payload' => [ 'content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel bild.jpg', @@ -272,7 +272,7 @@ test('it needs validation for single files', function (array $payloadOverwrites, $post = $this->newPost(); $payload = [ - 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id], 'payload' => [ 'content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel bild.jpg', @@ -290,7 +290,7 @@ test('it needs validation for single files', function (array $payloadOverwrites, })->with(function () { yield [['parent.model' => 'missingmodel'], 'parent.model']; yield [['parent.id' => -1], 'parent.id']; - yield [['parent.collection' => 'missingcollection'], 'parent.collection']; + yield [['parent.collection_name' => 'missingcollection'], 'parent.collection_name']; yield [['payload.content' => []], 'payload.content']; yield [['payload.content' => ['UU']], 'payload.content']; yield [['payload.content' => null], 'payload.content']; @@ -309,7 +309,7 @@ test('it needs validation for multiple files', function (array $payloadOverwrite $post = $this->newPost(); $payload = [ - 'parent' => ['model' => 'post', 'collection' => 'images', 'id' => $post->id], + 'parent' => ['model' => 'post', 'collection_name' => 'images', 'id' => $post->id], 'payload' => [ [ 'content' => base64_encode($this->pdfFile()->getContent()), diff --git a/tests/workbench/app/Models/Post.php b/tests/workbench/app/Models/Post.php index ab285b7..be93974 100644 --- a/tests/workbench/app/Models/Post.php +++ b/tests/workbench/app/Models/Post.php @@ -11,10 +11,12 @@ use Spatie\MediaLibrary\MediaCollections\Models\Media; use Workbench\App\Events\MediaChange; use Workbench\App\Events\MediaDestroyed; use Workbench\App\Events\MediaStored; +use Zoomyboy\MedialibraryHelper\DefersUploads; class Post extends Model implements HasMedia { use InteractsWithMedia; + use DefersUploads; public $guarded = [];