如何在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
相关产品推荐
相关产品推荐

