Symfony 5为CollectionType过滤实体 隐藏Performer等于Service Performer的行
问题原因
你之前的实现思路存在两个核心错误:
removeElement()是 DoctrineCollection类的方法,子表单的FormInterface没有该方法,无法在单个Service子表单内删除自己所在的Collection条目- PRE_SET_DATA事件作用在子表单时,Collection已经完成了初始条目生成,此时无法移除整行,只会导致表单和底层数据不匹配
解决方案:在父表单层过滤Collection数据
直接在PerformerServiceType中添加PRE_SET_DATA事件,提前过滤掉不符合要求的Service条目即可,不需要修改子表单逻辑:
<?php namespace App\Form; use App\Entity\User; use Doctrine\Common\Collections\Criteria; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; class PerformerServiceType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('performer_services',CollectionType::class,[ 'entry_type' => ServicePerformerNoMoneyUnitType::class, 'entry_options' => ['label' => false], 'allow_add' => true, 'allow_delete' => true, // 设为false避免修改原实体的原始数据,防止误删数据库条目 'by_reference' => false, ]) ->add('enregistrer', SubmitType::class); // 添加事件过滤不符合要求的条目 $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { /** @var User $user */ $user = $event->getData(); $form = $event->getForm(); if (!$user) { return; } // 用Criteria过滤集合,不会修改原实体的原始数据 $criteria = Criteria::create() ->where(Criteria::expr()->neq('performer', 'servicePerformer')); $filteredServices = $user->getPerformerServices()->matching($criteria); // 把过滤后的集合赋值给表单字段 $form->get('performer_services')->setData($filteredServices); }); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => User::class, ]); } }
优化建议
为了避免空对象、代理对象导致的比较异常,建议你判断两个关联字段相等时,直接比对主键ID更稳妥:
// 过滤条件可以替换为更严谨的版本 $criteria = Criteria::create() ->where(Criteria::expr()->neq('performer.id', 'servicePerformer.id'));
同时你可以删掉原来加在ServicePerformerNoMoneyUnitType里的PRE_SET_DATA事件代码,已经不需要了。
内容的提问来源于stack exchange,提问作者Pax
相关产品推荐
相关产品推荐

