Symfony 4.1命令中执行路由:解决PDF生成超时问题
在Symfony 4.1命令行中执行路由生成PDF
完全可以通过RouterInterface和HttpKernelInterface实现命令行环境下执行路由的需求,核心思路是模拟HTTP请求流程,让内核处理路由对应的控制器逻辑,以下是具体实现方案:
实现步骤
1. 注入核心服务
在自定义命令类的构造函数中注入RouterInterface(用于生成路由URI、设置请求上下文)、HttpKernelInterface(用于处理模拟的HTTP请求),以及你需要的其他服务(如文件存储、邮件服务)。
2. 手动设置路由上下文
命令行环境没有默认的HTTP请求上下文,需要手动指定请求的scheme(http/https)和host,确保路由能生成正确的URI。
3. 生成路由并处理请求
通过路由生成目标URI,创建Request对象后交给内核处理,获取响应中的PDF内容。
4. 后续处理
将PDF内容保存到服务器指定目录,再发送邮件通知用户下载链接。
代码示例
// src/Command/GeneratePersonPdfCommand.php namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; class GeneratePersonPdfCommand extends Command { protected static $defaultName = 'app:generate-person-pdf'; private $router; private $httpKernel; private $filesystem; private $mailer; private $pdfStorageDir; public function __construct( RouterInterface $router, HttpKernelInterface $httpKernel, Filesystem $filesystem, MailerInterface $mailer, string $pdfStorageDir ) { parent::__construct(); $this->router = $router; $this->httpKernel = $httpKernel; $this->filesystem = $filesystem; $this->mailer = $mailer; $this->pdfStorageDir = $pdfStorageDir; } protected function configure() { $this ->addArgument('personId', InputArgument::REQUIRED, '要生成PDF的用户ID') ->setDescription('通过路由生成用户PDF并发送下载通知邮件'); } protected function execute(InputInterface $input, OutputInterface $output): int { $personId = $input->getArgument('personId'); // 设置路由上下文,确保生成正确的URI $this->router->getContext()->setScheme('https'); $this->router->getContext()->setHost('mysite.com'); // 生成目标路由的URI try { $uri = $this->router->generate('print_person', ['id' => $personId]); } catch (\Exception $e) { $output->writeln(sprintf('路由生成失败:%s', $e->getMessage())); return Command::FAILURE; } // 创建模拟HTTP请求并交由内核处理 $request = Request::create($uri, 'GET'); $response = $this->httpKernel->handle($request, HttpKernelInterface::MASTER_REQUEST, false); // 检查响应状态 if (!$response->isSuccessful()) { $output->writeln(sprintf('请求处理失败,状态码:%d', $response->getStatusCode())); return Command::FAILURE; } // 保存PDF到服务器 $pdfFilename = sprintf('person_%d.pdf', $personId); $pdfPath = $this->pdfStorageDir . '/' . $pdfFilename; $this->filesystem->dumpFile($pdfPath, $response->getContent()); // 发送下载通知邮件 $downloadUrl = sprintf('https://mysite.com/download/pdf/%s', $pdfFilename); $email = (new Email()) ->from('noreply@mysite.com') ->to('user@example.com') // 替换为实际用户邮箱 ->subject('你的PDF已生成') ->text(sprintf('点击链接下载PDF:%s', $downloadUrl)); $this->mailer->send($email); $output->writeln(sprintf('PDF生成完成,路径:%s', $pdfPath)); return Command::SUCCESS; } }
注意事项
- 认证处理:如果
print_person路由需要用户认证,命令行环境没有默认会话,需手动设置认证令牌(通过TokenStorageInterface),或在控制器中添加逻辑允许命令行请求跳过认证(比如检查$request->isCli())。 - 路径配置:
pdfStorageDir需要在services.yaml中配置为参数,示例:parameters: pdf_storage_dir: '%kernel.project_dir%/public/pdf' - 异常处理:添加足够的异常捕获逻辑(如文件写入失败、邮件发送失败),确保任务能返回正确状态码,方便cron日志排查问题。
- 资源控制:若PDF生成耗时极长,需确保服务器资源充足,可添加任务锁机制避免重复执行。
内容的提问来源于stack exchange,提问作者Szatmari Daniel
相关产品推荐
相关产品推荐

