【问题标题】:Symfony2 - Display a form recursivelySymfony2 - 递归显示表单
【发布时间】:2014-04-05 06:06:02
【问题描述】:

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

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

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 表单来完成,或者我是否必须查看其他替代方案。如果这是可能的,我怎么能做到这一点,我怎么能限制表单只呈现我需要的四个级别(我、我的孩子、我的孙子和我的曾孙)??

我希望问题已经被理解了。

提前致谢。

【问题讨论】:

    标签: symfony symfony-forms symfony-2.4


    【解决方案1】:

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

    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
            ));
        }
    }
    

    这未经测试。

    【讨论】:

      【解决方案2】:

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

      我解决这个问题的方法是为 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
                      ));
              }
          });
      
      }
      

      我希望这对将来遇到此问题的人有所帮助

      【讨论】:

        【解决方案3】:

        感谢费迪纳特的回答!!

        我没有按照您建议的方式解决问题,但这种方法对我有所帮助。我在 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,)
                    );
                }
            }
        }
        

        【讨论】:

        • 您的解决方案有一个缺点:由于您使用constructor 传递您的值,因此您无法使用declare this Type as a service
        • 确实如此。我没有意识到这个观点。我想改变它。感谢您的评论 ferdynator!
        【解决方案4】:

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

        公共函数 setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(数组( 'data_class' => 'Anything\YourBundle\Entity\Person', 'recursionLevel' => 4 )); } 公共函数 buildForm(FormBuilderInterface $builder, array $options) { // ... if (--$options['recursionLevel'] > 0) { $builder->add('children', 'collection', array( '类型' => $childType, 'allow_add' => 真, 'by_reference' => 假, '选项' => [ 'recursionLevel' => $options['recursionLevel'] ], )); } }

        它解决了我们的问题。

        【讨论】:

          猜你喜欢
          • 2015-05-18
          • 2016-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-07
          • 1970-01-01
          相关资源
          最近更新 更多