Symfony 4 - Forms CollectionType 获取特定数据

Symfony 4 - Forms CollectionType get specific data

我有两种实体:"Affaire" 和 "Pointage"。我的 Affaire 实体与我的 Pointage 实体有 OneToMany 关系:

//App\Entity\Affaire.php

/**
 * @ORM\OneToMany(targetEntity="App\Entity\Pointage", mappedBy="numAffaire")
 */
    private $pointages;
//App\Entity\Pointage.php

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\Affaires", inversedBy="pointages")
 * @ORM\JoinColumn(nullable=false)
 */
    private $numAffaire;

我为我的事务实体创建了一个表单,该表单与 "Affaire" 相关联 "Pointages"。

//App\Form\AffaireType.php

class AffaireType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('numAffaire');
        $builder->add('descAffaire');
        $builder->add('pointages', CollectionType::class, [
            'entry_type' => PointageType::class,
            'entry_options' => ['label' => false, 'pointages' => $options['pointages']],
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setRequired(['pointages']);
        $resolver->setDefaults([
            'data_class' => Affaires::class,
        ]);
    }

}

//App\Form\PointageType.php

class PointageType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('heurePointage');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setRequired(['pointages']);
        $resolver->setDefaults([
            'data_class' => Pointage::class,
        ]);
    }
}

我的问题是:如何过滤 AffaireType class 中的 "Pointages"?例如,我想获取与我的 Affaire 关联的所有点数,其中我的点数 id 小于 100。是否可以在某处进行查询?

!!!我强烈反对你在那里做什么!!!

原因:

您的表单类型的数据 class 设置为一个实体(据我所知)。表单旨在 修改 给定的对象(您的实体)。现在,如果您为该表单类型过滤一个字段,然后表单尝试设置 "new" 值,所有 Pointage 不在表单提交中的对象(特别是所有被过滤的 out) 可能会被 删除。如果表单调用 setPointages,您将丢失数据 - 我不太确定,如果它总是被调用,即使 allow_add/allow_remove 设置为 false。如果你忘记了,这会让你的生活变得悲惨。我相信这总是被称为标准 CollectionType.

阅读一些代码后更新: 关于 setPointages 的调用:如果 addPointage/removePointage 存在,他们将被召唤。但是,先前的值由 PropertyAccessor(属性-访问组件)从目标对象(!)读取,并且将计算差异并相应地调用 add/remove -> 删除任何不在新集合中的实体

可能的解决方法

  • 大量使用数据映射器/数据转换器/表单事件来以某种方式隐藏存在更多Pointage实体的事实。这可能 非常 很好,但它使事情复杂化很多,但仍然可能成为一个干净的解决方案。

    大意是:

    • 表单渲染:从实体(事件,所有点数)获取 -> 你的 transformer/mapper/form 事件处理程序(事件,所有 -> 过滤点数) -> 表单(事务,过滤点数)
    • 表单提交:表单(事件,过滤点数)-> 你的 transformer/mapper/form 事件处理程序(事件, 过滤 -> "new all" 点数)->在实体上设置(外遇,所有点数)

    但是,您可能必须花费大量时间来了解表单组件的内部结构才能正确且安全地执行此操作...(一个指针是 MergeCollectionListener, which you might be able to adapt to your needs, you might also take a look at the ResizeFormListener,它添加和删除子表单取决于给定的数据。请记住,如果您过滤数据,您可能应该创建自己的集合类型并添加一个新的表单侦听器来优雅地处理所有事情)

  • 不是将其集成到 AffaireType 中,而是添加另一种具有两个字段的表单类型 AffairePointageTypeaffaireAffaireType 没有 pointages) 和 pointages (CollectionType) 并称它为

    $filteredPointages = someFilterFunction($affaire->getPointages());
    $this->createForm(AffairePointageType::class, [
         'affaire' => $affaire,
         'pointages' => $filteredPointages,
    ], [
         'pointages' => ... //the option you provide for the CollectionType
    ]);
    

    您显然可以提供任何点数,由您喜欢的任何过滤器过滤,并且只会编辑您提供的点数,而与 $affaire 相关的任何其他内容保持不变。但是请注意,您必须自己处理添加或删除的点数。 (虽然这可能会破坏关注点分离)

    本质上,这是一个更容易理解的先前解决方法的版本,其中所有过滤器逻辑都​​是外部的,但由于表单不通信 "You can edit pointages of the affaire",而是 "you can simultaneously edit an affaire and a set of pointages, which might or might not be related",它在语义上是清楚并且不会让未来的用户(包括你在内)感到惊讶。

但是,我认为您的方法可能存在缺陷...但是由于不清楚您实际想要实现的目标,因此很难提出合适的解决方案。

如果只是 "there are too many (sub) forms displayed" - 那么它更像是一个显示问题,可以而且可能应该通过 javascript 或 css (display:none) 或两者来解决. (恕我直言,这是更好的方法,不会混淆表单逻辑 and/mechanics)。