如何让IN在EasyAdmin EntityFilter中实现AND逻辑而非OR
解决EasyAdmin中多实体筛选的AND逻辑需求
要实现筛选同时拥有多个指定活动的人员,默认的EntityFilter用的是OR逻辑(匹配任意一个选中活动就会返回),所以需要自定义Filter来实现AND逻辑。下面提供两种可行方案:
方案一:基于EXISTS子查询的自定义Filter
这种方式对每个选中的活动单独添加存在性检查,确保用户同时关联所有选中项。
1. 创建自定义Filter类
<?php namespace App\Filter; use EasyCorp\Bundle\EasyAdminBundle\Filter\EntityFilter; use Doctrine\ORM\QueryBuilder; use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto; use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto; use EasyCorp\Bundle\EasyAdminBundle\Dto\FilterDataDto; class EntityAndFilter extends EntityFilter { protected function buildQuery(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { $selectedEntities = $filterDataDto->getValue(); if (empty($selectedEntities)) { return; } $rootAlias = $queryBuilder->getRootAliases()[0]; $associationField = $fieldDto->getProperty(); // 对每个选中的活动,添加独立的EXISTS子查询 foreach ($selectedEntities as $index => $activity) { $subAlias = 'activity_sub_' . $index; $queryBuilder ->andWhere(sprintf('EXISTS (SELECT 1 FROM %s.%s %s WHERE %s.id = :activity_id_%d)', $rootAlias, $associationField, $subAlias, $subAlias, $index)) ->setParameter('activity_id_' . $index, $activity->getId()); } } }
2. 在CrudController中使用自定义Filter
use App\Filter\EntityAndFilter; use App\Entity\Activity; // ... public function configureFilters(Filters $filters): Filters { return $filters ->add(EntityAndFilter::new('activities', 'Activités') ->setFormTypeOptions([ 'class' => Activity::class, 'multiple' => true, 'choice_label' => 'name', // 替换为你的Activity实体显示字段 ])); }
方案二:基于GROUP BY + COUNT的筛选逻辑
这种方式通过分组统计用户关联的活动数量,确保数量等于选中的活动总数,从而实现“同时拥有所有选中项”的效果。
修改自定义Filter的buildQuery方法
protected function buildQuery(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { $selectedEntities = $filterDataDto->getValue(); if (empty($selectedEntities)) { return; } $rootAlias = $queryBuilder->getRootAliases()[0]; $associationField = $fieldDto->getProperty(); $selectedActivityIds = array_map(fn($activity) => $activity->getId(), $selectedEntities); $requiredCount = count($selectedActivityIds); $queryBuilder ->innerJoin(sprintf('%s.%s', $rootAlias, $associationField), 'a') ->where('a.id IN (:selected_activity_ids)') ->setParameter('selected_activity_ids', $selectedActivityIds) ->groupBy(sprintf('%s.id', $rootAlias)) ->having(sprintf('COUNT(DISTINCT a.id) = %d', $requiredCount)); }
原理说明
默认的EntityFilter内部使用OrX组合条件,所以选中多个值时会返回匹配任意一个的结果。而上述两种方案:
- 方案一通过多个
EXISTS子查询的AND组合,确保用户同时关联所有选中的活动; - 方案二通过分组计数,验证用户关联的活动包含所有选中项(去重计数等于选中数量)。
两种方案都能精准筛选出同时拥有“illustrateur/dessinateur”和“Animateur/intervenant”两个活动的人员。
内容的提问来源于stack exchange,提问作者tristan
相关产品推荐
相关产品推荐

