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