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

Shopware 6应用:Twig脚本引发内存溢出问题排查

Shopware 6 产品列表徽章脚本内存溢出问题排查与优化

我们编写的Shopware 6产品列表徽章脚本,在产品数量较多时会触发PHP内存不足导致系统崩溃,日志明确提示内存耗尽。虽然已经做了批量获取媒体资源的优化,但代码仍存在几个关键性能问题,导致内存占用过高:

现有代码的核心问题

  • 重复遍历与计算冗余:代码中两次完整遍历所有产品,每次遍历还要重复处理产品的分类树、生成徽章列表并排序,大量重复操作会急剧消耗CPU和内存。
  • 频繁数组合并的内存损耗:Twig的merge操作会创建新数组,像逐个添加产品到products数组、每次生成badgeList时的合并,都会产生大量临时数组,导致内存碎片化,累积占用远超预期的内存。
  • 分类数据重复处理:每个产品的分类树在收集媒体ID和绑定徽章两个阶段被重复遍历,没有复用之前的计算结果。

优化后的代码

{# @var services \Shopware\Core\Framework\Script\ServiceStubs #}
{% set page = hook.page %}
{# @var page \Shopware\Storefront\Page\Product\ProductPage #}

{% set products = [] %}  

{% if hook.page.cmsPage.type === 'product_list' %}
    {% foreach hook.page.cmsPage.sections as section %}
        {% foreach section.blocks as sectionBlock %}
            {% if sectionBlock.type !== 'product-listing' %}
                {% continue %}
            {% endif %}

            {% foreach sectionBlock.slots as slot %}
                {% if slot.type !== 'product-listing' %}
                    {% continue %}
                {% endif %}
                {# 直接获取完整产品集合,避免逐个merge数组 #}
                {% set products = slot.data.listing.entities %}
            {% endforeach %}
        {% endforeach %}
    {% endforeach %}
{% endif %}

{% if products is not empty %}
    {# 收集所有分类ID并去重,减少分类查询量 #}
    {% set categoryIds = products|reduce((carry, product) => carry|merge(product.categoryTree), [])|unique %}
    {% set categories = services.repository.search('category', {'ids': categoryIds}) %}

    {# 预生成分类ID到徽章信息的映射表,避免重复处理分类 #}
    {% set categoryBadgeMap = {} %}
    {% foreach categories.entities as category %}
        {% if category.customFields.minds_cd_show_badge %}
            {% set categoryBadgeMap = categoryBadgeMap|merge({
                category.id: {
                    'priority': category.customFields.minds_cd_badge_priority,
                    'mediaId': category.customFields.minds_cd_badge
                }
            }) %}
        {% endif %}
    {% endforeach %}

    {# 一次性处理所有产品的徽章逻辑,收集媒体ID并建立产品-媒体ID映射 #}
    {% set productBadgeMap = {} %}
    {% set mediaIds = [] %}
    {% foreach products as product %}
        {% set badgeList = [] %}
        {% foreach product.categoryTree as categoryId %}
            {% if categoryBadgeMap[categoryId] is defined %}
                {% set badgeList = badgeList|merge([categoryBadgeMap[categoryId]]) %}
            {% endif %}
        {% endforeach %}

        {% if badgeList is not empty %}
            {% set badge = (badgeList|sort((a, b) => a.priority <=> b.priority))|last %}
            {% set productBadgeMap = productBadgeMap|merge({product.id: badge.mediaId}) %}
            {% set mediaIds = mediaIds|merge([badge.mediaId]) %}
        {% endif %}
    {% endforeach %}

    {# 媒体ID去重后批量查询,减少内存占用 #}
    {% set mediaCollection = services.repository.search('media', {'ids': mediaIds|unique}) %}

    {# 根据预先生成的映射绑定徽章到产品 #}
    {% foreach products as product %}
        {% if productBadgeMap[product.id] is defined %}
            {% set badgeMedia = mediaCollection.get(productBadgeMap[product.id]) %}
            {% do product.addArrayExtension('mcb', {
                'badge': badgeMedia
            }) %}
        {% endif %}
    {% endforeach %}
{% endif %}

关键优化点

  • 减少数组合并操作:直接获取产品集合而非逐个merge,分类ID先去重再查询,避免不必要的内存开销。
  • 预生成分类徽章映射:提前把带徽章的分类信息存入映射表,每次处理产品时直接查表,省去重复检查分类自定义字段的操作。
  • 复用计算结果:一次性生成产品与媒体ID的映射关系,避免两次遍历产品时重复生成徽章列表和排序。
  • 媒体ID去重:收集媒体ID时先去重,减少媒体查询的数量,降低内存占用。

内容的提问来源于stack exchange,提问作者Leon Schwanitz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 18:05:29