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

如何正确统计Symfony+Twig模板的使用次数?

解决方案:基于请求作用域的Twig片段计数器

核心思路

借助Symfony的RequestStack存储片段计数,计数仅在当前请求生命周期内有效——既避免全局变量污染,也不会像Session那样跨请求保留数据,完美适配单页内片段次数统计的需求。

步骤1:创建自定义Twig扩展

编写Twig扩展类,通过注入RequestStack来管理每个请求内的片段计数:

namespace App\Twig;

use Symfony\Component\HttpFoundation\RequestStack;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class FragmentCounterExtension extends AbstractExtension
{
    public function __construct(private readonly RequestStack $requestStack)
    {
    }

    public function getFunctions(): array
    {
        return [
            new TwigFunction('fragment_counter', [$this, 'getCounter']),
            new TwigFunction('fragment_has_been_used', [$this, 'hasBeenUsed']),
            new TwigFunction('fragment_get_total', [$this, 'getTotal']),
        ];
    }

    /**
     * 获取指定片段的当前计数(调用时自动递增)
     */
    public function getCounter(string $fragmentName): int
    {
        $request = $this->requestStack->getCurrentRequest();
        if (!$request) {
            return 1; // CLI环境下默认返回1
        }

        $counterKey = sprintf('twig_fragment_counter_%s', $fragmentName);
        $currentCount = $request->attributes->get($counterKey, 0);
        $currentCount++;
        $request->attributes->set($counterKey, $currentCount);

        return $currentCount;
    }

    /**
     * 检查指定片段是否已经被使用过
     */
    public function hasBeenUsed(string $fragmentName): bool
    {
        $request = $this->requestStack->getCurrentRequest();
        if (!$request) {
            return false;
        }

        $counterKey = sprintf('twig_fragment_counter_%s', $fragmentName);
        return $request->attributes->get($counterKey, 0) > 0;
    }

    /**
     * 获取指定片段的总使用次数(不递增计数)
     */
    public function getTotal(string $fragmentName): int
    {
        $request = $this->requestStack->getCurrentRequest();
        if (!$request) {
            return 0;
        }

        $counterKey = sprintf('twig_fragment_counter_%s', $fragmentName);
        return $request->attributes->get($counterKey, 0);
    }
}

步骤2:在Twig片段中使用

示例1:生成带序号的CSS ID(如YouTube视频片段)

在fragment/youtube_video.html.twig中:

{% set counter = fragment_counter('youtube_video') %}
<div class="fragment_video" id="fragment_video_{{ counter }}">
    <iframe src="https://www.youtube.com/embed/{{ youtube_video.id }}" 
            title="{{ youtube_video.title }}"
            lang="{{ youtube_video.language }}">
    </iframe>
</div>

示例2:限制片段仅能使用一次(如移动端导航)

在fragment/mobile_nav.html.twig开头添加判断:

{% if fragment_has_been_used('mobile_nav') %}
    {% do throw new Exception('移动端导航片段只能在页面中使用一次') %}
{% endif %}
{# 标记该片段已被使用 #}
{% set unused = fragment_counter('mobile_nav') %}

<nav id="mobile_nav" class="mobile-nav">
    {# 导航内容 #}
</nav>

方案优势

  • 请求隔离:计数存储在当前请求的attributes中,请求结束后自动销毁,完全不会影响其他请求。
  • 符合Symfony规范:依赖注入RequestStack而非全局变量,代码可测试性、可维护性更强。
  • 场景覆盖全:配套的fragment_has_been_used和fragment_get_total函数,能满足次数统计、重复使用限制等多种需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:25:39