如何扩展Api-Platform GetCollection的JsonLd响应以添加额外数据?
在Api-Platform 3的GetCollection响应中添加额外数据
针对你的需求,有两种简洁可靠的方案可以扩展JsonLD集合响应,添加时间段内的交易总金额:
方案一:使用View事件监听器(直接修改响应)
通过监听KernelEvents::VIEW事件,在响应序列化完成后,追加自定义数据到返回结果中。
实现代码
// src/EventListener/TransactionCollectionTotalListener.php namespace App\EventListener; use ApiPlatform\Symfony\EventListener\EventPriorities; use App\Entity\Transaction; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ViewEvent; use Symfony\Component\HttpKernel\KernelEvents; #[AsEventListener(event: KernelEvents::VIEW, priority: EventPriorities::POST_SERIALIZE)] class TransactionCollectionTotalListener implements EventSubscriberInterface { public function __construct(private EntityManagerInterface $em) {} public static function getSubscribedEvents(): array { return [ KernelEvents::VIEW => ['addTotalAmount', EventPriorities::POST_SERIALIZE], ]; } public function addTotalAmount(ViewEvent $event): void { $request = $event->getRequest(); $resourceClass = $request->attributes->get('_api_resource_class'); $operationName = $request->attributes->get('_api_operation_name'); // 仅对Transaction实体的GetCollection操作生效 if ($resourceClass !== Transaction::class || $operationName !== 'get_collection') { return; } $response = $event->getResponse(); if (!$response) { return; } // 构建总金额查询,复用请求中的过滤参数 $qb = $this->em->createQueryBuilder(); $qb->select('SUM(t.amount) as totalAmount') ->from(Transaction::class, 't'); // 应用日期范围过滤(匹配Api-Platform的DateFilter参数格式) $dateFilters = $request->query->get('date', []); if (isset($dateFilters['after'])) { $qb->andWhere('t.date >= :after') ->setParameter('after', new \DateTime($dateFilters['after'])); } if (isset($dateFilters['before'])) { $qb->andWhere('t.date <= :before') ->setParameter('before', new \DateTime($dateFilters['before'])); } // 执行查询获取总金额,处理空结果 $totalAmount = $qb->getQuery()->getSingleScalarResult() ?: 0.0; // 修改响应内容,追加总金额字段 $responseData = json_decode($response->getContent(), true); $responseData['totalAmount'] = (float)$totalAmount; $response->setContent(json_encode($responseData)); } }
方案二:自定义JsonLD规范化器(贴合Api-Platform序列化流程)
通过自定义规范化器,在实体集合序列化为JsonLD的过程中注入额外数据,这种方式更符合Api-Platform的设计规范。
实现代码
// src/Serializer/TransactionCollectionNormalizer.php namespace App\Serializer; use ApiPlatform\Serializer\SerializerContextBuilderInterface; use App\Entity\Transaction; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; class TransactionCollectionNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface { use NormalizerAwareTrait; public function __construct( private EntityManagerInterface $em, private SerializerContextBuilderInterface $contextBuilder ) {} public function supportsNormalization($data, string $format = null, array $context = []): bool { // 校验是否为Transaction集合的GetCollection操作,且输出格式为JsonLD return is_iterable($data) && $context['resource_class'] === Transaction::class && $context['operation_name'] === 'get_collection' && $format === 'jsonld'; } public function normalize($object, string $format = null, array $context = []): array { // 先通过默认规范化器生成标准JsonLD集合数据 $normalizedData = $this->normalizer->normalize($object, $format, $context); $request = $context['request'] ?? null; if (!$request) { return $normalizedData; } // 构建总金额查询 $qb = $this->em->createQueryBuilder(); $qb->select('SUM(t.amount) as totalAmount') ->from(Transaction::class, 't'); // 应用日期过滤参数 $dateFilters = $request->query->get('date', []); if (isset($dateFilters['after'])) { $qb->andWhere('t.date >= :after') ->setParameter('after', new \DateTime($dateFilters['after'])); } if (isset($dateFilters['before'])) { $qb->andWhere('t.date <= :before') ->setParameter('before', new \DateTime($dateFilters['before'])); } $totalAmount = $qb->getQuery()->getSingleScalarResult() ?: 0.0; // 追加总金额到规范化后的数据中 $normalizedData['totalAmount'] = (float)$totalAmount; return $normalizedData; } }
注册规范化器
在config/services.yaml中添加服务标签,确保优先级高于默认的JsonLD规范化器:
services: App\Serializer\TransactionCollectionNormalizer: arguments: $em: '@doctrine.orm.entity_manager' $contextBuilder: '@api_platform.serializer.context_builder' tags: - { name: serializer.normalizer, priority: 10 }
进阶优化:自动复用Api-Platform过滤器逻辑
如果你的实体配置了多种过滤器(如搜索、排序),手动处理过滤参数容易出错,可以通过Api-Platform的FilterLocator自动应用所有配置的过滤器:
// 在构造函数中注入相关依赖 use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use ApiPlatform\Symfony\Bridge\Doctrine\Orm\Filter\FilterLocatorInterface; public function __construct( private EntityManagerInterface $em, private FilterLocatorInterface $filterLocator, private ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory ) {} // 在构建查询时替换手动过滤逻辑 $resourceMetadata = $this->resourceMetadataFactory->create(Transaction::class); $operation = $resourceMetadata->getOperation('get_collection'); $filters = $operation->getFilters() ?? []; foreach ($filters as $filterId) { $filter = $this->filterLocator->get($filterId); if ($filter) { $filter->apply($qb, $operation, ['request' => $request]); } }
这样可以确保总金额的查询和Api-Platform的集合查询使用完全一致的过滤条件,避免逻辑不一致的问题。
内容的提问来源于stack exchange,提问作者Armandelis
相关产品推荐
相关产品推荐

