4

是否可以通过某种 walker 运行所有学说查询,以便我可以根据当前用户的凭据修改查询?理想情况下,我不必在每个查询中为自定义 walker 显式调用 setHint,因为这会限制我将当前 SecurityContext 传递给 walker 的能力。

另外,我不想使用 Doctrine 过滤器,因为我无法使用过滤器修改连接条件,并且我将被迫使用“IN”子句,这会严重影响性能

目前,我正在使用一项根据用户的凭据修改 QueryBuilder 的服务,但这变得乏味,因为我每次创建新的 QueryBuilder 时都需要调用该服务,而且当存储库发挥作用时更加痛苦(因为我需要将服务注入到需要修改查询的每个存储库中。

希望我已经足够清楚地解释了这一点。感谢任何反馈!

4

2 回答 2

1

我想我已经解决了我自己的问题。如果其他人有更优雅的方式来实现这些结果,请随时解释。为了修改我的所有查询,我创建了一个自定义 EntityManager 和自定义 EntityRepository。

在我的自定义 EntityManager 中,我覆盖了 2 个方法。create() 和 getRepository()

public static function create($conn, Configuration $config, EventManager $eventManager = null)
{
    if ( ! $config->getMetadataDriverImpl()) {
        throw ORMException::missingMappingDriverImpl();
    }

    switch (true) {
        case (is_array($conn)):
            $conn = \Doctrine\DBAL\DriverManager::getConnection(
                $conn, $config, ($eventManager ?: new EventManager())
            );
            break;

        case ($conn instanceof Connection):
            if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
                 throw ORMException::mismatchedEventManager();
            }
            break;

        default:
            throw new \InvalidArgumentException("Invalid argument: " . $conn);
    }

    return new MyCustomEntityManager($conn, $config, $conn->getEventManager());
}

此方法中唯一改变的是我返回了我自己的 EntityManger( MyCustomEntityManager )。然后,我将 getRepository 方法覆盖如下:

public function getRepository($entityName)
{
    $entityName = ltrim($entityName, '\\');

    if (isset($this->repositories[$entityName])) {
        return $this->repositories[$entityName];
    }

    $metadata = $this->getClassMetadata($entityName);
    $repositoryClassName = $metadata->customRepositoryClassName;

    if ($repositoryClassName === null) {
        $repositoryClassName = "Acme\DemoBundle\Doctrine\ORM\MyCustomEntityRepository";
    }

    $repository = new $repositoryClassName($this, $metadata);

    $this->repositories[$entityName] = $repository;

    return $repository;
}

在这里,我也只修改了一行。我没有依赖 DBAL 配置来检索默认的 $repositoryClassName,而是指定了我自己的默认存储库Acme\DemoBundle\Doctrine\ORM\MyCustomEntityRepository

一旦你创建了你自己的自定义 EntityRepository,天空就是极限。您可以将服务注入存储库(我目前使用 JMS Di 注释,如下所述),或在 createQueryBuilder 方法中对 QueryBuilder 执行自定义操作,如下所示:

use JMS\DiExtraBundle\Annotation as DI;

class MyCustomEntityRepository extends EntityRepository
{
    private $myService;

    public function createQueryBuilder($alias)
    {
         $queryBuilder = parent::createQueryBuilder($alias);

          /** INSERT CUSTOM CODE HERE **/

          return $queryBuilder;
    }

    /**
    * @DI\InjectParams({
    *     "myService" = @DI\Inject("my_service_id")
    * })
    */
    public function setMyService(MyServiceInterface $myService)
    {
        $this->myService = $myService;
    }
}

创建自己的 EntityRepository 后,您应该让所有需要此自定义功能的存储库扩展 MyCustomEntityRepository。您甚至可以更进一步,创建自己的 QueryBuilder 来进一步扩展它。

于 2012-11-28T15:49:33.190 回答
1

你可以编写一个自定义的 AST Walker并设置你的应用程序使用这个 walker 来处理所有带有defaultQueryHint(Doctrine 2.5 新特性)配置选项的查询:

<?php
/** @var \Doctrine\ORM\EntityManager $em */
$em->getConfiguration()->setDefaultQueryHint(
    Query::HINT_CUSTOM_TREE_WALKERS,
    ['YourWalkerFQClassName']
)
于 2015-05-04T09:50:37.663 回答