插件激活时批量添加商品价格超时失败的解决方案求助
解决插件激活时批量添加商品价格中途终止的问题
当前代码的核心问题是:循环内单次调用create()导致频繁数据库请求,累积耗时触发脚本超时;同时未处理异常、未优化数据加载,导致中断后无法定位原因。以下是具体解决方案:
1. 批量创建商品价格,减少数据库交互
将所有待创建的价格数据收集到数组,最后一次性提交,大幅降低数据库请求次数:
public function activate(ActivateContext $activateContext): void { // 获取规则,增加空值判断避免后续报错 $ruleRepository = $this->container->get('rule.repository'); $criteria = new Criteria(); $criteria->addFilter(new EqualsFilter('name', 'XYZ')); /** @var RuleEntity $rule */ $rule = $ruleRepository->search($criteria, $activateContext->getContext())->first(); if (!$rule) { return; } $ruleId = $rule->getId(); // 优化商品查询:只加载必要字段,减少内存占用 $productRepository = $this->container->get('product.repository'); $productCriteria = new Criteria(); $productCriteria->addAssociation('prices.' . Defaults::CURRENCY); $productCriteria->setFields(['id', 'prices']); $products = $productRepository->search($productCriteria, $activateContext->getContext()); $productPriceRepository = $this->container->get('product_price.repository'); $priceData = []; /** @var ProductEntity $product */ foreach ($products as $product) { $currencyPrice = $product->getCurrencyPrice(Defaults::CURRENCY); if (!$currencyPrice) { // 跳过无对应货币价格的商品 continue; } $priceData[] = [ 'id' => Uuid::randomHex(), 'productId' => $product->getId(), 'quantityStart' => 1, 'ruleId' => $ruleId, 'price' => [ [ 'currencyId' => Defaults::CURRENCY, 'gross' => $currencyPrice->getGross(), 'net' => $currencyPrice->getNet(), 'linked' => true ] ], ]; } // 批量提交创建请求 if (!empty($priceData)) { $productPriceRepository->create($priceData, $activateContext->getContext()); } }
2. 改用异步队列执行(适合大量商品场景)
如果商品数量较多,即使批量操作仍可能超时,建议将逻辑放到异步队列中,脱离激活流程的时间限制:
步骤1:创建消息类
// src/Message/AddProductPriceRuleMessage.php namespace YourPluginName\Message; use Symfony\Component\Messenger\MessageInterface; class AddProductPriceRuleMessage implements MessageInterface { private string $ruleId; private string $contextToken; public function __construct(string $ruleId, string $contextToken) { $this->ruleId = $ruleId; $this->contextToken = $contextToken; } public function getRuleId(): string { return $this->ruleId; } public function getContextToken(): string { return $this->contextToken; } }
步骤2:创建消息处理器
// src/MessageHandler/AddProductPriceRuleHandler.php namespace YourPluginName\MessageHandler; use YourPluginName\Message\AddProductPriceRuleMessage; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\Uuid\Uuid; use Shopware\Core\System\Currency\Defaults; use Symfony\Component\Messenger\Attribute\AsMessageHandler; #[AsMessageHandler] class AddProductPriceRuleHandler { private EntityRepository $productRepository; private EntityRepository $productPriceRepository; public function __construct( EntityRepository $productRepository, EntityRepository $productPriceRepository ) { $this->productRepository = $productRepository; $this->productPriceRepository = $productPriceRepository; } public function __invoke(AddProductPriceRuleMessage $message): void { $context = Context::createDefaultContext(); $context->setToken($message->getContextToken()); // 仅加载必要的商品字段 $criteria = new \Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria(); $criteria->addAssociation('prices.' . Defaults::CURRENCY); $criteria->setFields(['id', 'prices']); $products = $this->productRepository->search($criteria, $context); $priceData = []; foreach ($products as $product) { $currencyPrice = $product->getCurrencyPrice(Defaults::CURRENCY); if (!$currencyPrice) { continue; } $priceData[] = [ 'id' => Uuid::randomHex(), 'productId' => $product->getId(), 'quantityStart' => 1, 'ruleId' => $message->getRuleId(), 'price' => [ [ 'currencyId' => Defaults::CURRENCY, 'gross' => $currencyPrice->getGross(), 'net' => $currencyPrice->getNet(), 'linked' => true ] ], ]; } if (!empty($priceData)) { $this->productPriceRepository->create($priceData, $context); } } }
步骤3:修改激活方法发送异步消息
public function activate(ActivateContext $activateContext): void { $ruleRepository = $this->container->get('rule.repository'); $criteria = new Criteria(); $criteria->addFilter(new EqualsFilter('name', 'XYZ')); /** @var RuleEntity $rule */ $rule = $ruleRepository->search($criteria, $activateContext->getContext())->first(); if (!$rule) { return; } // 发送异步消息到队列 $messageBus = $this->container->get('messenger.bus.default'); $messageBus->dispatch(new AddProductPriceRuleMessage( $rule->getId(), $activateContext->getContext()->getToken() )); }
3. 添加错误日志排查问题
在代码中加入日志记录,便于定位中断的具体原因:
use Psr\Log\LoggerInterface; // 在插件类中注入LoggerInterface private LoggerInterface $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } // 在批量创建逻辑中捕获异常并记录 try { if (!empty($priceData)) { $productPriceRepository->create($priceData, $activateContext->getContext()); } } catch (\Exception $e) { $this->logger->error('批量创建商品价格失败', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; // 抛出异常让激活流程显示错误 }
4. 临时调整超时限制(不推荐长期使用)
如果以上方法仍无法解决,可临时调整PHP的max_execution_time配置,或Shopware后台的相关超时设置,但这只是应急方案,异步队列才是长期解决大量数据处理的最优选择。
内容的提问来源于stack exchange,提问作者M.E.
相关产品推荐
相关产品推荐

