Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.4] Custom upload validation rule does not run #19208

Closed
imbrish opened this issue May 15, 2017 · 6 comments
Closed

[5.4] Custom upload validation rule does not run #19208

imbrish opened this issue May 15, 2017 · 6 comments

Comments

@imbrish
Copy link
Contributor

imbrish commented May 15, 2017

  • Laravel Version: 5.4.19
  • PHP Version: 5.6.4
  • Database Driver & Version: Mysql 5.6.17

Description:

Back on Laravel 5.2 I've created custom upload validator upload:max-size, that checks if file uploaded correctly, and if not and error is either UPLOAD_ERR_INI_SIZE or UPLOAD_ERR_FORM_SIZE reports to the user not only that upload failed, but also that maximum handled filesize is:

ini_get_bytes('upload_max_filesize') ?: ini_get_bytes('post_max_size') ?: PHP_INT_MAX

However due to change below introduced in Laravel 5.3:

if ($value instanceof UploadedFile && ! $value->isValid() &&
     $this->hasRule($attribute, array_merge($this->fileRules, $this->implicitRules))
) {
     return $this->addFailure($attribute, 'uploaded', []);
}

My validation rule is never executed despite being implicit and user gets only default, generic validation.uploaded message.

Here are the validation rules:

'pictures' => 'required|array|min:1',
'pictures.*' => "required|upload:2048|mimes:jpeg,png,gif|dimensions:min_width=$width,min_height=$height",

Can I somehow ensure my custom upload validator gets executed? Or only way is to use two separated validators, one for upload and another for mimes, dimensions etc?

Steps To Reproduce:

Try to validate uploaded file via custom rule, when any of $fileRules or $implicitRules is defined.

@themsaid
Copy link
Member

If the uploaded file failed to upload laravel fails with an uploaded failure since 5.3, your rule won't run if this failure happened, so you're already safe from a failed file upload.

@imbrish
Copy link
Contributor Author

imbrish commented May 15, 2017

@themsaid that's true, however when uploaded file exceeds upload_max_filesize user would be informed only that The :attribute failed to upload., while my custom rule would give more feedback The :attribute may not be greater than :max kilobytes..

Is there a way to disable this default functionality, or I have to live with workarounds?

@themsaid
Copy link
Member

Well at least on 5.4 it's not possible to make changes since it'd be considered breaking

@TomaszKot11
Copy link

Somebody managed to come up with a solution?

@imbrish
Copy link
Contributor Author

imbrish commented Dec 4, 2018

I did. It's very hacky but worked for me on 5.6.

// ValidationServiceProvider.php

namespace App\Providers;

use App\Library\Validation\Validator;
use App\Library\Validation\UploadValidator;
use Illuminate\Validation\ValidationServiceProvider as ServiceProvider;

class ValidationServiceProvider extends ServiceProvider
{
    /**
     * Boot the service provider.
     *
     * @return void
     */
    public function boot()
    {
        $factory = $this->app->make('validator');

        // Extend base validator with improved upload validation.
        // Necessary because since Laravel 5.4 no validators would run if upload fails and
        // atrribute has any of the file or implicit rules, which almost always is the case.
        $factory->resolver(function ($translator, $data, $rules, $messages, $customAttributes) {
            return new Validator($translator, $data, $rules, $messages, $customAttributes);
        });

        // Register implicit custom validator.
        // This validators will always run and when failed prevent other validators from running.
        $factory->extendImplicit('upload', UploadValidator::class);
    }
}
// Validator.php

namespace App\Library\Validation;

use Illuminate\Validation\Validator as BaseValidator;

class Validator extends BaseValidator
{
    /**
     * Determine if the given attribute has a rule in the given set.
     * 
     * Overridden to prevent stopping validation when file upload fails, but our custom
     * upload validator is present in the rules of validated attribute.
     *
     * @param  string  $attribute
     * @param  string|array  $rules
     * @return bool
     */
    public function hasRule($attribute, $rules)
    {
        // When original validator wants to bail from validation of failed upload it checks against merged file and implicit rules.
        // Such a check is performed only in that place. Thus it is safe to return false when custom "upload" rule is present.
        // This will let us cheat validator to continue. It will continue only until "upload" validator fails.
        if ($rules == array_merge($this->fileRules, $this->implicitRules) && ! is_null($this->getRule($attribute, 'Upload'))) {
            return false;
        }

        return ! is_null($this->getRule($attribute, $rules));
    }
}

@Gildo-Sargi-Neto
Copy link

@themsaid that's true, however when uploaded file exceeds upload_max_filesize user would be informed only that The :attribute failed to upload., while my custom rule would give more feedback The :attribute may not be greater than :max kilobytes..

Is there a way to disable this default functionality, or I have to live with workarounds?

I lost a couple hours searching for this yesterday and it was kinda obvious, but i was not finding it, so there it is :)

At least in laravel 5.8(i dont know if this works in lower versions) there is a validation.php file with all default error messages located in resources/lang/{language} you can add the custom message there like below

'uploaded' => 'The :attribute may not be greater than :max kilobytes.',

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants