room13
room13

Reputation: 1922

Double validation errors in custom form type

I'm implementing a custom form type that provides an autocomplete field to select a location (country,city or spot). The form type creates two fields, one text field for the autocomplete search input and one hidden field to hold the selected id of the selected location.

When typing into the text field, a server call is made and results are displayed via jquery autocomplete. If a location is selected, the id of the selected location is written to the hidden field whereas the name of the location is displayed in the text field. On the server, I use a client transformer to lookup the entity of the id passed by the hidden field. The text field is ignored.

My model class defines a location field with a property to write back the location entity annotated with a NotNull validation constraint.

Everything works perfectly fine so far but if I do not select a location, the validation message "This value should not be null." is displayed two times.

Double validation error

The bundle is public and can be found in my github repo. The relevant classes are the LocationFieldType and the LocationDataTransformer and the form theme.

And now for how I'm integrating the form type into my project. I added the whole code, sorry for the mass;)

In the model, I define the property as following:

class JourneyCreate
{

    /**
     * @Assert\NotNull()
     * @Assert\Choice(choices = {"offer", "request"})
     */
    public $type;

    /**
     * @Assert\NotNull()
     * @Assert\Date()
     */
    public $date;

    /**
     * @Assert\NotNull()
     * @Assert\Time()
     */
    public $time;

    /**
     * @Assert\NotNull()
     *
     */
    public $start;

    /**
     * @Assert\NotNull()
     *
     */
    public $destination;


    public function buildJourney(User $owner)
    {
    switch($this->type)
    {
        case 'offer':
            $journey = new JourneyOffer();
            break;
        case 'request':
            $journey = new JourneyRequest();
            break;
        default:
            throw new \InvalidArgumentException('Invalid journey type');
    }

    $journey->setDate($this->date);
    $journey->setTime($this->time);

    $journey->addStation(new JourneyStation($this->start));
    $journey->addStation(new JourneyStation($this->destination));

    $journey->setOwner($owner);

    return $journey;
    }
}

And in the main form I add the field as following:

    class JourneyCreateType extends BaseType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {

    $builder
        ->add('type','choice', array(
            'choices'   => array(
                'offer'   => 'Driver',
                'request' => 'Passanger',
            ),
            'empty_value'=>'',
            'multiple'  => false,
            'expanded'  => true,
        ))
        ->add('date','date',array(
            'widget' => 'single_text',
            'format' => $this->getDateFormat(\IntlDateFormatter::TRADITIONAL),
        ))
        ->add('time','time',array(
            'widget' => 'single_text',
        ))
        ->add('start','room13_geo_location')
        ->add('destination','room13_geo_location')
    ;

    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array(
        'data_class' => 'Acme\DemoBundle\Form\Model\JourneyCreate',
    ));
    }


    public function getName()
    {
    return 'journey_create';
    }
}

And the controller code:

/**
* @Route("/create/{type}", defaults={"type" = null})
* @Template()
*/
public function createAction($type=null)
{

    if($type !== null && !in_array($type,array('request','offer')))
    {
        throw new NotFoundHttpException();
    }

    $journeyCreate = new JourneyCreate();
    $journeyCreate->type = $type;

    $form = $this->createForm(new JourneyCreateType(),$journeyCreate);

    if($this->isPost())
    {
        $form->bind($this->getRequest());

        if($form->isValid())
        {
          $journeyCreate = $form->getData();
          $journey = $journeyCreate->buildJourney($this->getCurrentUser());
          $this->persistAndFlush($journey);
          return $this->redirect($this->generateUrl('acme_demo_journey_edit',array('id'=>$journey->getId())));
        }
    }

    return array(
        'form'  => $form->createView(),
    );
}

And finaly the template code to display the form:

{% block page_body %}
    <form class="form-horizontal" action="{{ path('acme_demo_journey_create') }}" method="post" novalidate>
    {{form_widget(form)}}
    <div class="form-actions">
        <button class="btn btn-primary" type="submit">{{'form.submit'|trans}}</button>
        <a href="{{path("acme_demo_journey_index")}}" class="btn">{{'form.cancel'|trans}}</a>
    </div>
    </form>
{% endblock %}

I'm having the theory that this could be because I use two form fields but don't know how to fix this. Any suggestions about how to solve this more elegant are welcome.

Upvotes: 0

Views: 1900

Answers (1)

room13
room13

Reputation: 1922

As complicated as this question might look, the answer is as simple as removing the {{form_errors(form)}} from the widget template block. Because the *form_row* block looks like:

{% block form_row %}
{% spaceless %}
    <div class="form_row">
        {{ form_label(form) }}
        {{ form_errors(form) }}
        {{ form_widget(form) }}
    </div>
{% endspaceless %}
{% endblock form_row %}

The error was simply outputted two times.

Upvotes: 2

Related Questions