Symfony2表单:如何在创建后用查询结果填充动态添加的实体字段


Symfony2 form : how to populate dynamically added entity field with a query result after creation?

在Symfony 2.7项目中,假设我们有一个由两个字段"date"(日期)和"group"(实体)组成的表单,它们都有自己的EventListener附加到FormEvents::SUBMIT事件。

在第一次提交之后,我想在表单中添加一个新字段'travels',并使用前面两个字段作为标准的查询结果填充它。

如何防止'旅行'实体字段从数据库中获取所有'旅行',并在引发事件后手动填充它?我确实缺少一些理解,我是Symfony的新手。

我知道在每个事件中创建"旅行"时,我可以直接在"选择"选项中传递数据,但它会使无用的DB调用。我可能会计算(感兴趣的)注册事件的数量,并在最后一个事件发生时创建"旅行"字段,但这似乎有点奇怪…

对于这种情况是否有一个干净的解决方案?
(英语不好意思,不是我的母语)

<?php
namespace MyBundle'Form;
// use directives...
class TravelRequestsWorklistType extends AbstractType {
private $em;
private $travelRepository;
private $searchQueryBuilder;
public function __construct(EntityManager $em) {
  $this->em = $em;
  $this->travelRepository = $this->em->getRepository(Travel::class);
}
public function buildForm(FormBuilderInterface $builder, array $options) {
  $formFactory = $builder->getFormFactory();
  $builder
    ->add('requestedDate', 'date', array(
                                          'widget' => 'single_text',
                                          'input' => 'datetime',
                                          'format' => 'dd/MM/yyyy',
                                          'attr' => array('class' => 'date'),
                                          'data' => null,
                                          'mapped' => false
    ))
    ->add('selectedGroup', 'entity', array(
                                      'class' => 'MyBundle'Entity'Group',
                                      'placeholder' => '',
                                      'mapped' => false,
                                      'multiple' => false,
    ))
    ->add('search', 'submit');
    $builder->get('requestedDate')->addEventListener(FormEvents::SUBMIT,
                                                    $this->onDateCriteriaEvent($formFactory));
    $builder->get('selectedGroup')->addEventListener(FormEvents::SUBMIT,
                                                    $this->onGroupCriteriaEvent($formFactory));
}
private function onDateCriteriaEvent(FormFactory $ff) {
  return
  function(FormEvent $event) use ($ff) {
    $root = $event->getForm()->getParent();
    $requestedDate = $event->getData();
    $qb = $this->getQueryBuilder();
    $qb->andWhere('r.requestedDate = :requestedDate')
    ->setParameter('requestedDate', $requestedDate);
    if(!$this->searchHasResult($root)) {
      $this->addTravels($ff, $root);
    }
  };
}
private function onGroupCriteriaEvent(FormFactory $ff) {
  return
  function(FormEvent $event) use ($ff) {
    $root = $event->getForm()->getRoot();
    $selectedGroup = $event->getData();
    $qb = $this->getQueryBuilder();
    $qb->andWhere('r.group = :group')
    ->setParameter('group', $selectedGroup);
    if(!$this->searchHasResult($root)) {
      $this->addTravels($ff, $root);
    }
  };
}
private function addTravels(FormFactory $ff, Form $rootForm) {
  $travels = $ff->createNamedBuilder('travels', 'entity', null,
              array(
                  'class' => 'MyBundle'Entity'Travel',
                  'mapped' => false,
                  'multiple' => true,
                  'expanded' => true,
                  'auto_initialize' => false
              ));
  $submitButton = $ff->createNamedBuilder('validate', 'submit');
  $travels->addEventListener(FormEvents::PRE_SUBMIT, $this->onSearchResult());
  $form->add($travels->getForm())->add($submitButton->getForm());
}
// The method setData() shows "This form should not contain extra fields"
private function onSearchResult() {
  return
  function(FormEvent $e) {
    $data = $this->searchResultQueryBuilder->getQuery()->getResult();
    $e->setData($data);
  };
}
private function getQueryBuilder() {
  if(null === $this->searchQueryBuilder) {
    $this->searchResultQueryBuilder = $this->travelRepository->createQueryBuilder('r');
// add dummy where clause here in order to call "andWhere" indistinctly later
    $this->searchResultQueryBuilder->where("1 = 1");
  }
  return $this->searchQueryBuilder;
}
private function searchHasResult(Form $form) {
  return $form->has('travels');
}
}

我终于找到了一个解决办法。

$formField->addEventListener()上,我们可以设置优先级顺序作为第三个参数。这样我就可以知道最后触发哪个事件。

必须有另一个解决方案(如注册所有的事件,我想在一些成员数组监控,并检查他们是否已在每个回调执行)。目前,在不检查回调执行的情况下设置硬编码事件优先级是可以的(仅适用于2个事件)。

该字段在创建过程中填充,而不是在创建之后填充,但无论如何,它可以工作:)。

public function buildForm(FormBuilderInterface $builder, array $options) {
  $formFactory = $builder->getFormFactory();
  $builder
    ->add('requestedDate', 'date', array(
                                          'widget' => 'single_text',
                                          'input' => 'datetime',
                                          'format' => 'dd/MM/yyyy',
                                          'attr' => array('class' => 'date'),
                                          'data' => null,
                                          'mapped' => false
    ))
    ->add('selectedGroup', 'entity', array(
                                      'class' => 'MyBundle'Entity'Group',
                                      'placeholder' => '',
                                      'mapped' => false,
                                      'multiple' => false,
    ))
    ->add('search', 'submit');
    // Here we could use a register method which would contain listener informations
    $builder->get('requestedDate')
            ->addEventListener(FormEvents::SUBMIT,
                               $this->onDateCriteriaEvent($formFactory),
                               0); //Will be triggered first
    $builder->get('selectedGroup')
            ->addEventListener(FormEvents::SUBMIT,
                               $this->onGroupCriteriaEvent($formFactory),
                               1); //Will be triggered after 1st
}

private function onDateCriteriaEvent(FormFactory $ff) {
  return
  function(FormEvent $event) use ($ff) {
    $root = $event->getForm()->getParent();
    $qb = $this->getQueryBuilder();
    if(null !== $event->getData()) {
      $requestedDate = $event->getData();
      $qb->andWhere('r.requestedDate = :requestedDate')
         ->setParameter('requestedDate', $requestedDate);
      // Here we could check for registered events not already raised 
      // and do the appropriate action (if it is the last or something else...)
    }
  };
}
private function onGroupCriteriaEvent(FormFactory $ff) {
  return
  function(FormEvent $event) use ($ff) {
    $qb = $this->getQueryBuilder();
    $root = $event->getForm()->getRoot();
    if(null !== $event->getData()) {
      // Check for events not already raised....
      $selectedGroup = $event->getData();
      $qb->andWhere('r.group = :group')
         ->setParameter('group', $selectedGroup);
    }
    $travels = $qb->getQuery()->getResult();
    if($this->searchHasResult($root) {
        // We know this event is the last raised so we can add 'travels' field
        $this->addTravels($ff, $root);
    }
  };
}
private function addTravels(FormFactory $ff, Form $rootForm) {
  $travels = $ff->createNamedBuilder('travels', 'entity', null,
              array(
                  'class' => 'MyBundle'Entity'Travel',
                  'mapped' => false,
                  'multiple' => true,
                  'expanded' => true,
                  'auto_initialize' => false
              ));
  $submitButton = $ff->createNamedBuilder('validate', 'submit');
  $travels->addEventListener(FormEvents::PRE_SUBMIT, $this->onSearchResult());
  $form->add($travels->getForm())->add($submitButton->getForm());
}