Search code examples
phpvalidationlaravellaravel-5laravel-validation

Unique validation of a substring in Laravel


I am storing filenames + their extensions under filename column in the files table. My problem is, that since only the name is present in the $request object without it's corresponding extension, I can't validate the filename using unique validation rule without tinkering with the input data first. Example:

// . . .
$this->validate($request, [
    //   Suppose the name of uploaded file is 'file'.
    // The below rule will NEVER fail, because in the 
    // database, similar file will be stored as 'file.txt',
    // thus 'file' != 'file.txt'
    'filename' => 'unique:files'
]);
// . . .

Is there a way to validate the filename ignoring it's suffix (extension) in the database?


Solution

  • You can try to override the all() method in your Request class and append your extension before validation instead of after. That would be something like this

    public function all() {
        $data = parent::all();           // Get all the data in your request
        $data['filename'] .=  '.txt';    // Concatenate the file extension
    
        return $data;           // DONT FORGET TO RETURN THE CHANGED DATA
    }
    

    Now, your rule will work properly because it will search for the file with the extension. Reminder: You'll need to stop appending the extension in your Controller or whichever place you use to do so, otherwise you'll end up with filename.txt.txt and will be back to square 1.

    Personally, I find it a little messy to override the all() method whenever I feel like it, so I have the following trait

    trait SanitizeRequest {
    
        protected $sanitized = false;
    
        public function all() {
            return $this->sanitize(parent::all());
        }
    
        protected function sanitize(array $inputs) {
            if ($this->sanitized) return $inputs;
    
            foreach ($inputs as $field => $value) {
                if (method_exists($this, $field))
                    $inputs[$field] = $this->$field($value);
            }
            $this->replace($inputs);
            $this->sanitized = true;
            return $inputs;
        }
    
    }
    

    This trait allows me to write a custom method with the field name whenever I want to sanitize it before validation. Using this approach allows you to have a method like this

    class YourRequest extends Request {
    
        use SanitizeRequest;
    
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize() {
            return true;
        }
    
        ...
    
       protected function filename($value) {
           return $value . '.txt';
       }
    
    }