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

插件激活时批量添加商品价格超时失败的解决方案求助

解决插件激活时批量添加商品价格中途终止的问题

当前代码的核心问题是:循环内单次调用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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 15:45:33