问题
In a symfony 4 form, I need to use something like a query_builder
option that is available on EntityType
but from a CollectionType
. There is a similar question here with no good answers.
In my project, each Site
entity has many Goal
. Each Goal
has a numeric goal and a specific date. I'd like to edit the goals of a site for a specific date only. The problem is that a CollectionType form pulls all goals to show in the form, but I only want to pull the goals for a given date. How? There is no query_builder
on a CollectionType
like there is on an EntityType
. I could change the getter in my Site entity, but I don't know how to pass the needed date to my getter.
For now my work-around is to render the entire form (with ALL associated goals for a given site), and then use some javascript to hide all goals except those with the date to edit. This works, but it's a terrible solution for sites with lots of goals spanning a range of dates.
My Site
entity (only relevant code is shown):
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;
}
}
and my related Goal
entity:
class Goal
{
/** @ORM\Column(type="date") */
private $goalDate;
/** @ORM\Column(type="integer") */
private $goal;
/** @ORM\ManyToOne(targetEntity="App\Entity\Site", inversedBy="goals") */
private $site;
// ...
}
My forms:
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
]);
}
}
and the individual goal form:
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,
]);
}
}
回答1:
Using Form Events, while avoiding the allow_add and allow_delete options for the CollectionType
form might land you in the right neighbourhood:
First - let's assume we're filtering by year, for ease of example, and that the year is being scooped up from a ?y=2018
style of querystring. We'll pass that info down to the form builder:
<?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]);
// ...
}
This implies we should be updating the default options for the SiteGoalsAdminForm
class:
<?php
// SiteGoalsAdminForm.php
// ...
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Site::class,
'year_filter' => 2018
]);
}
// ...
Then, in the buildForm
method of that same class, we could access the Site
object and remove Goals
from it where the year of the goalDate did not fall inside the form's
<?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,
]);
}
);
}
// ...
}
Not a query_builder exactly, but functionally similar.
回答2:
Filter the results using the entity manager in the controller that you want to set on the collection type.
$goals = $entityManager->getRepository(Goals::class)->findBy(['year' => 2020]);
$form = $this->createForm(SiteGoalsType::class, $site, [
'goals' => $goals
]);
Then configure the SiteGoalsType::class
to accept new option goals.
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Site::class,
]);
$resolver->setRequired(['goals']);
}
In the buildForm
method of SiteGoalsType::class
Set the data to the collection type field from the options.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('goals', Type\CollectionType::class, [
'entry_type' => GoalsEmbeddedType::class,
'data' => $options['goals'],
'mapped` => false
]);
}
Make sure the add the 'mapped' => false
to your collection type field else it may lead to removing the records that didn't falls in the filter we have written in the controller.
$goals = $entityManager->getRepository(Goals::class)->findBy(['year' => 2020]);
来源:https://stackoverflow.com/questions/53844825/use-query-builder-on-collectiontype-in-symfony4-forms