将 EntityManager 从 EntityType 传递到另一个 EntityType

passing the EntityManager from an EntityType to another EntityType

我正在使用 Symfony 3.3。

- 摘要:

我有一个必须在控制器中验证的表单,验证中使用的实体包含 ArrayCollection 属性,这意味着我将使用 2 个 EntityType classes 验证嵌入表单(父类的 CollectionType 为子类)。

问题是:我想在验证之前通过 OptionResolver 将 EntityManager 对象传递给子 EntityType 以编辑表单数据。

所以,我必须通过 2 个步骤将 EntityManager 对象从 Controller 传递到 ChildType:

- 详情:

在控制器中:

public function postAssignmentAction(Request $request)
{
    $result = new Assignments();
    $em = $this->get('doctrine.orm.entity_manager');
    $form = $this->createForm(AssignmentsType::class, $result, array(
            'validation_groups' => array('Creation', 'Default'),
            'em' => $em,
    ));
    $form->submit($request->request->all()["assignments"]);
    //validation ...
}

在 ParentType (AssignmentsType) 中:

class AssignmentsType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    //dump($options['em']); //this line works fine and it dump the EntityManager
    $builder->add('assignments', CollectionType::class,array(
      'entry_type' => AssignmentType::class,
      'required'=> true,
      'allow_add' => true,
      'by_reference' => false,
      'em' => $options['em'] //without this line, there is no error if we delete the ChildType (AssignmentType)
    );
  public function configureOptions(OptionsResolver $resolver)
  {
    $resolver->setDefaults([
      'data_class' => 'Empire\UniversityBundle\Entity\Assignments',
      'cascade_validation' => true,
      'error_bubbling' => false,
      'em' => null,
    ]);
    $resolver->setRequired('em');
  }
}

到现在为止,如果我们从之前的代码中删除这一行 : ('em' => $options['em']) 没有错误。

如果我在将选项从 ParentType 传递到 ChildType 时重复相同的操作,就像我们在 ParentType 中所做的那样,我会收到错误消息(如下)。

让我们以 ChildType 结束 class:

class AssignmentType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    dump($options['em']); //this line can't be executed because of the error
    $this->em = $options['em'];
    $builder->add('full_name');
  }
  public function configureOptions(OptionsResolver $resolver)
  {
    $resolver->setDefaults([
        'data_class' => 'Empire\UniversityBundle\Entity\Assignment',
        'validation_groups' => array('Creation', 'Default'),
        'cascade_validation' => true,
        'error_bubbling' => false,
        'em' => null,
    ]);
    $resolver->setRequired('em');
  }
}

所以在 ParentType 中,使用 ParentType:buildForm() 的选项:

public function buildForm(FormBuilderInterface $builder, array $options){
  $builder->add('assignments', CollectionType::class,array(
    'entry_type' => AssignmentType::class,
    'required'=> true,
    'allow_add' => true,
    'by_reference' => false,
    'em' => $options['em']
  );
}

- 错误部分

我收到这个错误:

"message": "The option \"em\" does not exist. Defined options are: \"action\", \"allow_add\", \"allow_delete\", \"allow_extra_fields\", \"attr\", \"auto_initialize\", \"block_name\", \"by_reference\", \"compound\", \"constraints\", \"csrf_field_name\", \"csrf_message\", \"csrf_protection\", \"csrf_token_id\", \"csrf_token_manager\", \"data\", \"data_class\", \"delete_empty\", \"disabled\", \"empty_data\", \"entry_options\", \"entry_type\", \"error_bubbling\", \"error_mapping\", \"extra_fields_message\", \"horizontal_input_wrapper_class\", \"horizontal_label_class\", \"horizontal_label_offset_class\", \"inherit_data\", \"invalid_message\", \"invalid_message_parameters\", \"label\", \"label_attr\", \"label_format\", \"label_render\", \"mapped\", \"method\", \"post_max_size_message\", \"property_path\", \"prototype\", \"prototype_data\", \"prototype_name\", \"required\", \"sonata_admin\", \"sonata_field_description\", \"sonata_help\", \"translation_domain\", \"trim\", \"upload_max_size_message\", \"validation_groups\".",
"class": "Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException",

即使在 ChildType:configureOptions() 方法中定义了 "em" 选项之后:

$resolver->setDefaults([
    'data_class' => 'Empire\UniversityBundle\Entity\Assignment',
    'cascade_validation' => true,
    'error_bubbling' => false,
    'em' => null,
]);
$resolver->setRequired('em');

我不知道为什么第一步(将 EntityManager 从 Controller 传递到 ParentType)有效

但是在第二步中(将EntityManager从ParentType传递给ChildType),它不起作用

有关实体的更多详细信息,您可以在此处找到实体 classes:

否则,是否有任何解决方案可以在 ChildType 中传递 EntityManager ?我的意思是没有任何技巧,例如使用变量 $options["label"] 或 $options["attr"]

非常感谢您的任何建议。

- Github 讨论问题:https://github.com/symfony/symfony/issues/25675

此致

在 symfony 3.3 文档中

If you need to access services from your form class, add a __construct() method like normal:

class AssignmentsType extends AbstractType
{
    private $em;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->em = $entityManager;
    }

...
..
.

If you're using the default services.yml configuration (i.e. services from the Form/ are loaded and autoconfigure is enabled), this will already work!

If you're not using autoconfigure, make sure to tag your service with form.type.

我问了一个关于从一个 EntityType 到另一个 EntityType 的对象(不是特定的像 EntityManager 实例这样的服务,它在任何地方都具有相同值的服务)的一般性问题:github.

经过一些研究后,我找到了我在此处发布的最佳解决方案 github answer

我在 ParentType 的 buildForm:add() 方法的第三个参数中使用同一行 "custom_option" 将对象从 ParentType 传递到 ChildType 是错误的。因为 ParentType class 包含一个嵌入表单,并且要将任何选项(甚至是自定义选项)传递给 ChildType,该选项应该位于 buildForm:add 的第三个参数的 'entry_options' 行中().否则选项将被发送到 CollectionType 而不是 ChildType class.

参考:collection.html#entry-options

所以根据我第一个问题(本题)的代码修改后的ParentType应该是这样的:

class AssignmentsType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    //dump($options['em']); //this line works fine and it dump the object from the em option
    $builder->add('assignments', CollectionType::class,array(
      'entry_type' => AssignmentType::class,
      'required'=> true,
      'allow_add' => true,
      'by_reference' => false,
      'entry_options' => array('em' => $options['em']) ,// ->>>we modified this line <<<-
    );
  public function configureOptions(OptionsResolver $resolver)
  {
    $resolver->setDefaults([
      'data_class' => 'Empire\UniversityBundle\Entity\Assignments',
      'cascade_validation' => true,
      'error_bubbling' => false,
      'em' => null,
    ]);
    $resolver->setRequired('em');
  }
}

此致