Symfony2 - 递归显示表单

时间:2014-04-05 06:06:03

标签: symfony symfony-forms symfony-2.4

大家好(请原谅我的英文)。

我想做一个应用程序,需要允许用户必须在表单上填写他们的个人数据,他们的子女,孙子女和曾孙子(一个小家谱)。

class Person
{
/**
 * @var int
 *
 * @ORM\Id
 * @ORM\GeneratedValue
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(type="string")
 */
private $firstname;

/**
 * @var string
 *
 * @ORM\Column(type="string")
 */
private $lastname;

/**
 * @var \DateTime
 *
 * @ORM\Column(type="datetime")
 */
private $dateOfBirth;

/**
 * @var Person
 *
 * @ORM\ManyToMany(targetEntity="Person")
 */
private $children;


public function __construct()
{
    $this->children = new ArrayCollection();
}

}
}

在PersonType类中,我执行以下操作:

class PersonType extends AbstractType
{
/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('firstname');

    $builder->add('lastname');

    $builder->add('dateOfBirth');

    $builder->add('children', 'collection', array(
        'type'          => new PersonType(),
        'allow_add'     => true,
        'by_reference'  => false,)
    );
}

/**
 * @param OptionsResolverInterface $resolver
 */
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Anything\YourBundle\Entity\Person'
    ));
}

/**
 * @return string
 */
public function getName()
{
    return 'person';
}
}

通过这种方式,我在控制器中使用PersonType,如下所示:

public function newAction()
{
    $entity = new Person();
    $form = $this->createForm(new PersonType(), $entity, array(
        'action' => $this->generateUrl('person_create'),
        'method' => 'POST',
    ));

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

但问题是当我请求此操作的url,并且必须呈现此操作的视图时,存在一个问题,因为没有给出响应,因为它处于无限循环中(我认为是原因)。我想知道是否可以使用Symfony表单,或者如果我必须查看其他替代方案。如果这是可能的,我怎么能这样做,我怎么能限制表格只提供我需要的四个级别(我,我的孩子,我的孙子孙女和我的曾孙子)??

我希望能够理解这个问题。

提前致谢。

4 个答案:

答案 0 :(得分:2)

您可以在表单中添加一个自定义参数,指示当前的递归级别。 要存档,首先需要实现一个新选项:

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Anything\YourBundle\Entity\Person',
        'recursionLevel' => 4
    ));
}

现在,您可以使用buildForm方法更新此值:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    // ...
    if (--$options['recursionLevel'] > 0) {
        $resolver = new OptionsResolver();
        $resolver->setDefaults(
            $options
        );
        $childType = new PersonType();
        $childType->setDefaultOptions($resolver);

        $builder->add('children', 'collection', array(
            'type'          => $childType,
            'allow_add'     => true,
            'by_reference'  => false
        ));
    }
}

未经测试。

答案 1 :(得分:0)

感谢Ferdynator的回答!!

我没有以你提出的方式解决问题,但这种方法对我有所帮助。我在Person表单的构造函数中传递了递归级别,因此,我可以知道何时必须停止:

class PersonType extends AbstractType
{
    private $recursionLevel;

    public function __construct( $recursionLevel ){
        $this->recursionLevel = $recursionLevel;
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if($this->recursionLevel > 0)
        {
            $builder->add('children', 'collection', array(
                'type'          => new PersonType(--$this->recursionLevel),
                'allow_add'     => true,
                'by_reference'  => false,)
            );
        }
    }
}

答案 2 :(得分:0)

Ferdynator,谢谢你的回答。我想根据你的决定提出我的决定:

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Anything\YourBundle\Entity\Person',
            'recursionLevel' => 4
        ));
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // ...
        if (--$options['recursionLevel'] > 0) {
            $builder->add('children', 'collection', array(
                'type' => $childType,
                'allow_add' => true,
                'by_reference' => false,
                'options' => [
                    'recursionLevel' => $options['recursionLevel']
                ],
            ));
        }
    }

它解决了我们的问题。

答案 3 :(得分:0)

我遇到了同样的问题并尝试了此处提供的解决方案。 它们具有明显的缺点,如深度限制和性能开销 - 即使没有提交数据,也总是创建表单对象。

我为克服这个问题所做的是为FormEvents :: PRE_SUBMIT事件添加一个监听器,并在有数据需要解析的情况下动态添加集合类型字段。

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('content');

    $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
        $node = $event->getData();
        $form = $event->getForm();

        if (!$node) {
            return;
        }

        if(sizeof(@$node['children'])){
            $form->add('children', CollectionType::class,
                array(
                    'entry_type' => NodeType::class,
                    'allow_add' => true,
                    'allow_delete' => true
                ));
        }
    });

}

我希望这可以帮助将来有这个问题的人