290 lines
8.7 KiB
PHP
290 lines
8.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Traits\Requests;
|
|
|
|
// phpcs:ignore SlevomatCodingStandard.Namespaces.UnusedUses.UnusedUse
|
|
use Illuminate\Filesystem\FilesystemAdapter;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
/**
|
|
* ! This assumes file system is using local storage
|
|
*
|
|
* @mixin \Illuminate\Foundation\Http\FormRequest
|
|
*/
|
|
trait HasPicture
|
|
{
|
|
/**
|
|
* Minimum width
|
|
*
|
|
* @var int
|
|
*/
|
|
private $minWith = 400;
|
|
|
|
/**
|
|
* Maximum width
|
|
*
|
|
* @var int
|
|
*/
|
|
private $maxWith = 1800;
|
|
|
|
/**
|
|
* Minimum height
|
|
*
|
|
* @var int
|
|
*/
|
|
private $minHeight = 300;
|
|
|
|
/**
|
|
* Maximum height
|
|
*
|
|
* @var int
|
|
*/
|
|
private $maxHeight = 1800;
|
|
|
|
/**
|
|
* Maximum weight
|
|
*
|
|
* @var int
|
|
*/
|
|
private $maxWeight = 2048; // In Kb
|
|
|
|
/**
|
|
* Image rules
|
|
*
|
|
* @return array<string,string|array<string|callable(string, mixed,callable(string): void):void>>
|
|
*/
|
|
public function pictureRules(
|
|
string $name = 'picture',
|
|
?int $minWith = null,
|
|
?int $minHeight = null,
|
|
?int $maxWith = null,
|
|
?int $maxHeight = null,
|
|
bool $nullable = false
|
|
): array {
|
|
$sizes = (object) [
|
|
'minWith' => $minWith ?? $this->minWith,
|
|
'minHeight' => $minHeight ?? $this->minHeight,
|
|
'maxWith' => $maxWith ?? $this->maxWith,
|
|
'maxHeight' => $maxHeight ?? $this->maxHeight,
|
|
];
|
|
|
|
return [
|
|
$name => [
|
|
! $nullable ? 'required' : 'nullable',
|
|
function (string $attribute, $value, callable $fail) use ($sizes): void {
|
|
/** @var UploadedFile|string $value */
|
|
$valuePath = is_string($value) ? $value : $value->getPathname();
|
|
$this->validatePicture($attribute, $valuePath, $fail, $sizes);
|
|
},
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Images rules
|
|
*
|
|
* @return array<string,string|array<string|callable(string, mixed,callable(string): void):void>>
|
|
*/
|
|
public function picturesRules(
|
|
string $name = 'pictures',
|
|
?int $minWith = null,
|
|
?int $minHeight = null,
|
|
?int $maxWith = null,
|
|
?int $maxHeight = null
|
|
): array {
|
|
$sizes = (object) [
|
|
'minWith' => $minWith ?? $this->minWith,
|
|
'minHeight' => $minHeight ?? $this->minHeight,
|
|
'maxWith' => $maxWith ?? $this->maxWith,
|
|
'maxHeight' => $maxHeight ?? $this->maxHeight,
|
|
];
|
|
|
|
return [
|
|
$name => 'sometimes|array',
|
|
"{$name}Validate" => "required_with:{$name}|array",
|
|
"{$name}Validate.*" => [
|
|
'required',
|
|
function (string $attribute, $value, callable $fail) use ($sizes): void {
|
|
/** @var UploadedFile|string $value */
|
|
$valuePath = is_string($value) ? $value : $value->getPathname();
|
|
$this->validatePicture($attribute, $valuePath, $fail, $sizes);
|
|
},
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Validate picture
|
|
*
|
|
* @param callable(string):void $fail
|
|
* @param object{minWith:int,minHeight:int,maxWith:int,maxHeight:int} $sizes
|
|
*
|
|
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
|
*/
|
|
private function validatePicture(string $_attribute, string $value, callable $fail, object $sizes): void
|
|
{
|
|
unset($_attribute);
|
|
if (! ($useStorage = Storage::exists($value)) && ! File::exists($value)) {
|
|
$fail(trans('validation.custom.has_picture.file_does_not_exist'));
|
|
|
|
return;
|
|
}
|
|
/** @var string */
|
|
$fileFullPath = $this->getUploadedFileFullPath($value, $useStorage);
|
|
if (
|
|
! \in_array($this->getUploadedFileMimeType($value, $useStorage), [
|
|
'image/jpeg',
|
|
'image/jpg',
|
|
'image/png',
|
|
'image/gif',
|
|
])
|
|
) {
|
|
$fail(trans('validation.mimetypes', [
|
|
'values' => 'image/jpeg, image/jpg, image/png, image/gif',
|
|
]));
|
|
}
|
|
if (! is_readable($fileFullPath)) {
|
|
$fail(trans('validation.custom.has_picture.file_not_readable'));
|
|
|
|
return;
|
|
}
|
|
|
|
if (! ($dimensions = \getimagesize($fileFullPath))) {
|
|
$fail(trans('validation.dimensions'));
|
|
|
|
return;
|
|
}
|
|
$width = intval($dimensions[0]);
|
|
$height = intval($dimensions[1]);
|
|
|
|
$xMessage = trans('validation.custom.has_picture.width_between', [
|
|
'min' => "{$sizes->minWith}px",
|
|
'max' => "{$sizes->maxWith}px",
|
|
]);
|
|
if ($sizes->minWith === $sizes->maxWith) {
|
|
$xMessage = trans('validation.custom.has_picture.width_equal', [
|
|
'value' => "{$sizes->minWith}px",
|
|
]);
|
|
}
|
|
$yMessage = trans(
|
|
'validation.custom.has_picture.height_between',
|
|
[
|
|
'min' => "{$sizes->minHeight}px",
|
|
'max' => "{$sizes->maxHeight}px",
|
|
]
|
|
);
|
|
if ($sizes->minHeight === $sizes->maxHeight) {
|
|
$yMessage = trans('validation.custom.has_picture.height_equal', [
|
|
'value' => "{$sizes->minHeight}px",
|
|
]);
|
|
}
|
|
|
|
if (($width < $sizes->minWith) || ($width > $sizes->maxWith)) {
|
|
$fail($xMessage);
|
|
}
|
|
if (($height < $sizes->minHeight) || ($height > $sizes->maxHeight)) {
|
|
$fail($yMessage);
|
|
}
|
|
if (\filesize($fileFullPath) > ($this->maxWeight * 1024)) {
|
|
$fail(trans('validation.custom.has_picture.weight_max', [
|
|
'value' => $this->maxWeight,
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merges products pictures fields with request data
|
|
*/
|
|
protected function mergePicture(string $name = 'picture'): void
|
|
{
|
|
/** @var UploadedFile|string|null */
|
|
$picturePath = $this->{$name};
|
|
if (is_string($picturePath) || $picturePath instanceof UploadedFile) {
|
|
$this->merge(["{$name}Validate" => []]);
|
|
// ! Empty picture.
|
|
if ($picturePath === 'false') {
|
|
// * Empty picture and exit
|
|
$this->merge([$name => []]);
|
|
|
|
return;
|
|
}
|
|
$decodedPath = \urldecode(is_string($picturePath) ? $picturePath : $picturePath->getPathname());
|
|
|
|
$this->merge([
|
|
"{$name}Validate" => array_merge(
|
|
$this->{"{$name}Validate"},
|
|
[\public_path(ltrim($decodedPath, '/'))]
|
|
),
|
|
]);
|
|
$changedPicture = ltrim($decodedPath, '/');
|
|
$this->merge([
|
|
$name => $changedPicture,
|
|
]);
|
|
} // End if.
|
|
}
|
|
|
|
/**
|
|
* Merges products pictures fields with request data
|
|
*/
|
|
protected function mergePictures(string $name = 'pictures'): void
|
|
{
|
|
// Allows to empty pictures.
|
|
$pictures = $this->array($name);
|
|
$this->merge([
|
|
$name => $pictures,
|
|
]);
|
|
$this->merge(["{$name}Validate" => []]);
|
|
foreach ($pictures as $key => $picturePath) {
|
|
// ! Empty picture.
|
|
if ($picturePath === 'false') {
|
|
// * Empty picture and exit
|
|
$this->merge([$name => []]);
|
|
|
|
return;
|
|
}
|
|
$decodedPath = \urldecode((! is_string($picturePath) && $picturePath) ? $picturePath->getPathname() : $picturePath);
|
|
$this->merge([
|
|
"{$name}Validate" => array_merge(
|
|
$this->{"{$name}Validate"},
|
|
[is_string($picturePath) ? \public_path(ltrim($decodedPath, '/')) : $picturePath]
|
|
),
|
|
]);
|
|
$changedPictures = $this->{$name};
|
|
if (is_string($picturePath)) {
|
|
$changedPictures[$key] = ltrim($decodedPath, '/');
|
|
}
|
|
$this->merge([
|
|
$name => $changedPictures,
|
|
]);
|
|
} // End foreach.
|
|
}
|
|
|
|
/**
|
|
* Get the file full path
|
|
*/
|
|
private static function getUploadedFileFullPath(string $value, bool $useStorage): string
|
|
{
|
|
/** @var FilesystemAdapter */
|
|
$disk = Storage::disk();
|
|
|
|
if (! $useStorage) {
|
|
return realpath($value) ?: $value;
|
|
}
|
|
|
|
return $disk->path($value);
|
|
}
|
|
|
|
/**
|
|
* Get the file mimetype
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private function getUploadedFileMimeType(string $value, bool $useStorage)
|
|
{
|
|
return ! $useStorage ? File::mimeType($value) : Storage::mimeType($value);
|
|
}
|
|
}
|