Twig渲染列表时如何校验已输出数据实现去重合并同项子元素
Twig支持通过模板内定义临时变量记录已输出内容实现去重,但这类分组聚合逻辑不建议放在模板层实现,优先在控制器层完成数据分组整理,模板只负责渲染即可,具体实现方案如下:
控制器层数据修正方案
你之前组装数据失败的原因是直接存入了getAdjustings()、getUnderActivities()返回的集合对象,没有展开遍历集合内的实体,也没有做分组、去重处理。按以下逻辑组装即可得到直接可用的嵌套结构:
// 初始化分组存储数组,键为adjusting名称,满足同名合并需求 $groupedAdjustings = []; foreach ($activity->getUnderActivities() as $underActivity) { foreach ($underActivity->getIndications() as $indice) { // 提前匹配状态对应的样式类,PHP版本低于8.0可替换为if-else判断 $statusClass = match($indice->getIndicationPostAdjusting()) { 'Green' => 'text-success', 'Orange' => 'text-warning', default => 'text-danger' }; foreach ($indice->getAdjustings() as $adjusting) { if ($adjusting === null) { continue; } // 若存在同名但实际不同的adjusting,可将键替换为$adjusting->getId()避免误合并 $adjKey = $adjusting->getName(); // 分组不存在时初始化结构 if (!isset($groupedAdjustings[$adjKey])) { $groupedAdjustings[$adjKey] = [ 'name' => $adjusting->getName(), 'children' => [] ]; } // 子项去重:拼接唯一键避免重复子项挂载 $childKey = $underActivity->getName() . '_' . $statusClass; if (!isset($groupedAdjustings[$adjKey]['children'][$childKey])) { $groupedAdjustings[$adjKey]['children'][$childKey] = [ 'name' => $underActivity->getName(), 'class' => $statusClass ]; } } } } // 重置children数组的索引,去掉去重用的自定义键 foreach ($groupedAdjustings as &$item) { $item['children'] = array_values($item['children']); } unset($item); // 传入模板渲染 return $this->render('对应模板路径.html.twig', [ 'groupedAdjustings' => $groupedAdjustings, // 其余原有传入参数 ]);
模板渲染代码
数据整理完成后,模板不需要多层嵌套循环和状态判断,直接遍历分组后的数据输出即可:
{% if groupedAdjustings is not empty %} <ul> {% for adj in groupedAdjustings %} <li>{{ adj.name }} <ul> {% for child in adj.children %} <li class="{{ child.class }}">{{ child.name }}</li> {% endfor %} </ul> </li> {% endfor %} </ul> {% else %} <p>Aucun aménagement n a été trouvée.</p> {% endif %}
Twig原生去重实现(不推荐)
如果受场景限制无法修改后端逻辑,可直接在Twig中定义数组存储已渲染的项标识,判断未输出过再渲染对应内容:
{# 初始化存储已渲染项标识的数组 #} {% set renderedAdj = [] %} {% set renderedChild = [] %} {% if activity.underActivities is not empty %} <ul> {% for underActivity in activity.underActivities %} {% for indice in underActivity.indications %} {% set statusClass = indice.indicationPostAdjusting == 'Green' ? 'text-success' : (indice.indicationPostAdjusting == 'Orange' ? 'text-warning' : 'text-danger') %} {% for adjusting in indice.adjustings %} {% if adjusting is not null %} {# 顶级项未渲染过时输出顶级项开头 #} {% if adjusting.name not in renderedAdj %} {% set renderedAdj = renderedAdj|merge([adjusting.name]) %} <li>{{ adjusting.name }} <ul> {% endif %} {# 子项未渲染过时输出子项 #} {% set childKey = underActivity.name ~ '_' ~ adjusting.name %} {% if childKey not in renderedChild %} {% set renderedChild = renderedChild|merge([childKey]) %} <li class="{{ statusClass }}">{{ underActivity.name }}</li> {% endif %} {# 注意:该方案需要额外处理标签闭合、同组边界判断,维护成本极高,容易出现结构错误 #} {% endif %} {% endfor %} {% endfor %} {% endfor %} </ul> {% else %} <p>Aucun aménagement n a été trouvée.</p> {% endif %}
注意:Twig层直接实现分组去重需要处理大量边界判断,标签闭合逻辑复杂,后续需求调整极易出现bug,生产环境优先选择后端提前分组的方案。
内容的提问来源于stack exchange,提问作者Noé Femeland
相关产品推荐
相关产品推荐

