diff --git a/src/Illuminate/Validation/Rules/File.php b/src/Illuminate/Validation/Rules/File.php index b3968e035a37..d24e5a9c8139 100644 --- a/src/Illuminate/Validation/Rules/File.php +++ b/src/Illuminate/Validation/Rules/File.php @@ -14,7 +14,46 @@ class File implements Rule, DataAwareRule, ValidatorAwareRule { - use Conditionable, Macroable; + use Conditionable, Macroable { + Macroable::__call as macroCall; + Macroable::__callStatic as macroCallStatic; + } + + /** + * Handle dynamic calls to the object. + * + * @param string $method + * @param array $parameters + * @return mixed + * + * @throws \BadMethodCallException + */ + public function __call($method, $parameters) + { + if ($method === 'types') { + return $this->setTypes(...$parameters); + } + + return $this->macroCall($method, $parameters); + } + + /** + * Handle dynamic, static calls to the object. + * + * @param string $method + * @param array $parameters + * @return mixed + * + * @throws \BadMethodCallException + */ + public static function __callStatic($method, $parameters) + { + if ($method === 'types') { + return (new static())->setTypes(...$parameters); + } + + return static::macroCallStatic($method, $parameters); + } /** * The MIME types that the given file should match. This array may also contain file extensions. @@ -137,12 +176,18 @@ public static function image($allowSvg = false) /** * Limit the uploaded file to the given MIME types or file extensions. * + * This method can be called both statically (as a factory) and on an instance. + * When called statically, a new File instance is created. + * When called on an instance, the current instance is modified and returned. + * * @param string|array $mimetypes - * @return static + * @return $this */ - public static function types($mimetypes) + public function setTypes($mimetypes) { - return tap(new static(), fn ($file) => $file->allowedMimetypes = (array) $mimetypes); + $this->allowedMimetypes = (array) $mimetypes; + + return $this; } /** diff --git a/tests/Validation/ValidationFileRuleTest.php b/tests/Validation/ValidationFileRuleTest.php index adbd7024c792..f289d5c547c0 100644 --- a/tests/Validation/ValidationFileRuleTest.php +++ b/tests/Validation/ValidationFileRuleTest.php @@ -135,6 +135,31 @@ public function testMixOfMimetypesAndMimes() ); } + public function testTypesPreservesChainConfiguration() + { + // When types() is called on an existing instance, it should preserve + // prior configuration like max() instead of creating a new instance. + $this->fails( + File::image()->max(1)->types(['jpg', 'jpeg']), + UploadedFile::fake()->image('photo.jpg', 800, 600), + ['validation.max.file'], + ); + + // When types() is called statically, it should still work as a factory. + $this->fails( + File::types('text/plain'), + UploadedFile::fake()->createWithContent('foo.png', file_get_contents(__DIR__.'/fixtures/image.png')), + ['validation.mimetypes'], + ); + + // When types() is called before max(), the max constraint should also work. + $this->fails( + File::default()->types(['jpg', 'jpeg'])->max(1), + UploadedFile::fake()->image('photo.jpg', 800, 600), + ['validation.max.file'], + ); + } + public function testSingleExtension() { $this->fails(