title: Symfony 2 Form Validation: missing name of property in validation message
body: <p>I don't understand why some constraints does not insert name of property in error message after validation. I have this entity class:</p>
  
  <pre><code> &lt;?php
  
  
  namespace AC\OperaBundle\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  use AC\UserBundle\Entity\Utente;
  use Symfony\Component\Validator\Constraints as Assert;
  
  
  /**
   * Class Episodio
   * @package AC\OperaBundle\Entity
   * @ORM\Entity(repositoryClass="AC\OperaBundle\Repository\EpisodioRepository")
   * * @ORM\Table(
   *      name="ac_Episodio",
   *      uniqueConstraints={@ORM\UniqueConstraint(name="unique_idx", columns={"opera", "numero_episodio", "extra"})},
   *      indexes={   @ORM\Index(name="opera_idx", columns={"opera"}),
   *                  @ORM\Index(name="numero_episodio_idx", columns={"numero_episodio"}),
   *                  @ORM\Index(name="extra_idx", columns={"extra"}),
   *                  @ORM\Index(name="attivo_idx", columns={"attivo"}) })
   */
  class Episodio {
  
      /**
       * @var int
       *
       * @ORM\Column(name="id", type="integer")
       * @ORM\Id
       * @ORM\GeneratedValue(strategy="AUTO")
       */
      protected $id;
  
      /**
       * @var Opera
       *
       * @ORM\ManyToOne(targetEntity="AC\OperaBundle\Entity\Opera", inversedBy="episodi")
       * @ORM\JoinColumn(name="opera", referencedColumnName="id", nullable=false)
       */
      protected $opera;
  
      /**
       * @var string
       *
       * @ORM\Column(name="numero_episodio", type="string", length=5, nullable=false)
       */
      protected $numero_episodio;
  
      /**
       * @var string
       *
       * @ORM\Column(name="extra", type="string", length=30, nullable=false)
       */
      protected $extra;
  
      /**
       * @var string
       *
       * @ORM\Column(name="titolo_italiano", type="string", length=150, nullable=false)
       *  @Assert\NotBlank()
       */
      protected $titolo_italiano;
  
      /**
       * @var string
       *
       * @ORM\Column(name="titolo_originale", type="string", length=150, nullable=false)
       *  @Assert\NotBlank()
       */
      protected $titolo_originale;
  
      /**
       * @var \DateTime
       *
       * @ORM\Column(name="data_ita", type="date", nullable=true)
       * @Assert\Date()
       */
      protected $data_ita;
  
      /**
       * @var \DateTime
       *
       * @ORM\Column(name="data_jap", type="date", nullable=true)
       * @Assert\Date()
       */
      protected $data_jap;
  
      /**
       * @var int
       *
       * @ORM\Column(name="durata", type="smallint", nullable=false, options={"default" = 0})
       */
      protected $durata;
  
      /**
       * @var string
       *
       * @ORM\Column(name="trama", type="text", nullable=false)
       */
      protected $trama;
  
      /**
       * @var int
       *
       * @ORM\Column(name="ordine", type="smallint", nullable=false, options={"default" = 0})
       */
      protected $ordine;
  
      /**
       * @var int
       *
       * @ORM\Column(name="promosso", type="integer", nullable=false, options={"default" = 0})
       */
      protected $promosso;
  
      /**
       * @var int
       *
       * @ORM\Column(name="rimandato", type="integer", nullable=false, options={"default" = 0})
       */
      protected $rimandato;
  
      /**
       * @var int
       *
       * @ORM\Column(name="bocciato", type="integer", nullable=false, options={"default" = 0})
       */
      protected $bocciato;
  
      /**
       * @var boolean
       *
       * @ORM\Column(name="fansub", type="boolean", nullable=false)
       */
      protected $fansub;
  
      /**
       * @var boolean
       *
       * @ORM\Column(name="attivo", type="boolean", nullable=false)
       */
      protected $attivo;
  
      /**
       * @var \DateTime
       *
       * @ORM\Column(name="data_aggiornamento", type="date")
       */
      protected $data_aggiornamento;
  
      /**
       * @var Utente
       *
       * @ORM\ManyToOne(targetEntity="AC\UserBundle\Entity\Utente")
       * @ORM\JoinColumn(name="utente_aggiornamento", referencedColumnName="id", nullable=true)
       */
      protected $utente_aggiornamento;
  
      /**
       * @param boolean $attivo
       */
      public function setAttivo($attivo)
      {
          $this-&gt;attivo = $attivo;
      }
  
      /**
       * @return boolean
       */
      public function getAttivo()
      {
          return $this-&gt;attivo;
      }
  
      /**
       * @param int $bocciato
       */
      public function setBocciato($bocciato)
      {
          $this-&gt;bocciato = $bocciato;
      }
  
      /**
       * @return int
       */
      public function getBocciato()
      {
          return $this-&gt;bocciato;
      }
  
      /**
       * @param \DateTime $data_aggiornamento
       */
      public function setDataAggiornamento($data_aggiornamento)
      {
          $this-&gt;data_aggiornamento = $data_aggiornamento;
      }
  
      /**
       * @return \DateTime
       */
      public function getDataAggiornamento()
      {
          return $this-&gt;data_aggiornamento;
      }
  
      /**
       * @param \DateTime $data_ita
       */
      public function setDataIta($data_ita)
      {
          $this-&gt;data_ita = $data_ita;
      }
  
      /**
       * @return \DateTime
       */
      public function getDataIta()
      {
          return $this-&gt;data_ita;
      }
  
      /**
       * @param \DateTime $data_jap
       */
      public function setDataJap($data_jap)
      {
          $this-&gt;data_jap = $data_jap;
      }
  
      /**
       * @return \DateTime
       */
      public function getDataJap()
      {
          return $this-&gt;data_jap;
      }
  
      /**
       * @param int $durata
       */
      public function setDurata($durata)
      {
          $this-&gt;durata = $durata;
      }
  
      /**
       * @return int
       */
      public function getDurata()
      {
          return $this-&gt;durata;
      }
  
      /**
       * @param string $extra
       */
      public function setExtra($extra)
      {
          $this-&gt;extra = $extra;
      }
  
      /**
       * @return string
       */
      public function getExtra()
      {
          return $this-&gt;extra;
      }
  
      /**
       * @param boolean $fansub
       */
      public function setFansub($fansub)
      {
          $this-&gt;fansub = $fansub;
      }
  
      /**
       * @return boolean
       */
      public function getFansub()
      {
          return $this-&gt;fansub;
      }
  
      /**
       * @param int $id
       */
      public function setId($id)
      {
          $this-&gt;id = $id;
      }
  
      /**
       * @return int
       */
      public function getId()
      {
          return $this-&gt;id;
      }
  
      /**
       * @param string $numero_episodio
       */
      public function setNumeroEpisodio($numero_episodio)
      {
          $this-&gt;numero_episodio = $numero_episodio;
      }
  
      /**
       * @return string
       */
      public function getNumeroEpisodio()
      {
          return $this-&gt;numero_episodio;
      }
  
      /**
       * @param \AC\OperaBundle\Entity\Opera $opera
       */
      public function setOpera($opera)
      {
          $this-&gt;opera = $opera;
      }
  
      /**
       * @return \AC\OperaBundle\Entity\Opera
       */
      public function getOpera()
      {
          return $this-&gt;opera;
      }
  
      /**
       * @param int $ordine
       */
      public function setOrdine($ordine)
      {
          $this-&gt;ordine = $ordine;
      }
  
      /**
       * @return int
       */
      public function getOrdine()
      {
          return $this-&gt;ordine;
      }
  
      /**
       * @param int $promosso
       */
      public function setPromosso($promosso)
      {
          $this-&gt;promosso = $promosso;
      }
  
      /**
       * @return int
       */
      public function getPromosso()
      {
          return $this-&gt;promosso;
      }
  
      /**
       * @param int $rimandato
       */
      public function setRimandato($rimandato)
      {
          $this-&gt;rimandato = $rimandato;
      }
  
      /**
       * @return int
       */
      public function getRimandato()
      {
          return $this-&gt;rimandato;
      }
  
      /**
       * @param string $titolo_italiano
       */
      public function setTitoloItaliano($titolo_italiano)
      {
          $this-&gt;titolo_italiano = $titolo_italiano;
      }
  
      /**
       * @return string
       */
      public function getTitoloItaliano()
      {
          return $this-&gt;titolo_italiano;
      }
  
      /**
       * @param string $titolo_originale
       */
      public function setTitoloOriginale($titolo_originale)
      {
          $this-&gt;titolo_originale = $titolo_originale;
      }
  
      /**
       * @return string
       */
      public function getTitoloOriginale()
      {
          return $this-&gt;titolo_originale;
      }
  
      /**
       * @param string $trama
       */
      public function setTrama($trama)
      {
          $this-&gt;trama = $trama;
      }
  
      /**
       * @return string
       */
      public function getTrama()
      {
          return $this-&gt;trama;
      }
  
      /**
       * @param \AC\UserBundle\Entity\Utente $utente_aggiornamento
       */
      public function setUtenteAggiornamento($utente_aggiornamento)
      {
          $this-&gt;utente_aggiornamento = $utente_aggiornamento;
      }
  
      /**
       * @return \AC\UserBundle\Entity\Utente
       */
      public function getUtenteAggiornamento()
      {
          return $this-&gt;utente_aggiornamento;
      }
  
  
  }
  </code></pre>
  
  <p>In the controller perform the classi call at the <code>$form-&gt;isValid()</code> method to check validation. If there are errors i call <code>$form-&gt;getErrorsAsString()</code> and this is the result:</p>
  
  <pre><code>ERROR: This value should not be blank.
  ERROR: This value should not be blank.
  numeroEpisodio:
      No errors
  titoloItaliano:
      No errors
  titoloOriginale:
      No errors
  dataIta:
      ERROR: This value is not valid.
  dataJap:
      ERROR: This value is not valid.
  durata:
      No errors
  trama:
      No errors
  extra:
      No errors
  fansub:
      No errors
  </code></pre>
  
  <p>The property that use <code>@Assert\NotBlank()</code> dose not put property name in error message! And for this reason i have the first two error line with e generic:</p>
  
  <pre><code>ERROR: This value should not be blank.
  ERROR: This value should not be blank.
  </code></pre>
  
  <p>I i don't know who is the property that failed the validation. I look in the source code of Symfony Component and i see that for not blank constraint:</p>
  
  <pre><code>/**
   * @author Bernhard Schussek &lt;bschussek@gmail.com&gt;
   *
   * @api
   */
  class NotBlankValidator extends ConstraintValidator
  {
      /**
       * {@inheritdoc}
       */
      public function validate($value, Constraint $constraint)
      {
          if (!$constraint instanceof NotBlank) {
              throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\NotBlank');
          }
  
          if (false === $value || (empty($value) &amp;&amp; '0' != $value)) {
              $this-&gt;context-&gt;addViolation($constraint-&gt;message);
          }
      }
  }
  </code></pre>
  
  <p>And for data constraint:</p>
  
  <pre><code>/**
   * @author Bernhard Schussek &lt;bschussek@gmail.com&gt;
   *
   * @api
   */
  class DateValidator extends ConstraintValidator
  {
      const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/';
  
      /**
       * {@inheritdoc}
       */
      public function validate($value, Constraint $constraint)
      {
          if (!$constraint instanceof Date) {
              throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Date');
          }
  
          if (null === $value || '' === $value || $value instanceof \DateTime) {
              return;
          }
  
          if (!is_scalar($value) &amp;&amp; !(is_object($value) &amp;&amp; method_exists($value, '__toString'))) {
              throw new UnexpectedTypeException($value, 'string');
          }
  
          $value = (string) $value;
  
          if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) {
              $this-&gt;context-&gt;addViolation($constraint-&gt;message, array('{{ value }}' =&gt; $value));
          }
      }
  }
  </code></pre>
  
  <p>The important difference is  <code>$this-&gt;context-&gt;addViolation($constraint-&gt;message);</code> VS <code>$this-&gt;context-&gt;addViolation($constraint-&gt;message, array('{{ value }}' =&gt; $value));</code></p>
  
  <p>Why?</p>
  
  <p><strong>This is core part of controller</strong></p>
  
  <pre><code> if ($request-&gt;getMethod() == 'POST') {
  
              try {
                  $form-&gt;handleRequest($request);
                  if ($form-&gt;isValid()) {
                      /* @var $item Episodio */
                      $item = $form-&gt;getData();
                      $em-&gt;persist($item);
                      $em-&gt;flush();
                      $msg = new Message(true, Message::OK_MESSAGE);
                  } else {
                      $msg = new Message(false, Message::KO_MESSAGE);
                      $errors = Utility::getErrorMessages($form);
                      $msg-&gt;setData($errors);
                  }
              } catch (\Exception $ex) {
                  $msg = new Message(false, $ex-&gt;getMessage());
              }
  
              return new Response($this-&gt;get('jms_serializer')-&gt;serialize($msg, 'json'));
  
          }
  </code></pre>
  
  <p><strong>This is utility class that fetch error from form</strong></p>
  
  <pre><code>class Utility {
  
  
  
  
  static function getErrorMessages(\Symfony\Component\Form\Form $form) {
      $errors = array();
      foreach ($form-&gt;getErrors() as $key =&gt; $error) {
          $template = $error-&gt;getMessageTemplate();
          $parameters = $error-&gt;getMessageParameters();
  
          foreach($parameters as $var =&gt; $value){
              $template = str_replace($var, $value, $template);
          }
  
          $errors[$key] = $template;
      }
      //if ($form-&gt;hasChildren()) {
          foreach ($form-&gt;all() as $child) {
              if (!$child-&gt;isValid()) {
                  $errors[$child-&gt;getName()] =  Utility::getErrorMessages($child);
              }
          }
      //}
      return $errors;
  }
  
  
  
  }
  </code></pre>
  
  <p><strong>Form Class</strong></p>
  
  <pre><code>class EpisodioForm  extends AbstractType {
  
  
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder-&gt;add('numeroEpisodio', 'text');
          $builder-&gt;add('titoloItaliano', 'text',array());
          $builder-&gt;add('titoloOriginale', 'text');
          $builder-&gt;add('dataIta', 'date', array('widget' =&gt; 'single_text', 'format' =&gt; 'dd/MM/yyyy'));
          $builder-&gt;add('dataJap', 'date', array('widget' =&gt; 'single_text', 'format' =&gt; 'dd/MM/yyyy'));
          $builder-&gt;add('durata', 'text');
          $builder-&gt;add('trama', 'textarea');
          $builder-&gt;add('extra', 'text');
          $builder-&gt;add('fansub', 'checkbox');
      }
  
      /**
       * Returns the name of this type.
       *
       * @return string The name of this type
       */
      public function getName()
      {
          return "episodio_form";
      }
  }
  </code></pre>
  
  <p>The version of framework is <strong>Symfony 2.4</strong></p>