> */ 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> */ 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); } }