Symfony 5.4 及更高版本中 getDoctrine() 的新替代方案

New alternative for getDoctrine() in Symfony 5.4 and up

正如我的 IDE 指出的那样,AbstractController::getDoctrine() 方法现已弃用。

我在官方文档和 Github 变更日志中都没有找到任何关于此弃用的参考。

此快捷方式的新替代方案或解决方法是什么?

如前所述here

Instead of using those shortcuts, inject the related services in the constructor or the controller methods.

您需要使用依赖注入。

对于给定的控制器,只需在控制器的构造函数中注入 ManagerRegistry


use Doctrine\Persistence\ManagerRegistry;

class SomeController {

    public function __construct(private ManagerRegistry $doctrine) {}

    public function someAction(Request $request) {
        // access Doctrine
        $this->doctrine;
    }
} 

您可以使用 EntityManagerInterface $entityManager:

public function delete(Request $request, Test $test, EntityManagerInterface $entityManager): Response
{
    if ($this->isCsrfTokenValid('delete'.$test->getId(), $request->request->get('_token'))) {
        $entityManager->remove($test);
        $entityManager->flush();
    }

    return $this->redirectToRoute('test_index', [], Response::HTTP_SEE_OTHER);
}

根据@yivi 的回答和documentation中提到的,你也可以按照下面的例子直接在你想要的方法中注入Doctrine\Persistence\ManagerRegistry

// src/Controller/ProductController.php
namespace App\Controller;

// ...
use App\Entity\Product;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Response;

class ProductController extends AbstractController
{
    /**
     * @Route("/product", name="create_product")
     */
    public function createProduct(ManagerRegistry $doctrine): Response
    {
        $entityManager = $doctrine->getManager();

        $product = new Product();
        $product->setName('Keyboard');
        $product->setPrice(1999);
        $product->setDescription('Ergonomic and stylish!');

        // tell Doctrine you want to (eventually) save the Product (no queries yet)
        $entityManager->persist($product);

        // actually executes the queries (i.e. the INSERT query)
        $entityManager->flush();

        return new Response('Saved new product with id '.$product->getId());
    }
}

在我的例子中,依靠构造函数或 method-based 自动装配不够灵活。

我有一个特性被许多控制器使用,它们定义了自己的自动装配。该特征提供了一种从数据库中获取一些数字的方法。我不想将特征的功能与控制器的自动装配设置紧密结合。

我创建了另一个特征,我可以将其包含在任何我需要访问 Doctrine 的地方。奖金部分?它仍然是一种合法的自动装配方法:

<?php

namespace App\Controller;

use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Symfony\Contracts\Service\Attribute\Required;

trait EntityManagerTrait
{
    protected readonly ManagerRegistry $managerRegistry;

    #[Required]
    public function setManagerRegistry(ManagerRegistry $managerRegistry): void
    {
        // @phpstan-ignore-next-line PHPStan complains that the readonly property is assigned outside of the constructor.
        $this->managerRegistry = $managerRegistry;
    }

    protected function getDoctrine(?string $name = null, ?string $forClass = null): ObjectManager
    {
        if ($forClass) {
            return $this->managerRegistry->getManagerForClass($forClass);
        }

        return $this->managerRegistry->getManager($name);
    }
}

然后


<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Entity\Foobar;

class SomeController extends AbstractController
{
    use EntityManagerTrait

    public function someAction()
    {
        $result = $this->getDoctrine()->getRepository(Foobar::class)->doSomething();
        // ...
    }
}

如果您像我一样有多名经理,您也可以使用 getDoctrine() 参数来获取正确的经理。