Mohammad Khazaee
2 years ago
34 changed files with 761 additions and 110 deletions
-
2Dockerfile
-
3app/Http/Controllers/Controller.php
-
89app/Http/Controllers/FileController.php
-
15app/Http/Controllers/Traits/FileTrait.php
-
6app/Http/Kernel.php
-
12app/Http/Middleware/BindCollectionModelMiddleware.php
-
31app/Http/Middleware/BindFileModelMiddleware.php
-
2app/Http/Resources/CollectionResource.php
-
1app/Http/Resources/FileResource.php
-
110app/Image/ImageProcessor.php
-
28app/Image/Processor.php
-
10app/Models/Collection.php
-
1app/Models/File.php
-
3app/Providers/EventServiceProvider.php
-
31app/Providers/UuidServiceProvider.php
-
193app/Utilities/Helpers/Image.php
-
3config/app.php
-
13database/factories/BaseFactory.php
-
15database/factories/CollectionFactory.php
-
16database/factories/FileFactory.php
-
1database/migrations/2022_07_27_073858_create_files_table.php
-
3database/migrations/2022_07_27_073906_create_collections_table.php
-
23database/seeders/DatabaseSeeder.php
-
1php.ini
-
BINpublic/image-modified.webp
-
18resources/views/welcome.blade.php
-
2routes/api.php
-
28routes/web.php
-
BINstorage/image-modified.webp
-
0storage/stub/image.png
-
2tests/Base/FactoryMethodsTrait.php
-
8tests/Bootstrap.php
-
138tests/Feature/FileStoreTest.php
-
63tests/Feature/Traits/FileTraits.php
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers\Traits; |
|||
|
|||
trait FileTrait |
|||
{ |
|||
public function isImage($file) |
|||
{ |
|||
if(@is_array(getimagesize($file))){ |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Middleware; |
|||
|
|||
use App\Models\File; |
|||
use Closure; |
|||
use Illuminate\Support\Str; |
|||
use Illuminate\Http\Request; |
|||
|
|||
class BindFileModelMiddleware |
|||
{ |
|||
/** |
|||
* Handle an incoming request. |
|||
* |
|||
* @param \Illuminate\Http\Request $request |
|||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next |
|||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse |
|||
*/ |
|||
public function handle(Request $request, Closure $next) |
|||
{ |
|||
if ($request->route()->action['as'] == 'api.file.store') { |
|||
if(is_null($request->file('file'))){ |
|||
app()->bind('file', function () use ($request) { |
|||
return File::find($request->file); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
return $next($request); |
|||
} |
|||
} |
@ -0,0 +1,110 @@ |
|||
<?php |
|||
|
|||
namespace App\Image; |
|||
|
|||
use Jcupitt\Vips\Image; |
|||
use Jcupitt\Vips\Interpretation; |
|||
|
|||
class ImageProcessor |
|||
{ |
|||
public function brightness($image, float $brightness = 1.0, float $saturation = 1.0, float $hue = 0.0) |
|||
{ |
|||
$oldInterpretation = $image->interpretation; |
|||
$hue %= 360; |
|||
if ($hue < 0) { |
|||
$hue = 360 + $hue; |
|||
} |
|||
if ($image->hasAlpha()) { |
|||
$imageWithoutAlpha = $image->extract_band(0, ['n' => $image->bands - 1]); |
|||
$alpha = $image->extract_band($image->bands - 1, ['n' => 1]); |
|||
return $imageWithoutAlpha |
|||
->colourspace(Interpretation::LCH) |
|||
->linear([$brightness, $saturation, 1.0], [0.0, 0.0, $hue]) |
|||
->colourspace($oldInterpretation) |
|||
->bandjoin($alpha); |
|||
} |
|||
return $image |
|||
->colourspace(Interpretation::LCH) |
|||
->linear([$brightness, $saturation, 1.0], [0.0, 0.0, $hue]) |
|||
->colourspace($oldInterpretation); |
|||
} |
|||
|
|||
public function process(string $filename, string $target, array $options = ["w" => null, "h" => null, "r" => null,"flip" => null , "canv" => null,"rotation" => null],$saveOptions = ['Q' => 100]) |
|||
{ |
|||
if (!isset($options['w']) && !isset($options['h'])) { |
|||
$image = Image::thumbnail($filename, getimagesize($filename)[0]); |
|||
} |
|||
|
|||
if (isset($options['r'])) { |
|||
$rArray = explode(':', $options['r']); |
|||
|
|||
if (isset($options['w']) && !isset($options['h'])) { |
|||
$options['h'] = $options['w'] * $rArray[1]; |
|||
$options['w'] = $options['w'] * $rArray[0]; |
|||
} |
|||
|
|||
|
|||
if (!isset($options['w']) && isset($options['h'])) { |
|||
$options['h'] = $options['h'] * $rArray[1]; |
|||
$options['w'] = $options['h'] * $rArray[0]; |
|||
} |
|||
if (!isset($options['w']) && !isset($options['h'])) { |
|||
$options['h'] = getimagesize($filename)[0] * $rArray[1]; |
|||
$options['w'] = getimagesize($filename)[0] * $rArray[0]; |
|||
} |
|||
} |
|||
|
|||
if (isset($options['w']) && !isset($options['h'])) { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => getimagesize($filename)[1]]); |
|||
} |
|||
|
|||
if (!isset($options['w']) && isset($options['h'])) { |
|||
$image = Image::thumbnail($filename, getimagesize($filename)[0], ['height' => $options['h']]); |
|||
} |
|||
|
|||
|
|||
if (isset($options['w']) && isset($options['h']) && !($options['canv'] == true)) { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $options['h'], 'crop' => 'centre']); |
|||
} |
|||
|
|||
if (isset($options['w']) && isset($options['h']) && $options['canv'] == true) { |
|||
$image = Image::thumbnail($filename, $options['w'], ['height' => $options['h']]); |
|||
$widthH = ($options['h'] - $image->height) / 2; |
|||
$widthW = ($options['w'] - $image->width) / 2; |
|||
$image = $image->embed( |
|||
$widthW, |
|||
$widthH, |
|||
$options['w'], |
|||
$options['h'], |
|||
['extend' => 'background', 'background' => 1024] |
|||
); |
|||
} |
|||
|
|||
if (isset($options['brightness']) || isset($options['saturation']) || isset($options['hue'])) { |
|||
$image = $this->brightness($image, isset($options['brightness']) ? $options['brightness'] : 1.0, isset($options['saturation']) ? $options['saturation'] : 1.0, isset($options['hue']) ? $options['hue'] : 0.0); |
|||
} |
|||
|
|||
if (isset($options['rotation'])) { |
|||
$image = $image->rotate($options['rotation']); |
|||
} |
|||
|
|||
if (isset($options['flip'])) { |
|||
if ($options['flip'] == "h") { |
|||
$image = $image->fliphor(); |
|||
} |
|||
|
|||
if ($options['flip'] == "v") { |
|||
$image = $image->flipver(); |
|||
} |
|||
|
|||
if ($options['flip'] == "hv") { |
|||
$image = $image->fliphor(); |
|||
$image = $image->flipver(); |
|||
} |
|||
} |
|||
|
|||
$image->writeToFile($target,$saveOptions); |
|||
|
|||
return $target; |
|||
} |
|||
} |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace App\Image; |
|||
|
|||
use Jcupitt\Vips\Interpretation; |
|||
|
|||
class Processor { |
|||
public static function brightness($image, float $brightness = 1.0, float $saturation = 1.0, float $hue = 0.0) { |
|||
$oldInterpretation = $image->interpretation; |
|||
$hue %= 360; |
|||
if ($hue < 0) { |
|||
$hue = 360 + $hue; |
|||
} |
|||
if ($image->hasAlpha()) { |
|||
$imageWithoutAlpha = $image->extract_band(0, ['n' => $image->bands - 1]); |
|||
$alpha = $image->extract_band($image->bands - 1, ['n' => 1]); |
|||
return $imageWithoutAlpha |
|||
->colourspace(Interpretation::LCH) |
|||
->linear([$brightness, $saturation, 1.0], [0.0, 0.0, $hue]) |
|||
->colourspace($oldInterpretation) |
|||
->bandjoin($alpha); |
|||
} |
|||
return $image |
|||
->colourspace(Interpretation::LCH) |
|||
->linear([$brightness, $saturation, 1.0], [0.0, 0.0, $hue]) |
|||
->colourspace($oldInterpretation); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
<?php |
|||
|
|||
namespace App\Providers; |
|||
|
|||
use Illuminate\Support\ServiceProvider; |
|||
use Illuminate\Support\Str; |
|||
|
|||
class UuidServiceProvider extends ServiceProvider |
|||
{ |
|||
/** |
|||
* Register services. |
|||
* |
|||
* @return void |
|||
*/ |
|||
public function register() |
|||
{ |
|||
//
|
|||
} |
|||
|
|||
/** |
|||
* Bootstrap services. |
|||
* |
|||
* @return void |
|||
*/ |
|||
public function boot() |
|||
{ |
|||
$this->app->bind('uuid', function () { |
|||
return Str::uuid(); |
|||
}); |
|||
} |
|||
} |
@ -0,0 +1,193 @@ |
|||
<?php |
|||
|
|||
namespace App\Utilities\Helpers; |
|||
|
|||
use Faker\Provider\Base; |
|||
|
|||
class Image extends Base { |
|||
/** |
|||
* @var string |
|||
*/ |
|||
public const BASE_URL = 'https://via.placeholder.com'; |
|||
|
|||
public const FORMAT_JPG = 'jpg'; |
|||
public const FORMAT_JPEG = 'jpeg'; |
|||
public const FORMAT_PNG = 'png'; |
|||
|
|||
/** |
|||
* @var array |
|||
* |
|||
* @deprecated Categories are no longer used as a list in the placeholder API but referenced as string instead |
|||
*/ |
|||
protected static $categories = [ |
|||
'abstract', 'animals', 'business', 'cats', 'city', 'food', 'nightlife', |
|||
'fashion', 'people', 'nature', 'sports', 'technics', 'transport', |
|||
]; |
|||
|
|||
/** |
|||
* Generate the URL that will return a random image |
|||
* |
|||
* Set randomize to false to remove the random GET parameter at the end of the url. |
|||
* |
|||
* @example 'http://via.placeholder.com/640x480.png/CCCCCC?text=well+hi+there' |
|||
* |
|||
* @param int $width |
|||
* @param int $height |
|||
* @param string|null $category |
|||
* @param bool $randomize |
|||
* @param string|null $word |
|||
* @param bool $gray |
|||
* @param string $format |
|||
* |
|||
* @return string |
|||
*/ |
|||
public static function imageUrl( |
|||
$width = 640, |
|||
$height = 480, |
|||
$category = null, |
|||
$randomize = true, |
|||
$word = null, |
|||
$gray = false, |
|||
$format = 'png' |
|||
) { |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
// Validate image format
|
|||
$imageFormats = static::getFormats(); |
|||
|
|||
if (!in_array(strtolower($format), $imageFormats, true)) { |
|||
throw new \InvalidArgumentException(sprintf( |
|||
'Invalid image format "%s". Allowable formats are: %s', |
|||
$format, |
|||
implode(', ', $imageFormats) |
|||
)); |
|||
} |
|||
|
|||
$size = sprintf('%dx%d.%s', $width, $height, $format); |
|||
|
|||
$imageParts = []; |
|||
|
|||
if ($category !== null) { |
|||
$imageParts[] = $category; |
|||
} |
|||
|
|||
if ($word !== null) { |
|||
$imageParts[] = $word; |
|||
} |
|||
|
|||
if ($randomize === true) { |
|||
$imageParts[] = Lorem::word(); |
|||
} |
|||
|
|||
$backgroundColor = $gray === true ? 'CCCCCC' : str_replace('#', '', Color::safeHexColor()); |
|||
|
|||
return sprintf( |
|||
'%s/%s/%s%s', |
|||
self::BASE_URL, |
|||
$size, |
|||
$backgroundColor, |
|||
count($imageParts) > 0 ? '?text=' . urlencode(implode(' ', $imageParts)) : '' |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Download a remote random image to disk and return its location |
|||
* |
|||
* Requires curl, or allow_url_fopen to be on in php.ini. |
|||
* |
|||
* @example '/path/to/dir/13b73edae8443990be1aa8f1a483bc27.png' |
|||
* |
|||
* @return bool|string |
|||
*/ |
|||
public static function image( |
|||
$dir = null, |
|||
$width = 640, |
|||
$height = 480, |
|||
$category = null, |
|||
$fullPath = true, |
|||
$randomize = true, |
|||
$word = null, |
|||
$gray = false, |
|||
$format = 'png' |
|||
) { |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
$dir = null === $dir ? sys_get_temp_dir() : $dir; // GNU/Linux / OS X / Windows compatible
|
|||
// Validate directory path
|
|||
if (!is_dir($dir) || !is_writable($dir)) { |
|||
throw new \InvalidArgumentException(sprintf('Cannot write to directory "%s"', $dir)); |
|||
} |
|||
|
|||
// Generate a random filename. Use the server address so that a file
|
|||
// generated at the same time on a different server won't have a collision.
|
|||
$name = md5(uniqid(empty($_SERVER['SERVER_ADDR']) ? '' : $_SERVER['SERVER_ADDR'], true)); |
|||
$filename = sprintf('%s.%s', $name, $format); |
|||
$filepath = $dir . DIRECTORY_SEPARATOR . $filename; |
|||
|
|||
$url = static::imageUrl($width, $height, $category, $randomize, $word, $gray, $format); |
|||
|
|||
// save file
|
|||
if (function_exists('curl_exec')) { |
|||
// use cURL
|
|||
$fp = fopen($filepath, 'w'); |
|||
$ch = curl_init($url); |
|||
curl_setopt($ch, CURLOPT_FILE, $fp); |
|||
$success = curl_exec($ch) && curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200; |
|||
fclose($fp); |
|||
curl_close($ch); |
|||
|
|||
if (!$success) { |
|||
unlink($filepath); |
|||
|
|||
// could not contact the distant URL or HTTP error - fail silently.
|
|||
return false; |
|||
} |
|||
} elseif (ini_get('allow_url_fopen')) { |
|||
// use remote fopen() via copy()
|
|||
$success = copy($url, $filepath); |
|||
|
|||
if (!$success) { |
|||
// could not contact the distant URL or HTTP error - fail silently.
|
|||
return false; |
|||
} |
|||
} else { |
|||
return new \RuntimeException('The image formatter downloads an image from a remote HTTP server. Therefore, it requires that PHP can request remote hosts, either via cURL or fopen()'); |
|||
} |
|||
|
|||
return $fullPath ? $filepath : $filename; |
|||
} |
|||
|
|||
public static function getFormats(): array |
|||
{ |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
return array_keys(static::getFormatConstants()); |
|||
} |
|||
|
|||
public static function getFormatConstants(): array |
|||
{ |
|||
trigger_deprecation( |
|||
'fakerphp/faker', |
|||
'1.20', |
|||
'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' |
|||
); |
|||
|
|||
return [ |
|||
static::FORMAT_JPG => constant('IMAGETYPE_JPEG'), |
|||
static::FORMAT_JPEG => constant('IMAGETYPE_JPEG'), |
|||
static::FORMAT_PNG => constant('IMAGETYPE_PNG'), |
|||
]; |
|||
} |
|||
} |
@ -1 +0,0 @@ |
|||
ffi.enable = "true" |
Binary file not shown.
Binary file not shown.
Before Width: 423 | Height: 750 | Size: 468 KiB After Width: 423 | Height: 750 | Size: 468 KiB |
@ -0,0 +1,138 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature; |
|||
|
|||
use App\Http\Controllers\Traits\FileTrait; |
|||
use App\Models\Collection; |
|||
use App\Models\File; |
|||
use Illuminate\Foundation\Testing\RefreshDatabase; |
|||
use Illuminate\Foundation\Testing\WithFaker; |
|||
use Illuminate\Http\UploadedFile; |
|||
use Tests\Bootstrap; |
|||
use Tests\TestCase; |
|||
use Illuminate\Support\Str; |
|||
use Tests\Feature\Traits\FileTraits; |
|||
|
|||
class FileStoreTest extends Bootstrap |
|||
{ |
|||
use FileTraits; |
|||
|
|||
public function test_tmp_and_model_id_forbidden() |
|||
{ |
|||
$data = [ |
|||
"file" => UploadedFile::fake()->image('test.png'), |
|||
"alts" => ['1', '2', '3'], |
|||
"description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', |
|||
"public" => 1 |
|||
]; |
|||
$collection = Collection::factory()->create([ |
|||
'tmp_support' => false |
|||
]); |
|||
$response = $this->loginAs('dick')->postJson(route('api.file.store', ['collection_name' => $collection->name]), $data); |
|||
|
|||
$response->assertForbidden(); |
|||
} |
|||
|
|||
public function test_tmp_false_and_collection_is_full_forbidden() |
|||
{ |
|||
$randomCount = rand(2, 10); |
|||
$collection = Collection::factory()->createQuietly([ |
|||
'tmp_support' => false, |
|||
'count' => $randomCount |
|||
]); |
|||
for($i=$randomCount; $i > 0; $i--) { |
|||
$uuid = app()->uuid; |
|||
$this->one(File::class, [ |
|||
'uuid' => $uuid, |
|||
'user_id' => auth()->id(), |
|||
'collection_id' => $collection->id, |
|||
'server_path' => '/' . date('y') . '/' . date('m') . '/' . $uuid . '.' . $collection->ext, |
|||
]); |
|||
} |
|||
|
|||
$data = [ |
|||
"file" => UploadedFile::fake()->image('test.png'), |
|||
"alts" => ['1', '2', '3'], |
|||
"description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', |
|||
"public" => 1 |
|||
]; |
|||
$response = $this->loginAs('dick')->postJson(route('api.file.store', ['collection_name' => $collection->name]), $data); |
|||
$response->assertForbidden(); |
|||
} |
|||
|
|||
public function test_file_is_not_isset_forbidden() |
|||
{ |
|||
|
|||
$collection = Collection::factory()->create([ |
|||
'tmp_support' => true |
|||
]); |
|||
|
|||
$data = [ |
|||
"file" => app()->uuid, |
|||
"alts" => ['1', '2', '3'], |
|||
"description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', |
|||
"public" => 1 |
|||
]; |
|||
|
|||
$response = $this->loginAs('dick')->postJson(route('api.file.store', ['collection_name' => $collection->name]), $data); |
|||
|
|||
$response->assertForbidden(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* @dataProvider storeValidationTestProvider |
|||
*/ |
|||
public function test_store_dynamic_validation_unprocessable($collectionFields, $dataFields) |
|||
{ |
|||
$collection = Collection::factory()->create($collectionFields); |
|||
$response = $this->loginAs('dick')->postJson(route('api.file.store', ['collection_name' => $collection->name]), $dataFields); |
|||
$response->assertUnprocessable(); |
|||
} |
|||
|
|||
// /**
|
|||
// * @testWith
|
|||
// * ['email:gt']
|
|||
// * ['email:gt']
|
|||
// * ['email:gt']
|
|||
// * ['email:gt']
|
|||
// * ['email:gt']
|
|||
// */
|
|||
// public function test_store_static_validation_unprocessable($key)
|
|||
// {
|
|||
// File::factory()->smash($key);
|
|||
// }
|
|||
|
|||
|
|||
public function test_store_dynamic_validation_stored_file_unprocessable() |
|||
{ |
|||
|
|||
$collection = Collection::factory()->create([ |
|||
'alt_required' => false, |
|||
'description_required' => false, |
|||
'tmp_support' => true, |
|||
'max_width' => 2000, |
|||
'max_height' => 2000, |
|||
'min_width' => 1, |
|||
'min_height' => 1, |
|||
'min_file_size' => 0 |
|||
]); |
|||
$uuid = app()->uuid; |
|||
$file = File::factory()->createImage(storage_path('app/' . date('y') . '/' . date('m') . '/' . $uuid . '.' . $collection->ext))->create([ |
|||
'uuid' => $uuid, |
|||
'server_path' => '/' . date('y') . '/' . date('m') . '/', |
|||
'collection_id' => $collection->id |
|||
]); |
|||
|
|||
$data = [ |
|||
"file" => $file->uuid, |
|||
"alts" => ['1', '2', '3'], |
|||
"description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', |
|||
"public" => 1 |
|||
]; |
|||
|
|||
$response = $this->loginAs('dick')->postJson(route('api.file.store', ['collection_name' => $collection->name]), $data); |
|||
|
|||
$response->assertUnprocessable(); |
|||
} |
|||
} |
@ -0,0 +1,63 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature\Traits; |
|||
|
|||
use Illuminate\Http\UploadedFile; |
|||
|
|||
trait FileTraits |
|||
{ |
|||
|
|||
private function defaultArray() |
|||
{ |
|||
return [ |
|||
[ |
|||
'alt_required' => false, |
|||
'description_required' => false, |
|||
'tmp_support' => true, |
|||
'max_width' => 2000, |
|||
'max_height' => 2000, |
|||
'min_width' => 1, |
|||
'min_height' => 1, |
|||
'min_file_size' => 0 |
|||
], |
|||
[ |
|||
"file" => UploadedFile::fake()->image('test.png', 400, 400), |
|||
"public" => 1 |
|||
] |
|||
]; |
|||
} |
|||
|
|||
private function changeDefaultArrayAndReturn($defaultArray, $key, $value): array |
|||
{ |
|||
$defaultArray[$key[0]][$key[1]] = $value; |
|||
return $defaultArray; |
|||
} |
|||
|
|||
public function storeValidationTestProvider() |
|||
{ |
|||
|
|||
$data = [ |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'alt_required'], true), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'description_required'], true), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_width'], 2), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_height'], 2), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_width'], 1500), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_height'], 1500), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_file_size'], 1000), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_file_size'], 0), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'exts'], ['jpg']), |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'mimetypes'], ['image/jpg']), |
|||
]; |
|||
return $data; |
|||
} |
|||
|
|||
|
|||
public function storeValidationWithUuidTestProvider() |
|||
{ |
|||
|
|||
$data = [ |
|||
$this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'alt_required'], true), |
|||
]; |
|||
return $data; |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue