Add deferred uploads

This commit is contained in:
philipp lang 2024-01-12 00:16:42 +01:00
parent 5c05ab7c80
commit c4eb67c09f
5 changed files with 92 additions and 44 deletions

33
src/DefersUploads.php Normal file
View File

@ -0,0 +1,33 @@
<?php
namespace Zoomyboy\MedialibraryHelper;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
trait DefersUploads
{
public function setDeferredUploads(array $uploads): void
{
$collection = $this->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'));
}
}

View File

@ -23,13 +23,13 @@ class ModelRule implements InvokableRule
app(Factory::class)->make([$attribute => $value], [ app(Factory::class)->make([$attribute => $value], [
"{$attribute}.id" => 'nullable|integer|gt:0', "{$attribute}.id" => 'nullable|integer|gt:0',
"{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())], "{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())],
"{$attribute}.collection" => 'required|string', "{$attribute}.collection_name" => 'required|string',
])->validate(); ])->validate();
$this->model = data_get($value, 'model'); $this->model = data_get($value, 'model');
$this->id = data_get($value, 'id'); $this->id = data_get($value, 'id');
$this->collection = data_get($value, 'collection'); $this->collection = data_get($value, 'collection_name');
if (is_null($this->id)) { if (is_null($this->id)) {
$this->validateDeferred($attribute, $value); $this->validateDeferred($attribute, $value);
@ -38,7 +38,7 @@ class ModelRule implements InvokableRule
$model = app('media-library-helpers')->get($this->model); $model = app('media-library-helpers')->get($this->model);
app(Factory::class)->make([$attribute => $value], [ 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'], "{$attribute}.id" => ['required', 'exists:' . (new $model)->getTable() . ',id'],
])->validate(); ])->validate();
} }
@ -47,19 +47,19 @@ class ModelRule implements InvokableRule
{ {
app(Factory::class)->make([$attribute => $value], [ app(Factory::class)->make([$attribute => $value], [
"{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())], "{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())],
"{$attribute}.collection" => 'required|string', "{$attribute}.collection_name" => 'required|string',
])->validate(); ])->validate();
$model = app('media-library-helpers')->get($this->model); $model = app('media-library-helpers')->get($this->model);
app(Factory::class)->make([$attribute => $value], [ 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(); ])->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 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<HasMedia> * @return class-string<HasMedia>
*/ */
public static function getModelClassName($modelParam): 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 public static function getCollection($modelParam): MediaCollection
{ {
$className = static::getModelClassName($modelParam); $className = static::getModelClassName($modelParam);
return (new $className)->getMediaCollection($modelParam['collection']); return (new $className)->getMediaCollection($modelParam['collection_name']);
} }
} }

View File

@ -2,23 +2,18 @@
namespace Zoomyboy\MedialibraryHelper\Tests\Feature; namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
test('it uploads a deferred file to a collection', function () { test('it uploads a deferred file to a collection', function () {
$this->auth()->registerModel()->withoutExceptionHandling(); $this->auth()->registerModel()->withoutExceptionHandling();
$content = base64_encode($this->pdfFile()->getContent());
$payload = [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
'payload' => [ 'payload' => ['content' => base64_encode($this->pdfFile()->getContent()), 'name' => 'beispiel.pdf'],
'content' => $content, ])
'name' => 'beispiel.pdf',
],
];
$this->postJson('/mediaupload', $payload)
->assertStatus(201) ->assertStatus(201)
->assertJson([ ->assertExactJson([
'is_deferred' => true, 'is_deferred' => true,
'original_url' => Storage::disk('temp')->url('media-library/beispiel.pdf'), 'original_url' => Storage::disk('temp')->url('media-library/beispiel.pdf'),
'name' => 'beispiel', 'name' => 'beispiel',
@ -30,12 +25,30 @@ test('it uploads a deferred file to a collection', function () {
Storage::disk('temp')->assertExists('media-library/beispiel.pdf'); 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 () { test('it uploads multiple files', function () {
$this->auth()->registerModel()->withoutExceptionHandling(); $this->auth()->registerModel()->withoutExceptionHandling();
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$payload = [ $payload = [
'parent' => ['model' => 'post', 'collection' => 'multipleForced', 'id' => null], 'parent' => ['model' => 'post', 'collection_name' => 'multipleForced', 'id' => null],
'payload' => [ 'payload' => [
['content' => $content, 'name' => 'beispiel.pdf'], ['content' => $content, 'name' => 'beispiel.pdf'],
['content' => $content, 'name' => 'beispiel2.pdf'], ['content' => $content, 'name' => 'beispiel2.pdf'],
@ -72,7 +85,7 @@ test('it reduces file size', function () {
$this->auth()->registerModel()->withoutExceptionHandling(); $this->auth()->registerModel()->withoutExceptionHandling();
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
'payload' => [ 'payload' => [
'content' => base64_encode($this->jpgFile()->getContent()), 'content' => base64_encode($this->jpgFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -87,7 +100,7 @@ test('it handles authorization with collection', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -100,7 +113,7 @@ test('it handles authorization with collection correctly', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => null], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -124,9 +137,9 @@ test('it needs a collection', function ($key, $value) {
$this->postJson('/mediaupload', $payload)->assertJsonValidationErrors($key); $this->postJson('/mediaupload', $payload)->assertJsonValidationErrors($key);
})->with(function () { })->with(function () {
yield ['parent.collection', '']; yield ['parent.collection_name', ''];
yield ['parent.collection', -1]; yield ['parent.collection_name', -1];
yield ['parent.collection', 'missingcollection']; yield ['parent.collection_name', 'missingcollection'];
yield ['parent.model', 'lalala']; yield ['parent.model', 'lalala'];
yield ['parent.model', -1]; yield ['parent.model', -1];
yield ['parent.model', '']; yield ['parent.model', ''];

View File

@ -13,7 +13,7 @@ test('it uploads a single file to a single file collection', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', '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()); $content = base64_encode($this->jpgFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -58,7 +58,7 @@ test('it forces a filename for a single collection', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'singleForced', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'singleForced', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -77,7 +77,7 @@ test('it sets custom title when storing', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'singleStoringHook', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'singleStoringHook', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -97,7 +97,7 @@ test('it sets custom properties from properties method', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'multipleProperties', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'multipleProperties', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -117,7 +117,7 @@ test('it forces a filename for multiple collections', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'multipleForced', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'multipleForced', 'id' => $post->id],
'payload' => [ 'payload' => [
[ [
'content' => $content, 'content' => $content,
@ -138,7 +138,7 @@ test('it throws event when file has been uploaded', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'singleWithEvent', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'singleWithEvent', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -158,7 +158,7 @@ test('it throws event when multiple files uploaded', function () {
$content = base64_encode($this->pdfFile()->getContent()); $content = base64_encode($this->pdfFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'multipleFilesWithEvent', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'multipleFilesWithEvent', 'id' => $post->id],
'payload' => [ 'payload' => [
[ [
'content' => $content, 'content' => $content,
@ -183,7 +183,7 @@ test('it uploads multiple files', function () {
$post->addMedia($file->getPathname())->preservingOriginal()->toMediaCollection('images'); $post->addMedia($file->getPathname())->preservingOriginal()->toMediaCollection('images');
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'images', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'images', 'id' => $post->id],
'payload' => [ 'payload' => [
[ [
'content' => base64_encode($file->getContent()), 'content' => base64_encode($file->getContent()),
@ -216,7 +216,7 @@ test('it returns 403 when not authorized', function () {
$post = $this->newPost(); $post = $this->newPost();
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', '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->auth(['storeMedia' => ['id' => $post->id, 'collection' => 'defaultSingleFile']])->registerModel();
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -238,7 +238,7 @@ test('it checks for model when running authorization', function () {
])->assertStatus(201); ])->assertStatus(201);
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $otherPost->id], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $otherPost->id],
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', '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->auth(['storeMedia' => ['id' => $post->id, 'collection' => 'defaultSingleFile']])->registerModel();
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -259,7 +259,7 @@ test('it checks for collection when running authorization', function () {
])->assertStatus(201); ])->assertStatus(201);
$this->postJson('/mediaupload', [ $this->postJson('/mediaupload', [
'parent' => ['model' => 'post', 'collection' => 'singleWithEvent', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'singleWithEvent', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -272,7 +272,7 @@ test('it needs validation for single files', function (array $payloadOverwrites,
$post = $this->newPost(); $post = $this->newPost();
$payload = [ $payload = [
'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -290,7 +290,7 @@ test('it needs validation for single files', function (array $payloadOverwrites,
})->with(function () { })->with(function () {
yield [['parent.model' => 'missingmodel'], 'parent.model']; yield [['parent.model' => 'missingmodel'], 'parent.model'];
yield [['parent.id' => -1], 'parent.id']; 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' => []], 'payload.content'];
yield [['payload.content' => ['UU']], 'payload.content']; yield [['payload.content' => ['UU']], 'payload.content'];
yield [['payload.content' => null], '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(); $post = $this->newPost();
$payload = [ $payload = [
'parent' => ['model' => 'post', 'collection' => 'images', 'id' => $post->id], 'parent' => ['model' => 'post', 'collection_name' => 'images', 'id' => $post->id],
'payload' => [ 'payload' => [
[ [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),

View File

@ -11,10 +11,12 @@ use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Workbench\App\Events\MediaChange; use Workbench\App\Events\MediaChange;
use Workbench\App\Events\MediaDestroyed; use Workbench\App\Events\MediaDestroyed;
use Workbench\App\Events\MediaStored; use Workbench\App\Events\MediaStored;
use Zoomyboy\MedialibraryHelper\DefersUploads;
class Post extends Model implements HasMedia class Post extends Model implements HasMedia
{ {
use InteractsWithMedia; use InteractsWithMedia;
use DefersUploads;
public $guarded = []; public $guarded = [];