在事件订阅者中嵌入嵌套的,过滤的集合字段类型

时间:2013-11-08 11:17:00

标签: forms symfony

我正在创建一个动态的问题/答案表单。我需要显示给定部分的答案字段集合,这些字段仅与登录用户有关。数据模型是:

部分有很多SectionQuestion(一个部分代表表单的页面) SectionQuestion有一个问题和许多答案 答案有一个用户和一个SectionQuestion

我的表单代码是:

//SectionFormType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('sectionQuestions', 'collection', array(
            'type' => new SectionQuestionFormType($this->answerListener),
            'label' => false
        ))
        ->add('save', 'submit');
}

//SectionQuestionFormType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventSubscriber($this->answerListener);
}

//AnswerListener.php
public function preSetData($event)
{
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) {
        return;
    }

    $form->add(
        'answers',
        'collection',
        array(
            'type' => new AnswerFormType($data->getQuestion()),
            'auto_initialize' => false,
            'label' => false,
            'required' => false,
        )
    );
}

//AnswerFormType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{

    $fieldType = $this->question->getAnswerType();

    $config = array(
        'required' => true,
        'label'    => $this->question->getText(),
        'auto_initialize' => false
    );

    if ($fieldType == 'choice') {
        $valueList = $this->question->getValueList();
        $values = $valueList->loadChoiceList();

        $config['choice_list'] = $values;
        $config['empty_value'] = 'Please select one';
    }

    $builder->add('answer', $fieldType, $config);
}

这段代码很实用,但不是我真正需要的。我需要在AnswerListener中添加的集合,以提供与当前登录的用户空白答案相关的现有答案的混合,其中尚未输入答案。

  1. 我知道不是在AnswerListener'答案'(它与SectionQuestions中的关系属性匹配)中命名集合字段,我可以将其命名为'filteredAnswers',并在SectionQuestions实体类中编写一个getter / setter返回答案的子集。但!我无法访问Entity类中getter内的登录用户。

  2. 我无法将字段类型更改为Entity,因为据我所知,我将无法规定使用AnswerFormType表单,这对于设置答案字段标签至关重要使用我的问题实体的值(实际问题文本)。

  3. 在AnswerListener中,我可以使用$ form-> add($ this-> factory-> createNamed(....))并以这种方式提供数据子集(因为我可以访问实体管理器在AnswerListener类中登录用户并执行查询。但!我无法弄清楚如何使这成为一个集合字段类型。我可以使用createNamed来正确显示表单:

    $form->add(
        $this->factory->createNamed(
            'answer', 
            new AnswerFormType(
                $data->getQuestion()), 
                $answer, 
                array(
                        'auto_initialize' => false,
                        'mapped' => false,
                        'label' => false
                 )
            )
        )
    )
    
  4. ...但是!这只会在表单中添加单独的答案字段,因此发布的值永远不会持久保存到数据库中(可能是因为SectionQuestions希望通过未找到的'answers'属性添加它们。)

    似乎我在每一个转折点都难过。关于如何让这个工作的任何想法?

1 个答案:

答案 0 :(得分:0)

好吧,我自己想出来了。我不得不嵌入个人答案'形式如上文第3点所述。如上所述,SectionQuestions与通过$ answers属性的Answer相关,该属性期望是一个集合,因此我无法保存表单。为了解决这个问题,我使用了'映射' => createNamed中的false选项,并将表单数据作为数组访问。在代码中更容易看到,所以这里是:

//AnswerListener.php
public function preSetData($event)
{
    $data = $event->getData();
    $form = $event->getForm();

    if (null === $data) {
        return;
    }

    $answerRepository = $this->entityManager
                             ->getRepository('RecruitmentCoreBundle:Answer');

    $answer = $answerRepository->findOneBy(
        array(
            'sectionQuestion_id' => $data->getId(),
            'user_id' => $authenticated_user_id
        )
    );

    if(!$answer) {
     $answer = null;
    }

    $form->add(
        $this->factory->createNamed(
            'answer',
            new AnswerFormType($data->getQuestion()),
            $answer,
            $formOptions = array (
                'auto_initialize' => false,
                'mapped' => false,
                'label' => false,

            )
        )
    );
}

您可以在此处看到我检查数据库以查看登录用户是否已经为给定的SectionQuestion提供了答案。如果有,则传递给createdNamed,如果没有,则传递null并在屏幕上显示空白的答案表单字段。

要保存表单,我有一个如下服务:

//FormService.php
class FormService
{
    private $formFactory;
    private $entityManager;
    private $sectionForm;

    /**
     * @param FormFactoryInterface $factory
     * @param EntityManager $em
     * @param SectionFormType $form
     */
    public function __construct(
         FormFactoryInterface $factory, 
         EntityManager $em, 
         SectionFormType $form)
    {
        $this->formFactory = $factory;
        $this->entityManager = $em;
        $this->sectionForm = $form;
    }

    public function generateSectionForm($roundId, $sectionId)
    {

        $sectionRepository = $this->entityManager
                                  ->getRepository('RecruitmentCoreBundle:Section');

        $section = $sectionRepository->findOneBy(array('id' => $sectionId));

        return $this->formFactory->create($this->sectionForm, $section);
    }


    public function saveSectionAnswers($form, Section $section)
    {
        foreach($form['sectionQuestions'] as $sectionQuestion) {

            $answer = $sectionQuestion['answer']->getData();

            if (null === $answer->getId()) {
                $sectionQuestion = $sectionQuestion->getData();
                $answer->setSectionQuestion($sectionQuestion);
                $answer->setUser(//logged in user);
            }

            $this->entityManager->persist($answer);
        }

        $this->entityManager->flush();

        return $form;
    }
}

即使我使用了映射' =>在AnswerListener.php中的createNamed方法调用中,您可以在我的FormService类的saveSectionAnswers方法中看到$ sectionQuestion [' answer'] - > getData();返回一个Answer对象。这将填充给定的SectionQuestion的已提交表单数据,然后我可以将其保存到数据库中。可能是提供了Answer对象,因为我将Answer实体定义为我的AnswerFormType.php类中的data_class:

//AnswerFormType.php
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(
         array(
        'data_class' => 'Recruitment\CoreBundle\Entity\Answer'
         )
    );
}

无论如何,它工作并提供我所需的功能。