API Platform 2.6:如何向自定义PDF编码器传递模板路径配置选项
在API Platform 2.6里,直接在ApiResource的操作配置中添加自定义参数(比如你尝试的export数组)确实会被框架过滤,无法直接传递到编码器的context中。不过有几种简单且可行的方法来实现动态传递模板路径的需求,下面是最常用的几种方案:
方案1:利用官方的serializer_context传递参数(推荐)
API Platform原生支持通过serializer_context配置项传递序列化/反序列化相关的参数,这些参数会被自动合并到编码器的context数组中,这是最直接且无需额外代码的方案。
修改资源配置
直接将模板路径配置到操作的serializer_context中:
// src/Resource/MyResource.php #[ApiResource( itemOperations: [ 'export' => [ 'method' => 'GET', 'path' => '/myResource/{id}/export', 'requirements' => ['id' => '\d+'], 'output' => MyResourceView::class, 'serializer_context' => [ 'export' => [ 'template' => 'pdf/template.html.twig', ] ] ], ], )] class MyResource { public int $id; /** Other Attributes **/ }
验证编码器逻辑
你之前修改的PdfEncoder代码完全可以直接使用,因为serializer_context里的参数会被自动传入encode方法的$context参数:
// src/Encoder/PdfEncoder.php public function encode($data, $format, array $context = []): string { $template = $context['export']['template']?? null; if (!$template){ throw new \Exception('Twig template is not defined'); } $content = $this->twig->render($template, ['data' => $data]); $filename = sprintf('/tmp/%s.pdf', uniqid()); $this->pdf->generateFromHtml($content, $filename); return file_get_contents($filename); }
这个方案的优势是完全利用API Platform的原生机制,不需要额外编写处理器或事件订阅者,适合大多数简单场景。
方案2:自定义操作处理器传递配置
如果需要更灵活的逻辑(比如动态生成模板路径、基于请求参数调整配置),可以通过自定义操作处理器来传递配置到编码器的context中。
创建自定义处理器
// src/Processor/PdfExportProcessor.php namespace App\Processor; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; use ApiPlatform\Core\State\ProcessorInterface; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; class PdfExportProcessor implements ProcessorInterface { private $innerProcessor; private $resourceMetadataFactory; public function __construct(ProcessorInterface $innerProcessor, ResourceMetadataFactoryInterface $resourceMetadataFactory) { $this->innerProcessor = $innerProcessor; $this->resourceMetadataFactory = $resourceMetadataFactory; } public function process($data, $operation, array $uriVariables = [], array $context = []) { // 从操作配置中获取自定义的export参数 $exportConfig = $operation->getExtraProperties()['export'] ?? []; if (empty($exportConfig['template'])) { throw new BadRequestHttpException('PDF template is not configured for this operation'); } // 将配置添加到序列化context中 $context['serializer_context']['export'] = $exportConfig; // 调用原生处理器完成后续逻辑 return $this->innerProcessor->process($data, $operation, $uriVariables, $context); } }
注册处理器并配置资源
在config/services.yaml中注册处理器:
# config/services.yaml services: App\Processor\PdfExportProcessor: arguments: $innerProcessor: '@api_platform.doctrine.orm.state.item_processor' $resourceMetadataFactory: '@api_platform.metadata.resource.metadata_factory'
然后在资源配置中指定这个处理器:
// src/Resource/MyResource.php #[ApiResource( itemOperations: [ 'export' => [ 'method' => 'GET', 'path' => '/myResource/{id}/export', 'requirements' => ['id' => '\d+'], 'output' => MyResourceView::class, 'processor' => \App\Processor\PdfExportProcessor::class, 'export' => [ 'template' => 'pdf/template.html.twig', ] ], ], )] class MyResource { // ... }
方案3:事件订阅者动态注入配置
如果需要给多个导出操作统一添加配置逻辑,可以通过事件订阅者监听序列化前的事件,将操作配置中的参数注入到context中。
创建事件订阅者
// src/EventSubscriber/PdfExportContextSubscriber.php namespace App\EventSubscriber; use ApiPlatform\Core\EventListener\EventPriorities; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ViewEvent; use Symfony\Component\HttpKernel\KernelEvents; class PdfExportContextSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { return [ KernelEvents::VIEW => ['addExportTemplateToContext', EventPriorities::PRE_SERIALIZE], ]; } public function addExportTemplateToContext(ViewEvent $event) { $request = $event->getRequest(); $operation = $request->attributes->get('_api_operation'); // 只处理带有export配置的操作 if (!$operation || !isset($operation['export']['template'])) { return; } // 获取当前的序列化context并添加模板配置 $serializerContext = $request->attributes->get('_api_serializer_context', []); $serializerContext['export'] = $operation['export']; $request->attributes->set('_api_serializer_context', $serializerContext); } }
Symfony会自动注册这个订阅者(如果你的项目开启了自动配置),不需要额外的服务配置。之后你就可以在资源操作中直接添加export配置,订阅者会自动将其注入到编码器的context中。
内容的提问来源于stack exchange,提问作者beta-developper

