
本文详解如何在 Twig 模板中将扁平数组按 object.category 字段重新组织为嵌套结构,避免 merge 覆盖同名键的问题,正确实现“一类别多条目”的分组效果。
本文详解如何在 twig 模板中将扁平数组按 `object.category` 字段重新组织为嵌套结构,避免 `merge` 覆盖同名键的问题,正确实现“一类别多条目”的分组效果。
在 Twig 中对数组进行分组(如按分类聚合)是一个常见需求,但直接使用 merge 函数极易踩坑:由于 Twig 的 merge 将关联数组视为「键唯一映射」,重复键(如多次出现 "Cat1")会导致前序值被后序值完全覆盖,最终仅保留最后一个元素——这正是提问者遇到“两条记录被删除”的根本原因。
要实现真正的分组聚合(即每个 category 对应一个包含所有匹配项的数组),必须手动维护目标结构。核心思路是:
- 初始化一个空数组(建议用 [] 而非 {},更利于后续索引操作);
- 遍历源数据,对每个元素检查其 category 是否已在目标数组中定义;
- 若未定义,先初始化该 category 对应的空数组;
- 再将当前元素追加到该 category 的子数组中。
以下是推荐的健壮实现:
{% set grouped = [] %}
{% for item in sourceElements %}
{% set category = item.object.category %}
{# 若该 category 尚未初始化,则创建空数组 #}
{% if not attribute(grouped, category) is defined %}
{% set grouped = grouped|merge({ (category): [] }) %}
{% endif %}
{# 将当前 item 追加到对应 category 的数组中 #}
{% set grouped = grouped|merge({
(category): grouped[category]|merge([item])
}) %}
{% endfor %}
{# 使用示例:遍历分组结果 #}
{% for category, items in grouped %}
<h3>{{ category }}</h3>
-
{% for item in items %}
- {{ item.object.name }} ({{ item.type }}) {% endfor %}
✅ 关键要点说明:
- 使用 attribute(grouped, category) 检测键是否存在,比 grouped[category] is defined 更安全(避免未定义键触发警告);
- grouped[category]|merge([item]) 确保每次追加新元素,而非替换整个子数组;
- 初始化时用 [](索引数组)而非 {}(关联数组),可避免 Twig 对空对象的隐式类型转换问题;
- ⚠️ 强烈建议:业务逻辑层面的数组重组应移至控制器或服务层(如 PHP 中使用 array_reduce 或 foreach 构建),模板层仅负责展示。Twig 并非为复杂数据处理设计,模板中嵌入多层 merge 会显著降低可读性、可维护性与性能。
若需进一步优化,可结合自定义 Twig 扩展(如 group_by 过滤器),从根本上解耦逻辑与视图。











