Custom Your Validator In Symfony

Perhaps , sometimes , you want to create your own validator instead of those existing in symfony like : File , notBlank , NotNull  …

If you have to do this implementation , your are reading the suitable tutorial .

problematic

Assuming we are dealing with an entity that manages the loading of files in several modules of your application, the client has asked you to check that the upload of each file in the application must not be exceeded for example 5 megabytes. This value must be stored in the parameters.yml config file, while you know that there is the constraint symfony file that can do that while the value has to get static in the entity, so it’s not possible. This is where you need to create your custom validator.

requirements

this tutorial requires that you master :

  1. PHP
  2. OOP (oriented object programming)
  3. SYMFONY
  4. information about the validator component of Symfony
the version of symfony used in this tutorial is 2.8

Implementation

Before starting , the code below is up for any custom modification , just focus on the aim of your need and respect the usage of component of symfony framework .

//parameters.yml
max_file_size_upload: 2 // the unit is MEGABYTE

To implement a custom validator , symfony offer you to create a class for the constraint and to other one for the validation of the constraint .

See the official documentation here

So let’s first create the constraint class

<?php

namespace Acme\AppBundleBundle\Service\Validation;


use Symfony\Component\Validator\Constraint;

    /**
     * the Max file size upload constraint
     *
     * @Annotation
     * Class FileSize
     * @package Acme\AppBundle\Service\Validation

     */
    class FileSize extends Constraint
    {
        /**
         * @var string the message error if the file {{size}} uploaded is greater than {{limit}}
         *
         * {{size}} the file upload size
         * {{limit}} max_file_size_upload in the parameters.(env).yml
         *
         * in case of custom the error message, add the maxSizeMessage attribute the the assertion :
         * @example :
         *
         *           maxSizeMessage= "you custom message ({{ size }} ). you custom message  {{ limit }} ."
         *
         */
        public $maxSizeMessage = 'The file is too large ({{ size }} M). Allowed maximum size is {{ limit }} M.';


    }

The validator class of the constraint 

<?php


namespace Acme\AppBundle\Service\Validation;


use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
    /** 
     *
     * Class FileSizeValidator
     * @package Acme\AppBundle\Service\Validation
     */
    class FileSizeValidator extends ConstraintValidator
    {
        const CONVERT_MB_TO_B = "MBTOB";// convert from MB to B
        const CONVERT_B_TO_MB = "BTOMB";// convert from B to MB

        private $_maxFileSizeUpload;


        public function __construct($maxFileSizeUpload)
        {
            $this->_maxFileSizeUpload = $maxFileSizeUpload;
        }

        /**
         * @param mixed $value
         * @param Constraint $constraint
         */
        public function validate($value, Constraint $constraint)
        {

            if (!$constraint instanceof FileSize) {

                throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\FileSize');
            }

            if($value instanceof UploadedFile){

                if($this->factorizeSize($this->_maxFileSizeUpload) < $value->getClientSize()){

                    $this->context->buildViolation($constraint->maxSizeMessage)
                        ->setParameter('{{ size }}', $this->factorizeSize($value->getClientSize(),self::CONVERT_B_TO_MB))
                        ->setParameter('{{ limit }}', $this->_maxFileSizeUpload)
                        ->addViolation();
                }
            }
            return;
        }
        /**
         * @param $size
         * @param string $convert
         * @return float|int
         */
        protected function factorizeSize($size,$convert =self::CONVERT_MB_TO_B){

            $size = intval($size);

            if($convert == self::CONVERT_MB_TO_B){

                return $size*pow(10,6);

            }
            else{

                return intval($size/pow(10,6));

            }


        }




    }

now , you have to make your Validator as Service to inject the value from parameter.yml , may be you have to get it from database or another service , so you have just to inject the suitable service than make this value available in your Validator .

in your service.yml :

  fileSizeValidator.service:
    class: Acme\AppBundle\Service\Validation\FileSizeValidator
    arguments: [%max_file_size_upload%]
    tags:
      - name: validator.constraint_validator
        alias: file_size_correct

Here , i’m using construct injection , while your can use the setter injection with a setter method to modify the value of the property $_maxFileSizeUpload

For more information about setter injection , check the official documentation

The alias : file_size_correct is the value that your method validateBy sould return in the FileSize Class , because your Validator is nowdeclared as service , if not , your constraint can’t find the Validator class . take a look on the official documentation

Here we are , till now , you just have to call the assert FileSize on the property of your entity that manage the upload file .

   /**
     * @MyCustomAssert\FileSize()
     *
     * @var File $file
     */
    protected $file;

don’t forget to use the name space of your constraint :

use Acme\AppBundleBundle\Service\Validation as MyCustomAssert;

I hope that help you .

For people who wanna learn more about the validator component of symfony , visit this link here .

inferom
web developer passionate about new technologies