Validaciones Fechas en Symfony 2

symfony.jpg
Solucionex
13
Ene 13

Symfony2 nos da una gran potencia a la hora de validar nuestros formularios. En unos casos este proceso puedes ser más o menos sencillo, en otros, la cosa puede complicarse. Hoy mostraremos con un ejemplo, como realizar una validación genérica para dos fechas de una entidad. Este validador comprobará que la primera de ellas en inferior o igual a la segunda. Si no has visto nada sobre validación de formularios en Symfony2, te aconsejo que comiences visitando este enlace.

Por el contrario, si estás familiarizado con la validación, pero no de un modo avanzado, puedes echar un vistazo a esta otra entrada de la documentación.

En primer lugar crearemos una carpeta llamada Validator en el bundle sobre el que vayamos a trabajar, y dentro de esta otra carpeta llamada Constraint.

Una vez creadas las carpetas, generaremos la clase Constraint, la llamaremos DateLessOrEqual.php, su contenido quedará del siguiente modo:

 

normaliceName($this->property1); } /** * Method getProperty2 * * @return string */ public function getProperty2() { return $this->normaliceName($this->property2); } public function getRequiredOptions() { return array('property1', 'property2'); } public function getTargets() { return self::CLASS_CONSTRAINT; } public function validatedBy() { return get_class($this).'Validator'; } }

En esta clase, definiremos:

  • $message  El mensaje a mostrar en caso de que falle la validación, en nuestro caso hemos incluido placeholders para sustituirlos posteriormente con los valores de las fechas dados.
  • $property1 Propiedad 1, corresponde a la primera fecha que queremos comparar.
  • $property2 Propiedad 2, corresponde a la segunda  fecha que queremos comparar.

El método normaliceName recibirá por parámetros el nombre de un atributo de clase (aquellos con nombres compuestos van separados por guiones bajos), y lo transformará a CamelCase.

A continuación tenemos los métodos getter de las dos propiedades, que devuelve el nombre en camecase de ambas, el método getRequiredOptions  que indicará que que debemos incluir en la validación el nombre de las dos propiedades, que en nuestro caso serán una fecha desde y una fecha hasta del objeto entidad que vamos a validar.

El método getTargets  nos permitirá indicar el objetivo de validación, para nuestro caso, el objetivo será la clase y lo indicamos con:  self::CLASS_CONSTRAINT.

Por último debemos indicar que clase realizará la validación de esta restricción, por regla general, se sigue el patrón: nombre de la restricción, concatenado con Validator.

Ahora veremos como quedaría nuestro validador, esta clase se llamará DateLessOrEqualValidator.php y estará a la misma altura en el directorio que  DateLessOrEqual.php.

Su contenido será el siguiente:

 

namespace Slx\BootstrapBundle\Validator\Constraint; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; class DateLessOrEqualValidator extends ConstraintValidator { public function validate($value, Constraint $constraint) { if(null === $value || '' === $value) { return; } if (!is_string($constraint->property1)) { throw new UnexpectedTypeException($constraint->property1, 'string'); } if (!is_string($constraint->property2)) { throw new UnexpectedTypeException($constraint->property2, 'string'); } // Get the actual value of property1 and property2 in the object $method1 = "get" .$constraint->getProperty1(); $method2 = "get" .$constraint->getProperty2(); $date_from = null; $date_to = null; if(is_callable(array($value, $method1))){ $date_from = $value->$method1(); } if(is_callable(array($value, $method2))){ $date_to = $value->$method2(); } if(!empty($date_from) && !empty($date_to)){ if($date_from->getTimestamp() > $date_to->getTimestamp()){ $this->context->addViolation($constraint->message, array( '%value_property1%' => $date_from->format("d/m/Y"), '%value_property2%' => $date_to->format("d/m/Y") )); } } } }

La clase debe heredar de la clase ConstraintValidator, y debemos implementar el método validate.

El método validate, será el método que realice la validación, para ello recibirá por parámetros dos valores:

$value En este caso, será la clase sobre la que estamos realizando la validación.

$constraint Es la restricción, es la clase que hemos definido previamente, donde están almacenados los nombre de las propiedades y el mensaje de error en caso de fallo de la validación.

Este método en primer lugar comprobará: que recibimos ambos valores de fecha y que son una cadena de texto. A partir de los nombre de las propiedades en camelcase, concatenaremos get, formando así el método getter de la entidad, para obtener el valor de las mismas.

Llamaremos a estos métodos sobre la clase que estamos validando, representada por $value. Para ello comprobaremos que estos métodos se pueden llamar dentro del objeto, y que sus valores son distintos de vacío.

Si todo está bien, comprobaremos que la primera fecha es menor o igual que la segunda, si no es así lanzaremos una violación utilizando el mensaje almacenado en la clase restricción, sustituyendo los placeholders por los valores de las fechas.

 

Para utilizar este validador, incluiremos en la clase correspondiente, la anotación para la validación de la misma, indicando  las fechas que deseamos validar: use Slx\BootstrapBundle\Validator\Constraint as SlxAssert; /** * @ORM\Entity * @ORM\Table(name="documento") * @SlxAssert\DateLessOrEqual(property1="fecha_desde", property2="fecha_hasta") * */

Como podemos ver en primer lugar hemos incluido el espacio de nombres donde se encuentra nuestro validador. Después en la anotación de de la entidad hemos hecho uso del mismo, indicando que las fechas a validar son fecha_desde y fecha_hasta.

 Esperamos que este validador sea de ayuda para aquellos que lo necesiten, podría mejorarse paremetrizando el tipo de comparación entre fechas... pero el objetivo de esta entrada es mostrar un caso menos complejo.