2015-11-16 3 views
1

Je travaille sur un collection form qui est appelé objectifs, un utilisateur peut ajouter autant de buts qu'ils veulent, cette partie fonctionne très bien, je suis capable d'afficher/ajouter/modifier/supprimer des objectifs bienSymfony incapable de valider le formulaire de collecte

enter image description here

problème que j'ai est de savoir comment valider les données. Sur un formulaire, il y a un champ goal target (entier) et un champ saved to date (entier).

La règle est la valeur de saved to date ne peut pas être plus goal target et pour cela, j'ai créé le custom validation et cette classe est en cours Cueilli un formulaire est soumis.

SavedToDate.php

namespace MyBundle\Validator\Constraints; 
use Symfony\Component\Validator\Constraint; 
class SavedToDate extends Constraint 
{ 
    public $message = '"%string%" Saved to date cannot be greater than target date.'; 
} 

SavedToDateValidator.php

namespace MyBundle\Validator\Constraints; 

use Symfony\Component\Validator\Constraint; 
use Symfony\Component\Validator\ConstraintValidator; 

class SavedToDateValidator extends ConstraintValidator 
{ 
    public function validate($value, Constraint $constraint) 
    { 
     $values = $this->context->getRoot()->getdata()->getGoals()->getValues(); 
     foreach($values as $item){ 
      $target = $item->getTarget(); 
      $savedToDate = $item->getReached(); 
      if ($savedToDate > $target) { 
       $this->context->buildViolation($constraint->message) 
        ->setParameter('%string%', $value) 
        ->addViolation(); 
      } 
     } 
    } 

    public function getTargets() 
    { 
     return self::CLASS_CONSTRAINT; 
    } 
} 

À la lecture de la documentation symfony il semble que je dois ajouter la contrainte Valid que j'ai à l'intérieur validation.yml.

goals: 
    - Valid: 

Problème 1

Supposons que quand je rentre saved to date qui est supérieure à goal target contre le premier but, au lieu d'obtenir l'erreur que contre cet objectif que je reçois l'erreur contre les deux objectifs .

NOTE La deuxième erreur ne devrait pas être là comme 8000 est inférieur à 20000 enter image description here

Problème 2

On suppose contre les deux objectifs que je donne saved to date supérieure à goal target je puis voir 2 erreurs contre chaque champ.

enter image description here

Ceci est mon point de vue modèle

{% for goals in form.goals %}  
     <div class="container-fluid"> 
      <div class="row"> 
       <div class="col-lg-12"> 
        {% if(form_errors(goals.target)) %} 
         <div class="alert alert-danger" role="alert">{{ form_errors(goals.target) }}</div> 
        {% endif %} 
        {% if(form_errors(goals.reached)) %} 
         <div class="alert alert-danger" role="alert">{{ form_errors(goals.reached) }}</div> 
        {% endif %} 
       </div> 
      </div> 
     </div> 
     <div class="row"> 
      <div class="col-xs-2" style="padding-top: 5%"> 
       <label class="" for="exampleInputEmail2">Goal target</label> 
       <div class="form-group input-group"> 
        {{ form_widget(goals.target, {'attr': {'class': 'form-control'}}) }} 
       </div> 


      </div> 
      <div class="col-xs-2" style="padding-top: 5%"> 
       <label class="" for="exampleInputEmail2">Saved to date</label> 

       <div class="form-group input-group"> 
        {{ form_widget(goals.reached, {'attr': {'class': 'form-control'}}) }} 
       </div> 
      </div> 
      <div class="col-xs-2" style="padding-top: 5%"> 
       <label class="" for="exampleInputEmail2">Goal deadline</label> 

       <div class="form-group input-group"> 
        {{ form_widget(goals.deadline, {'attr': {'class': 'form-control dp'}}) }} 
       </div> 
      </div> 
      <div class="col-xs-2" style="padding-top: 5%"> 
       <label class="" for="exampleInputEmail2">Savings</label> 

       <div class="form-group input-group"> 
        {{ form_widget(goals.allocated, {'attr': {'class': 'form-control'}}) }} 
       </div> 

      </div> 
     </div> 
{% endfor %} 

Ceci est mon action

public function prioritiseGoalsAction(Request $request) 
{ 

    $em = $this->getDoctrine()->getManager(); 
    //get user id of currently logged in user 
    $userId = $this->getUser()->getId(); 

    //get survey object of currently logged in user 
    $userGoalsInfo = $em->getRepository('MyBundle:survey')->findOneByuserID($userId); 

    //create the form 
    $form = $this->createForm(new GoalsType(), $userGoalsInfo); 
    $form->handleRequest($request); 

    if ($request->isMethod('POST')) { 
     if ($form->isValid()) { 
      $em->persist($userGoalsInfo); 
      $em->flush(); 
      $this->get('session')->getFlashBag()->add(
       'notice', 
       'Your Goals information has been saved' 
      ); 
      return $this->render('MyBundle:Default/dashboard:prioritise-my-goals.html.twig', array(
       'form' => $form->createView(), 
      )); 
     } 
    } 


    return $this->render('MyBundle:Default/dashboard:prioritise-my-goals.html.twig', array(
     'form' => $form->createView(), 
    )); 
} 

A ce stade, je suis assez désemparés comme je l'ai passé des heures à essayer de résoudre ceci, j'apprécierai vraiment n'importe quelle aide dans ceci.

Répondre

0

Enfin j'ai pu résoudre le problème.

  1. Lors de la création d'un personnalisés de validation et vous devez accéder à la classe entière vous devez ajouter le morceau de code suivant dans votre Constraint classe. Dans mon cas, c'est SavedToDate et j'étais en l'ajoutant dans SavedToDateValidator qui était faux.

    public function getTargets() 
    { 
        return self::CLASS_CONSTRAINT; 
    } 
    
  2. Pour vous assurer que les erreurs de validation apparaissent correctement contre les champs tout en travaillant avec le collection form, je devais améliorer ma fonction validate() de la coutume Validator SavedToDateValidator, grâce à @Richard pour la pointe.

    public function validate($value, Constraint $constraint) 
    { 
        if ($value instanceof Goals) { 
         $target = $value->getTarget(); 
         $savedToDate = $value->getReached(); 
         if ($savedToDate > $target) { 
          $this->context->buildViolation($constraint->message) 
           ->setParameter('%goalname%', $value->getName()) 
           ->setParameter('%reached%', $value->getReached()) 
           ->setParameter('%targetamount%', $value->getTarget()) 
           ->atPath('reached') 
           ->addViolation(); 
         } 
        } 
    } 
    

    Une de la partie importante de la fonction ci-dessus est ->atPath('reached') cette atPath() colle l'erreur sur le terrain où la violation est, je n'ai pas eu plus tôt et qui a été conduit à l'affichage des messages d'erreur contre tous champs plutôt que le seul contre le champ où l'erreur appartenait réellement. Le paramètre à l'intérieur du atpath('fieldname') est le nom de propriété avec lequel vous souhaitez lier l'erreur. Mais pour que cela fonctionne, vous devez également désactiver error_bubbling afin que les erreurs ne soient pas transmises au formulaire parent.

    $builder 
         ->add('goals', 'collection', array(
           'type' => new GoalType(), 
           'allow_add' => true, 
           'by_reference' => false, 
           'error_bubbling' => false 
           )); 
    

Cette solution a fonctionné pour moi et je dois dire que c'était vraiment plaisir à travailler là-dessus, m'a gardé excité.

+1

Bonnes choses, heureux que vous l'ayez trié! – Richard

2

Il s'agit d'une contrainte de niveau classe qui se déclenchera pour chaque instance de votre classe d'objectifs que vous persistez dans votre formulaire.Comme vous parcourez tous vos objets dans le validateur (pourquoi?) Pour chaque instance de votre classe d'objectif, vous allez vérifier toutes vos entités d'objectif, ce qui n'est pas idéal (pour l'entité 2x, vous allez vérifier chaque entité 2x, pour l'entité 3x vous allez vérifier chaque entité 3x, etc.).

Notez que $ value ici est votre objet de classe donc il n'est pas nécessaire de regarder d'autres entités dans le validateur.

public function validate($value, Constraint $constraint) 

Vous devriez écrire validateur quelque chose comme (je n'ai pas de syntaxe cochée):

class SavedToDateValidator extends ConstraintValidator 
{ 
    public function validate($value, Constraint $constraint) 
    { 
      // value should already be an instance of Goal but you could put in a sanity check like 

      if (!$value instanceof Goal) { 

       // throw an exception or whatever 
      }     

      $target = $value->getTarget(); 
      $savedToDate = $value->getReached(); 
      if ($savedToDate > $target) { 
       $this->context->buildViolation($constraint->message) 
        ->setParameter('%string%', $value) 
        ->addViolation(); 
      } 
     } 
    } 
} 

avoir une lecture de la documentation pour class constraint validators

+0

Veuillez modifier votre exemple de code, car un ConstraintValidator n'a pas de fonction 'getTargets()'. Seule la contrainte fait. – DelphiLynx

+1

@DelphiLynx noté et changé – Richard