在 symfony4 形式的 CollectionType 上使用 query_builder?

Use query_builder on CollectionType in symfony4 forms?

在 symfony 4 形式中,我需要使用类似 query_builder 的选项,它在 EntityType 上可用,但在 CollectionType 上可用。有一个类似的问题 没有很好的答案。

在我的项目中,每个 Site 个实体有多个 Goal。每个 Goal 都有一个数字目标和一个特定日期。我只想编辑特定日期的网站目标。问题是 CollectionType 表单会提取 all 目标以在表单中显示,但我只想提取给定日期的目标。如何? CollectionType 上没有 query_builder,而 EntityType 上有。我可以更改我的站点实体中的 getter,但我不知道如何将所需日期传递给我的 getter。

目前我的解决方法是呈现整个表单(包含给定站点的所有关联目标),然后使用一些 javascript 隐藏所有目标,但带有编辑日期的目标除外。这行得通,但对于具有跨越一系列日期的大量目标的网站来说,这是一个糟糕的解决方案。

我的 Site 实体(仅显示相关代码):

class Site
{
    public function __construct()
    {
        $this->goals = new ArrayCollection();
    }

    /** @ORM\OneToMany(targetEntity="App\Entity\Goal", mappedBy="site") */
    private $goals;


    public function getGoals()
    {
        return $this->goals;
    }
}

和我相关的 Goal 实体:

class Goal
{
    /** @ORM\Column(type="date") */
    private $goalDate;

    /** @ORM\Column(type="integer") */
    private $goal;

    /** @ORM\ManyToOne(targetEntity="App\Entity\Site", inversedBy="goals") */
    private $site;

    // ...
}

我的表格:

class SiteGoalsAdminForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('goals', CollectionType::class, [
                'entry_type' => GoalsEmbeddedForm::class,
            ]);
    }

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

和个人目标形式:

class GoalsEmbeddedForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('goal', IntegerType::class)
            ->add('goalDate', DateType::class);
    }

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

CollectionType 表单使用 Form Events, while avoiding the allow_add and allow_delete 选项可能会让您进入正确的社区:

首先 - 假设我们按年份过滤,为了便于示例,并且年份是从 ?y=2018 样式的查询字符串中获取的。我们将该信息传递给表单生成器:

<?php
// Inside a *Action method of a controller

public function index(Request $request): Response
{
    // ...
    $filteredYear = $request->get('y');
    $form         = $this->createForm(SiteGoalsAdminForm::class, $site, ['year_filter' => $filteredYear]);
    // ...
}

这意味着我们应该更新 SiteGoalsAdminForm class:

的默认选项
<?php

// SiteGoalsAdminForm.php

// ...
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
          'data_class' => Site::class,
          'year_filter' => 2018
        ]);
     }
// ...

然后,在同一个 class 的 buildForm 方法中,我们可以访问 Site 对象并从目标日期所在的年份移除 Goals不属于表格的

<?php

// SiteGoalsAdminForm.php

namespace App\Form;

// ... other `use` statements, plus:
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class SiteGoalsAdminForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($options) {
                $form = $event->getForm();
                /** @var Site */
                $site  = $event->getData();
                $goals = $site->getGoals();

                foreach ($goals as $g) {
                    if ($g->getGoalDate()->format('Y') !== (string) $options['year_filter']) {
                        $site->removeGoal($g);
                    }
                }

                $form->add('goals', CollectionType::class, [
                    'entry_type' => GoalsEmbeddedForm::class,
                ]);
            }
        );
    }

    // ...
}

不完全是 query_builder,但功能相似。

使用要在集合类型上设置的控制器中的实体管理器过滤结果。

$goals = $entityManager->getRepository(Goals::class)->findBy(['year' => 2020]);
$form = $this->createForm(SiteGoalsType::class, $site, [
   'goals' => $goals
]);

然后配置 SiteGoalsType::class 以接受新的选项目标。

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

SiteGoalsType::classbuildForm方法中,将数据设置为选项中的集合类型字段。

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('goals', Type\CollectionType::class, [
        'entry_type' => GoalsEmbeddedType::class,
        'data' => $options['goals'],
        'mapped` => false
    ]);
}

确保将 'mapped' => false 添加到您的集合类型字段,否则它可能会导致删除不属于我们在控制器中编写的过滤器的记录。

$goals = $entityManager->getRepository(Goals::class)->findBy(['year' => 2020]);