diff --git a/src/CollectionExtension.php b/src/CollectionExtension.php new file mode 100644 index 0000000..58c828a --- /dev/null +++ b/src/CollectionExtension.php @@ -0,0 +1,61 @@ + $this->registerCustomCallback('forceFileName', $callback); + } + + public function storing() + { + return fn ($callback) => $this->registerCustomCallback('storing', $callback); + } + + public function stored() + { + return fn ($callback) => $this->registerCustomCallback('stored', $callback); + } + + public function runCallback() + { + return function (string $callback, ...$parameters) { + $this->setDefaultCustomCallbacks(); + + return call_user_func($this->customCallbacks->get($callback), ...$parameters); + }; + } + + public function registerCustomCallback() + { + return function (string $name, callable $callback) { + $this->setDefaultCustomCallbacks(); + $this->customCallbacks->put($name, $callback); + + return $this; + }; + } + + public function setDefaultCustomCallbacks() + { + return function () { + if (property_exists($this, 'customCallbacks')) { + return; + } + $this->customCallbacks = collect([ + 'forceFileName' => fn ($name) => $name, + 'stored' => fn ($event) => true, + 'storing' => fn ($adder, $name) => $adder, + ]); + }; + } + }); + } +} diff --git a/src/MediaController.php b/src/MediaController.php index 75aac61..f2eb83e 100644 --- a/src/MediaController.php +++ b/src/MediaController.php @@ -6,16 +6,36 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Str; use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; use Spatie\MediaLibrary\HasMedia; +use Spatie\MediaLibrary\MediaCollections\MediaCollection; use Spatie\MediaLibrary\MediaCollections\Models\Media; class MediaController { use AuthorizesRequests; + private function validateModel(Request $request): HasMedia + { + $model = app('media-library-helpers')->get($request->input('model')); + + $request->validate([ + 'collection' => [ + 'required', + 'string', + Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name')) + ], + ]); + + $model = $model::find($request->input('id')); + if (!$model) { + throw ValidationException::withMessages(['model' => 'nicht gefunden']); + } + + return $model; + } + public function store(Request $request) { $request->validate([ @@ -30,8 +50,9 @@ class MediaController $request->validate($isSingle ? [ 'payload' => 'array', - 'payload.name' => 'string|max:255', - 'payload.content' => 'string', + 'payload.*' => '', + 'payload.name' => 'required|string|max:255', + 'payload.content' => 'required|string', ] : [ 'payload' => 'required|array|min:1', 'payload.*' => 'array', @@ -41,30 +62,19 @@ class MediaController $content = $isSingle ? [$request->input('payload')] : $request->input('payload'); - $medias = collect([]); - foreach ($content as $c) { - if (property_exists($collection, 'forceFileRenamer')) { - $fileRenamer = $collection->forceFileRenamer; - $path = $fileRenamer($model, pathinfo($c['name'], PATHINFO_FILENAME)).'.'.pathinfo($c['name'], PATHINFO_EXTENSION); - } else { - $path = $c['name']; - } + $medias = collect($content)->map(function($c) use ($collection, $model) { + $pathinfo = pathinfo($c['name']); + $path = $collection->runCallback('forceFileName', $pathinfo['filename']).'.'.$pathinfo['extension']; Storage::disk('public')->put($path, base64_decode($c['content'])); - $adder = $model->addMedia(Storage::disk('public')->path($path)); - if (property_exists($collection, 'storingCallback')) { - $callback = $collection->storingCallback; - $adder = $callback($adder, $path); - } - $media = $adder->toMediaCollection($collection->name); - $medias->push($media); - if ($isSingle) { - return MediaData::fromMedia($media); - } - } + return tap( + $collection->runCallback('storing', $adder, $path)->toMediaCollection($collection->name), + fn ($media) => $collection->runCallback('stored', $media) + ); + }); - return MediaData::collection($medias); + return $isSingle ? MediaData::from($medias->first()) : MediaData::collection($medias); } public function index(Request $request, $parentModel, int $parentId, string $collection): JsonResponse @@ -86,18 +96,9 @@ class MediaController return response()->json([]); } - private function validateModel(Request $request): HasMedia + protected function hasCallback(MediaCollection $collection, string $callback): bool { - $model = app('media-library-helpers')->get($request->input('model')); - - $request->validate([ - 'collection' => ['required', 'string', Rule::in((new $model())->getRegisteredMediaCollections()->map(fn ($collection) => $collection->name)->toArray())], - ]); - - if (!$model::find($request->input('id'))) { - throw ValidationException::withMessages(['model' => 'nicht gefunden']); - } - - return $model::find($request->input('id')); + return property_exists($collection, $callback); } + } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index caf30fe..62ab8df 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -11,6 +11,7 @@ class ServiceProvider extends BaseServiceProvider public function register(): void { app()->bind('media-library-helpers', fn () => collect([])); + app()->singleton(CollectionExtension::class, fn () => new CollectionExtension()); } public function boot(): void @@ -21,17 +22,8 @@ class ServiceProvider extends BaseServiceProvider $router->get('mediaupload/{parent_model}/{parent_id}/{collection}', [MediaController::class, 'index'])->name('media.index'); }); - MediaCollection::macro('forceFileName', function ($callback) { - $this->forceFileRenamer = $callback; + app(CollectionExtension::class)->boot(); - return $this; - }); - - MediaCollection::macro('storing', function ($callback) { - $this->storingCallback = $callback; - - return $this; - }); } /** diff --git a/tests/Events/MediaStored.php b/tests/Events/MediaStored.php new file mode 100644 index 0000000..9667d85 --- /dev/null +++ b/tests/Events/MediaStored.php @@ -0,0 +1,22 @@ +auth()->registerModel(); @@ -98,6 +100,55 @@ test('it forces a filename for multiple collections', function() { $this->assertEquals('beispiel-bild-2023-04-04.jpg', $post->getFirstMedia('multipleForced')->file_name); }); +test('it throws event when file has been uploaded', function() { + Event::fake(); + Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00')); + $this->auth()->registerModel()->withoutExceptionHandling(); + $post = $this->newPost(); + $content = base64_encode($this->pdfFile()->getContent()); + + $response = $this->postJson('/mediaupload', [ + 'model' => 'post', + 'id' => $post->id, + 'collection' => 'singleWithEvent', + 'payload' => [ + 'content' => $content, + 'name' => 'beispiel bild.jpg', + ] + ]); + + $response->assertStatus(201); + Event::assertDispatched(MediaStored::class, fn($event) => $event->media->id === $response->json('id')); +}); + +test('it throws event when multiple files uploaded', function() { + Event::fake(); + Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00')); + $this->auth()->registerModel()->withoutExceptionHandling(); + $post = $this->newPost(); + $content = base64_encode($this->pdfFile()->getContent()); + + $response = $this->postJson('/mediaupload', [ + 'model' => 'post', + 'id' => $post->id, + 'collection' => 'multipleFilesWithEvent', + 'payload' => [ + [ + 'content' => $content, + 'name' => 'beispiel bild.jpg', + ], + [ + 'content' => $content, + 'name' => 'beispiel bild 1.jpg', + ], + ] + ]); + + $response->assertStatus(201); + Event::assertDispatched(MediaStored::class, fn($event) => $event->media->id === $response->json('0.id')); + Event::assertDispatched(MediaStored::class, fn($event) => $event->media->id === $response->json('1.id')); +}); + test('it uploads multiple files', function() { $this->auth()->registerModel(); $post = $this->newPost(); diff --git a/tests/Models/Post.php b/tests/Models/Post.php index bac5d9a..343826c 100644 --- a/tests/Models/Post.php +++ b/tests/Models/Post.php @@ -3,8 +3,11 @@ namespace Zoomyboy\MedialibraryHelper\Tests\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Event; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; +use Spatie\MediaLibrary\MediaCollections\Models\Media; +use Zoomyboy\MedialibraryHelper\Tests\Events\MediaStored; class Post extends Model implements HasMedia { @@ -19,11 +22,11 @@ class Post extends Model implements HasMedia $this->addMediaCollection('images'); - $this->addMediaCollection('singleForced')->singleFile()->forceFileName(function($adder, $name) { + $this->addMediaCollection('singleForced')->singleFile()->forceFileName(function($name) { return $name.' '.now()->format('Y-m-d'); }); - $this->addMediaCollection('multipleForced')->forceFileName(function($adder, $name) { + $this->addMediaCollection('multipleForced')->forceFileName(function($name) { return $name.' '.now()->format('Y-m-d'); }); @@ -33,6 +36,14 @@ class Post extends Model implements HasMedia 'ttt' => pathinfo($fileName, PATHINFO_FILENAME), ]); }); + + $this->addMediaCollection('singleWithEvent')->singleFile()->stored(function(Media $media) { + Event::dispatch(new MediaStored($media)); + }); + + $this->addMediaCollection('multipleFilesWithEvent')->stored(function(Media $media) { + Event::dispatch(new MediaStored($media)); + }); } }