Symfony 2.5:实体类型,防止在提交表单时获取所有记录

Symfony 2.5 : Entity Type, prevent fetching all records when form submitted

我有一个包含一些经典字段的表单和一个用户实体下拉列表(使用 AngularJs 处理,而不是使用经典表单视图)。 当我提交表单时,整个对象(以及通过下拉列表选择的用户)通过 Ajax 请求发送到我的控制器。

class MyRestController{

    public function MyRestAction(...){
        ...
        $myObject = new MyObject();

        $myForm = $this->createForm(new MyFormType(), $myObject);

        $myForm->handleRequest($request);

        ...
    }
}

class MyFormType extends AbstractType{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add(...),
            ->add(...),
            ->add(...),
            ->add('user', 'entity', array(
                'required' => true,
                'class' => 'User',
                'property' => 'id',
            ))
        ;
            ...
    }

}

然后我持久化整个对象并刷新它。它工作得很好。 但是这个过程非常慢...

问题出在调用 getChoicesForValue 函数 (EntityChoiceList.php) 时的 handleRequest 函数。在我的例子中,该过程对用户实体执行 findAll() 请求,从数据库中获取所有记录(数千条)。

在此函数中,有一个条件检查 EntityChoiceList 是否具有这样的 entityLoader:

if ($this->idAsValue && $this->entityLoader) {
    // do a findById()
} else {
    // do a findAll()
}

(我真的简化了代码,比较复杂,但是你可以在EntityChoiceList.php文件中找到完整的代码)。 在调试我的应用程序之后,我可以看到 $this->idAsValuetrue,而 $this->entityLoadernull,这解释了为什么它执行 findAll()。

所以我想知道是否有一个选项或任何东西可以使这个过程更快并防止获取所有记录?

谢谢。

编辑:

我想我可以尝试自己替换 handleRequest 和 hydrate 对象,但我希望有另一种方法来保持经典过程。

终于找到了解决办法。 我用一个简单的文本类型替换了我的实体类型,然后创建了我自己的 DataTransformer class 以便将用户字符串 ID 转换为真正的用户对象。 通过这种方式用户对象被正确地水化而不需要获取数千条记录,显着提高了加载速度。

这正是我所做的:

我改变了:

class MyFormType extends AbstractType{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('user', 'entity', array(
                'required' => true,
                'class' => 'User',
                'property' => 'id',
            ))
        ;
    }

}

至:

class MyFormType extends AbstractType{

    private $manager;

    public function __construct(ObjectManager $manager)
    {
        $this->manager = $manager;
    }
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('user', 'text', array(
                'required' => true,
            ))
        ;
       // we use a specific ModelTransformer (UserTransformer) created by ourself (see below)
       $builder->get('user')->addModelTransformer(new UserTransformer($this->manager));
    }

}

然后我像这样创建了自己的 UserTransformer class:

class UserTransformer implements DataTransformerInterface
{
    private $manager;

    public function __construct(ObjectManager $manager)
    {
        $this->manager = $manager;
    }

    public function transform($user)
    {
        if (null === $user) {
            return '';
        }

        return $user->getId();
    }

    public function reverseTransform($id)
    {
        if (!$id) {
            return null;
        }

        $user= $this->manager
            ->getRepository(User::class)
            ->find($id)
        ;

        if (null === $user) {
            throw new TransformationFailedException(sprintf(
                'There is no User with the id "%s" !',
                $id
            ));
        }
        return $user;
    }
}

最后我将 EntityManager 传递给这样的表单:

class MyRestController{

    public function MyRestAction(...){
        ...
        $myObject = new MyObject();

        $manager = $this->getDoctrine()->getManager();
        $myForm = $this->createForm(new MyFormType($manager), $myObject);

        $myForm->handleRequest($request);

        ...
    }
}

我在此处的 Symfony 文档中找到了该解决方案: https://symfony.com/doc/2.7/form/data_transformers.html

它运行完美,现在我的应用程序快了 10 倍!