Symfony. Validar un objeto en función de su versión previa
Cómo validar un objeto mediante el componente Validator de Symfony y teniendo en cuenta su versión anterior
English version: https://medium.com/@ger86/symfony-validate-an-object-based-on-its-previous-version-4b6ca7a85dc6
Hoy os traigo una de esas recetas para Symfony que siempre está bien conocer por si en algún momento nos encontramos con la oportunidad de aplicarla. Básicamente lo que pretendo en este artículo es enseñaros a validar un objeto que estamos persistiendo por medio de Doctrine en función del valor que tenía previamente.
Dicho de otro modo, supongamos que tenemos la entidad:

y una clase MyEntityFormType
que representa el formulario con el cual creamos y modificamos objetos de esta clase:

Lo que buscamos es añadir una validación al campo title
de modo que, en función del valor que tuviese previamente, el nuevo valor sea o no válido. Por ejemplo vamos a trabajar sobre la siguiente validación: la longitud del valor introducido para title
tiene que ser siempre superior a la que tenía el valor anterior. Es decir, si antes title
almacenaba el string Título
, un nuevo valor válido sería Otro Título
pero no así Title
.
Esta receta además nos permitirá familiarizarnos con el componente Validator así como con el concepto de UnitOfWork de Doctrine del cual ya os hablé en este artículo:
por lo que os animo a llegar hasta el final porque creo que es muy interesante.
Así que dicho esto, vamos allá.
Definiendo una constraint
Puesto que Symfony ni el componente Validator nos permiten resolver el caso que hemos planteado con una de las Constraints
de las que disponemos por defecto, lo que tendremos que hacer será crear la nuestra propia.
Así que lo primero que haremos será crear una clase llamada TitleLength
dentro de la carpeta src/Validator/Constraints
:

De esta clase me gustaría resaltar 3 cosas:
- Necesitamos añadir la
Annotation
@Annotation
si queremos emplear estaConstraint
mediante anotaciones en la clase a validar. - La propiedad
$message
puede tener lo que queráis y será el mensaje de error que contendrá el formulario en el caso de que el nuevotitle
no supere la validación. - Y finalmente y la más importante, necesitamos declarar esta
Constraint
como unaCLASS_CONSTRAINT
ya que posteriormente necesitaremos recibir en el validador asociado a estaConstraint
el objeto de la claseMyEntity
y no la propiedadtitle
.
Definiendo un validador
Dicho esto, lo siguiente que haremos será escribir el validador de esta Constraint
. Para ello, crearemos una clase llamada TitleLengthValidator
dentro de la misma donde creamos la constraint.
Symfony es capaz de asociar automáticamente el validador asociado a cada
Constraint
. Para ello, la claseValidator
la nombraremos como “nombre de la constraint + Validator”

Para realizar la validación, lo primero que necesitaremos es el EntityManager
, por lo que lo inyectamos mediante el autowire de Symfony.
Hecho esto, escribiremos el método validate
el cual recibe dos argumentos:
- El valor u objeto a validar
- La
Constraint
que se está validando
Puesto que lo que queremos es comprobar la longitud del nuevo título con la del anterior, necesitaremos recuperar dicho valor anterior. Es aquí donde conocer el concepto de UnitOfWork
de Doctrine cobra importancia, ya que gracias a estas “unidades de trabajo” podremos recuperar el objeto tal y como se encuentra en base de datos.
A ver, a ver, explica mejor eso de la UnitOfWork
Imaginad que queremos editar objetos de la clase MyEntity
. Lo que habremos hecho en nuestro controlador habrá sido recuperarlo por id
mediante el repositorio asociado, crear un formulario de la clase MyEntityFormType
pasándole dicho objeto y asociar ese formulario a la Request
mediante el método handleRequest
:

De este modo, cuando el usuario envía el formulario, el método handleRequest
mappeará los valores enviados al objeto $obj
por lo que ahora contendrá los valores enviados y no los que se encuentran en base de datos.
Es por ello que necesitamos emplear una UnitOfWork
de Doctrine para recuperar los valores anteriores (los que se encuentran en base de datos), ya que si lo intentásemos empleando $myEntityRepo->findOneById(1)
lo que haría Doctrine sería devolvernos una versión “cacheada” con los nuevos valores, pues considerará que dicha referencia la tiene ya cargada en memoria y no es necesario irla a buscar a base de datos.
Sigamos con la clase TitleLengthValidator
Ahora creo que queda más claro por qué necesitamos declarar la constraint como CLASS_CONSTRAINT
, pues lo que necesitamos es recibir el objeto en el validador de cara a poder recuperarlo mediante la UnitOfWork
.
Explicado esto, el resto del código de la clase TitleLengthValidator
es bastante sencillo: comprobaremos que la longitud del nuevo valor supera la del antiguo y si no es así crearemos un error.
Asociar la constraint TitleLength a la entidad
Y ya el último paso será asociar nuestra constraint a la entidad. Emplearé por comodidad anotaciones, para lo cual iremos a src/Entity/MyEntity.php
y añadiremos lo que veis a continuación:

De este modo, siempre que vamos a crear o modificar un objeto de la clase MyEntity
nuestra constraint TitleLength
será comprobada.
¿Quieres recibir más artículos como este?
Suscríbete a nuestra newsletter: