4

I would like doing a form in some steps in Symfony2 (2.3 exactly), but when I try to do this, I get an error in my form.

I have done the next:

1) I've created a class

class MyClass
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="name", type="string", length=255)
 * @Assert\NotNull()
 */
private $name;

/**
 * @var string
 *
 * @ORM\Column(name="surname", type="string", length=255)
 * @Assert\NotNull()
 */
private $surname;
}

2) I've created the FormType class:

class MyClassType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name', null, array('label' => 'name'))
        ->add('surname', null, array('label' => 'surname'));     
}

And I have created 2 more classes to separate the process for getting the data of the form:

class MyClass1Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name', null, array('label' => 'name'));     
}

class MyClass2Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('surname', null, array('label' => 'surname'));     
}

And in the controller I have some methods:

public function new1Action()
{
    $entity = new MyClass();
    $form   = $this->createForm( new MyClass1Type( $entity );

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

public function new2Action(Request $request)
{
    $entity  = new MyClass();
    $formMyClass1 = $this->createForm(new MyClass1Type($entity) );
    $formMyClass1->bind($request);

    if (!$formMyClass1->isValid()) {
        print_r($formMyClass1->getErrors());
        return new Response("Error");
    }

    $form   = $this->createForm( new MyClass2Type($entity) );

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

I render the first form (new1Action) and it get the data perfectly, but the problem is when I submit the data. In the new2Action, the application goes throw the response("error") code, because the form is not valid. The print_r() function shows the next information:

Array ( [0] => Symfony\Component\Form\FormError Object ( [message:Symfony\Component\Form\FormError:private] => Este valor no debería ser null. [messageTemplate:protected] => This value should not be null. [messageParameters:protected] => Array ( ) [messagePluralization:protected] => ) ) 

I think that the problem is that the class is not complete with the data got in the first form, but I need to separate the form in two steps and I have no idea how deal with this error.

Could someone help me?

Thanks in advance.

Airam
  • 2,048
  • 2
  • 18
  • 36

1 Answers1

6

After binding your entity with MyClass1Type, your entity have a valid name but no surname. $myFormClass1->isValid() returns false, because it try to validate the entity and you didn't specify to validate part of data, so it don't like surname being null.

You should use validation groups to validate your entity on partial data. Check here in Symfony book.

Add in your form :

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => array('validationStep1'),
    ));
}

And define your validation group on the @Assert annotation on your entity with @Assert\NotNull(groups={"validationStep1"}):

/**
 * @var string
 *
 * @ORM\Column(name="name", type="string", length=255)
 * @Assert\NotNull(groups={"validationStep1"})
 */
private $name;

/**
 * @var string
 *
 * @ORM\Column(name="surname", type="string", length=255)
 */
private $surname;
Florent
  • 806
  • 2
  • 8
  • 14
  • Thank you so much!! I've never read about this, but it solves my problem completely. You have +1 and the tick. A greeting!! – Airam Jul 22 '13 at 15:43
  • Great, it is what I'm looking for, but I still have some doubts. First, in MyClass2Type you have to include the field name again, because if not, the new3Action (which bind the MyClass2Type with the whole entity) doesn't bind the name field in the entity because there isn't any name field in the MyClass2Type. Furthermore, if you put the name field with the read_only attribute you can change by html, so is not secure :( – Angel Jul 24 '13 at 11:11
  • yes, you're right for name attribute, but if you add it to new3Action you should make sure that the user cannot change it. The way I dealt with that is to save the entity in the session between each step. First screen show MyClass1Type, then bind it to the entity, save it to the session and show MyClass2Type. Just be sure to get the entity from the session when you create the form. In this case you don't need to have all field defined in each type. – Florent Jul 24 '13 at 12:26