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

API Platform 2.6:如何向自定义PDF编码器传递模板路径配置选项

解决方案:在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:24:05