Add Model rule

This commit is contained in:
philipp lang 2024-01-03 02:12:26 +01:00
parent 79cb5a8f58
commit 22d7841d5a
3 changed files with 110 additions and 159 deletions

View File

@ -5,8 +5,6 @@ namespace Zoomyboy\MedialibraryHelper;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Spatie\Image\Image; use Spatie\Image\Image;
use Spatie\LaravelData\DataCollection; use Spatie\LaravelData\DataCollection;
use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\HasMedia;
@ -14,7 +12,7 @@ use Spatie\MediaLibrary\MediaCollections\Exceptions\InvalidBase64Data;
use Spatie\MediaLibrary\MediaCollections\FileAdder; use Spatie\MediaLibrary\MediaCollections\FileAdder;
use Spatie\MediaLibrary\MediaCollections\MediaCollection; use Spatie\MediaLibrary\MediaCollections\MediaCollection;
use Spatie\MediaLibrary\MediaCollections\Models\Media; use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Symfony\Component\HttpFoundation\File\File; use Zoomyboy\MedialibraryHelper\Rules\ModelRule;
class MediaController class MediaController
{ {
@ -23,12 +21,11 @@ class MediaController
public function store(Request $request) public function store(Request $request)
{ {
$request->validate([ $request->validate([
'model' => ['required', 'string', Rule::in(app('media-library-helpers')->keys())], 'parent' => ['required', new ModelRule()],
'id' => 'required',
]); ]);
$model = $this->validateModel($request); $model = ModelRule::getModel($request->input('parent'));
$collection = $model->getMediaCollection($request->input('collection')); $collection = ModelRule::getCollection($request->input('parent'));
$isSingle = 1 === $collection->collectionSizeLimit; $isSingle = 1 === $collection->collectionSizeLimit;
$this->authorize('storeMedia', [$model, $collection->name]); $this->authorize('storeMedia', [$model, $collection->name]);
@ -116,26 +113,6 @@ class MediaController
return property_exists($collection, $callback); return property_exists($collection, $callback);
} }
protected 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;
}
protected function fileAdderFromData($model, $data, $collection): FileAdder protected function fileAdderFromData($model, $data, $collection): FileAdder
{ {
$maxWidth = $collection->runCallback('maxWidth', 9); $maxWidth = $collection->runCallback('maxWidth', 9);

59
src/Rules/ModelRule.php Normal file
View File

@ -0,0 +1,59 @@
<?php
namespace Zoomyboy\MedialibraryHelper\Rules;
use Illuminate\Contracts\Validation\InvokableRule;
use Spatie\MediaLibrary\HasMedia;
use Illuminate\Validation\Factory;
use Illuminate\Validation\Rule;
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
class ModelRule implements InvokableRule
{
public ?string $collection;
public ?int $id;
public ?string $model;
/**
* @param array{?id: int, ?collection: string, ?model: string} $attribute
*/
public function __invoke($attribute, $value, $fail)
{
app(Factory::class)->make([$attribute => $value], [
"{$attribute}.id" => 'required|integer|gt:0',
"{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())],
"{$attribute}.collection" => 'required|string',
])->validate();
$this->model = data_get($value, 'model');
$this->id = data_get($value, 'id');
$this->collection = data_get($value, 'collection');
$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}.id" => ['required', 'exists:' . (new $model)->getTable() . ',id'],
])->validate();
}
/**
* @param array{?id: int, ?collection: string, ?model: string} $modelParam
*/
public static function getModel($modelParam): HasMedia
{
$model = app('media-library-helpers')->get($modelParam['model']);
return $model::find($modelParam['id']);
}
/**
* @param array{?id: int, ?collection: string, ?model: string} $modelParam
*/
public static function getCollection($modelParam): MediaCollection
{
return static::getModel($modelParam)->getMediaCollection($modelParam['collection']);
}
}

View File

@ -13,9 +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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id],
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -35,88 +33,13 @@ test('it uploads a single file to a single file collection', function () {
$response->assertJsonMissingPath('model_id'); $response->assertJsonMissingPath('model_id');
}); });
test('test validation', function (array $attributes, string $messages) {
$this->auth()->registerModel();
$post = $this->newPost();
$content = base64_encode($this->pdfFile()->getContent());
$this->postJson('/mediaupload', [
'model' => 'post',
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [
'content' => $content,
'name' => 'beispiel bild.jpg',
],
...$attributes
])->assertJsonValidationErrors($messages);
})->with([
'missing collection' => [
['collection' => ''],
'collection'
],
'missing id' => [
['id' => ''],
'id'
],
]);
test('test validation for payload', function () {
$this->auth()->registerModel();
$post = $this->newPost();
$this->postJson('/mediaupload', [
'model' => 'post',
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [
'content' => '',
'name' => 'beispiel bild.jpg',
],
])->assertJsonValidationErrors('payload.content');
});
test('test validation for name', function () {
$this->auth()->registerModel();
$post = $this->newPost();
$content = base64_encode($this->pdfFile()->getContent());
$this->postJson('/mediaupload', [
'model' => 'post',
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [
'content' => $content,
'name' => '',
],
])->assertJsonValidationErrors('payload.name');
});
test('test validation for extension', function () {
$this->auth()->registerModel();
$post = $this->newPost();
$content = base64_encode($this->pdfFile()->getContent());
$this->postJson('/mediaupload', [
'model' => 'post',
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [
'content' => $content,
'name' => 'aaa',
],
])->assertJsonValidationErrors('payload.name');
});
test('it uploads a single image to a single file collection', function () { test('it uploads a single image to a single file collection', function () {
$this->auth()->registerModel(); $this->auth()->registerModel();
$post = $this->newPost(); $post = $this->newPost();
$content = base64_encode($this->jpgFile()->getContent()); $content = base64_encode($this->jpgFile()->getContent());
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id],
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -134,9 +57,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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'singleForced', 'id' => $post->id],
'id' => $post->id,
'collection' => 'singleForced',
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -155,9 +76,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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'singleStoringHook', 'id' => $post->id],
'id' => $post->id,
'collection' => 'singleStoringHook',
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -177,9 +96,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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'multipleProperties', 'id' => $post->id],
'id' => $post->id,
'collection' => 'multipleProperties',
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -199,9 +116,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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'multipleForced', 'id' => $post->id],
'id' => $post->id,
'collection' => 'multipleForced',
'payload' => [ 'payload' => [
[ [
'content' => $content, 'content' => $content,
@ -222,9 +137,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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'singleWithEvent', 'id' => $post->id],
'id' => $post->id,
'collection' => 'singleWithEvent',
'payload' => [ 'payload' => [
'content' => $content, 'content' => $content,
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -244,9 +157,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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'multipleFilesWithEvent', 'id' => $post->id],
'id' => $post->id,
'collection' => 'multipleFilesWithEvent',
'payload' => [ 'payload' => [
[ [
'content' => $content, 'content' => $content,
@ -271,9 +182,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', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'images', 'id' => $post->id],
'id' => $post->id,
'collection' => 'images',
'payload' => [ 'payload' => [
[ [
'content' => base64_encode($file->getContent()), 'content' => base64_encode($file->getContent()),
@ -306,9 +215,7 @@ test('it returns 403 when not authorized', function () {
$post = $this->newPost(); $post = $this->newPost();
$response = $this->postJson('/mediaupload', [ $response = $this->postJson('/mediaupload', [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id],
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
@ -318,63 +225,71 @@ test('it returns 403 when not authorized', function () {
$response->assertStatus(403); $response->assertStatus(403);
}); });
test('it needs validation for single files', function (array $payload, string $invalidFieldName) { test('it needs validation for single files', function (array $payloadOverwrites, string $invalidFieldName) {
$this->auth()->registerModel(); $this->auth()->registerModel();
$post = $this->newPost(); $post = $this->newPost();
$response = $this->postJson('/mediaupload', [ $payload = [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'defaultSingleFile', 'id' => $post->id],
'id' => $post->id,
'collection' => 'defaultSingleFile',
'payload' => [ 'payload' => [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
], ],
...$payload, ];
]);
foreach ($payloadOverwrites as $key => $value) {
data_set($payload, $key, $value);
}
$response = $this->postJson('/mediaupload', $payload);
$response->assertStatus(422); $response->assertStatus(422);
$response->assertJsonValidationErrors($invalidFieldName); $response->assertJsonValidationErrors($invalidFieldName);
})->with(function () { })->with(function () {
yield [['model' => 'missingmodel'], 'model']; yield [['parent.model' => 'missingmodel'], 'parent.model'];
yield [['id' => -1], 'model']; yield [['parent.id' => -1], 'parent.id'];
yield [['collection' => 'missingcollection'], 'collection']; yield [['parent.id' => ''], 'parent.id'];
yield [['payload' => ['name' => 'AAA', 'content' => []]], 'payload.content']; yield [['parent.collection' => 'missingcollection'], 'parent.collection'];
yield [['payload' => ['name' => 'AAA', 'content' => ['UU']]], 'payload.content']; yield [['payload.content' => []], 'payload.content'];
yield [['payload' => ['name' => 'AAA', 'content' => null]], 'payload.content']; yield [['payload.content' => ['UU']], 'payload.content'];
yield [['payload' => ['name' => 'AAA', 'content' => '']], 'payload.content']; yield [['payload.content' => null], 'payload.content'];
yield [['payload' => ['name' => 'AAA', 'content' => 1]], 'payload.content']; yield [['payload.content' => ''], 'payload.content'];
yield [['payload' => ['name' => '', 'content' => 'aaadfdf']], 'payload.name']; yield [['payload.content' => 1], 'payload.content'];
yield [['payload' => ['name' => ['U'], 'content' => 'aaadfdf']], 'payload.name']; yield [['payload.name' => ''], 'payload.name'];
yield [['payload' => ['name' => 1, 'content' => 'aaadfdf']], 'payload.name']; yield [['payload.name' => ['U']], 'payload.name'];
yield [['payload' => ['name' => null, 'content' => 'aaadfdf']], 'payload.name']; yield [['payload.name' => 1], 'payload.name'];
yield [['payload.name' => null], 'payload.name'];
yield [['payload' => 'lalal'], 'payload']; yield [['payload' => 'lalal'], 'payload'];
yield [['payload' => 55], 'payload']; yield [['payload' => 55], 'payload'];
}); });
test('it needs validation for multiple files', function (array $payload, string $invalidFieldName) { test('it needs validation for multiple files', function (array $payloadOverwrites, string $invalidFieldName) {
$this->auth()->registerModel(); $this->auth()->registerModel();
$post = $this->newPost(); $post = $this->newPost();
$response = $this->postJson('/mediaupload', [ $payload = [
'model' => 'post', 'parent' => ['model' => 'post', 'collection' => 'images', 'id' => $post->id],
'id' => $post->id,
'collection' => 'images',
'payload' => [ 'payload' => [
[ [
'content' => base64_encode($this->pdfFile()->getContent()), 'content' => base64_encode($this->pdfFile()->getContent()),
'name' => 'beispiel bild.jpg', 'name' => 'beispiel bild.jpg',
], ],
], ],
...$payload, ];
]);
foreach ($payloadOverwrites as $key => $value) {
data_set($payload, $key, $value);
}
$response = $this->postJson('/mediaupload', $payload);
$response->assertStatus(422); $response->assertStatus(422);
$response->assertJsonValidationErrors($invalidFieldName); $response->assertJsonValidationErrors($invalidFieldName);
})->with(function () { })->with(function () {
yield [['model' => 'missingmodel'], 'model']; yield [['parent.model' => 'missingmodel'], 'parent.model'];
yield [['id' => -1], 'model']; yield [['parent.model' => 'post.missingcollection'], 'parent.model'];
yield [['collection' => 'missingcollection'], 'collection']; yield [['parent.id' => -1], 'parent.id'];
yield [['payload' => 'lalal'], 'payload']; yield [['payload' => 'lalal'], 'payload'];
yield [['payload' => []], 'payload']; yield [['payload' => []], 'payload'];
yield [['payload' => 1], 'payload']; yield [['payload' => 1], 'payload'];