You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Symfony事件订阅者中启用ORM过滤器?过滤器未生效

Symfony中Doctrine Filter启用后未生效问题

在Symfony的EventSubscriber中启用Doctrine Filter时遇到异常:过滤器的查询条件未被应用。已通过$this->entityManager->getFilters()->getEnabledFilters()确认过滤器已激活,且参数设置成功,但Profiler显示的查询未添加Filter中定义的WHERE子句,完全没有过滤器生效的迹象。

我们使用了API-Platform,该过滤器针对Entity\Emote.php的get或getCollection()操作,曾怀疑是事件优先级问题但无法验证。

相关代码

EventSubscriber\EmoteTierFilterSubscriber.php

<?php

namespace App\EventSubscriber;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class EmoteTierFilterSubscriber implements EventSubscriberInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private Security $security,
    ) {
    }

    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::REQUEST => ['activateFilter', -10],
        ];
    }


    public function activateFilter(RequestEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }

       $user = $this->security->getUser();
       $filter = $this->entityManager->getFilters()->enable('emote_tier_filter');

        if ($user && $tier = $user->getChannelFollowTier()) {
            $filter->setParameter('tier', $tier);
        } else {
            $filter->setParameter('tier', 'NULL');
        }
    }
}

Doctrine\Filter\EmoteTierFilter.php

<?php

namespace App\Doctrine\Filter;

use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;

class EmoteTierFilter extends SQLFilter
{
    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
    {
        if ($targetEntity->getReflectionClass()->getName() !== 'App\Entity\Emote') {
            return '';
        }

        try {
            $tier = $this->getParameter('tier');
        } catch (\InvalidArgumentException $e) {
            return '';
        }

        if ($tier === 'NULL') {
            return sprintf('(%s.tier IS NULL OR %s.tier = \'0000\')',
                $targetTableAlias,
                $targetTableAlias
            );
        }

        // Cast both sides to integer for comparison
        return sprintf('(%s.tier IS NULL OR %s.tier = \'0000\' OR CAST(%s.tier AS INTEGER) <= %d)',
            $targetTableAlias,
            $targetTableAlias,
            $targetTableAlias,
            (int)$tier
        );
    }
}

doctrine.yaml

doctrine:
  dbal:
    url: "%env(resolve:DATABASE_URL)%"

    # IMPORTANT: You MUST configure your server version,
    # either here or in the DATABASE_URL env var (see .env file)
    #server_version: '15'

    profiling_collect_backtrace: "%kernel.debug%"
  orm:
    filters:
      emote_tier_filter:
        class: App\Doctrine\Filter\EmoteTierFilter
        enabled: false

..

Entity\Emote.php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use App\Repository\EmoteRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: EmoteRepository::class)]
#[ORM\HasLifecycleCallbacks]
#[ApiResource(
    operations: [
        new GetCollection(security: null),
        new Get(security: null),
        new Post(security: "is_granted('ROLE_ADMIN')"),
        new Patch(security: "is_granted('ROLE_ADMIN')"),
    ],
    paginationEnabled: false
)]
#[ApiFilter(SearchFilter::class, properties: ['category' => 'exact', 'name' => 'partial', 'tier' => 'exact'])]
class Emote
{

...

Profiler中的查询

SELECT e0_.id AS id_0, e0_.vendor_id AS vendor_id_1, e0_.name AS name_2, e0_.images AS images_3, e0_.format AS format_4, e0_.scale AS scale_5, e0_.themes AS themes_6, e0_.added_at AS added_at_7, e0_.category AS category_8, e0_.tier AS tier_9 FROM emote e0_ ORDER BY e0_.id ASC

Parameters:
[]

排查建议

  • 调整事件优先级:当前订阅者绑定KernelEvents::REQUEST的优先级为-10,API-Platform的查询构建逻辑可能更早执行。尝试将优先级调高(比如设为100),确保过滤器在查询生成前完成启用。
  • 验证EntityManager实例一致性:确认EventSubscriber注入的EntityManager和API-Platform使用的是同一个实例。可以在订阅者的activateFilter方法和EmoteRepository的查询方法中,分别输出spl_object_hash($this->entityManager)进行对比。
  • 优化实体类名匹配逻辑:将$targetEntity->getReflectionClass()->getName()替换为$targetEntity->name,后者直接获取Doctrine映射的实体类名,避免反射可能带来的类名匹配问题。
  • 改用API-Platform查询扩展:API-Platform有专门的QueryExtensionInterface,用于在其查询生命周期中添加过滤条件,比原生Doctrine Filter更适配API-Platform的执行流程。
  • 检查参数传递有效性:在EmoteTierFilter::addFilterConstraint方法开头添加var_dump($this->getParameter('tier')),确认参数是否正确传递到过滤器中。
  • 更换事件触发点:尝试改用KernelEvents::CONTROLLER事件(优先级默认0),或者API-Platform的ViewEvent来启用过滤器,确保时机晚于路由匹配但早于查询执行。

内容的提问来源于stack exchange,提问作者Sordep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 12:57:03