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

Symfony 3.4 - 每页加载时自定义视图渲染优化方案问询

嘿,针对你在Symfony 3.4大型应用里遇到的用户自定义渲染优化问题,结合你提到的动态菜单收藏这类没法用Twig角色搞定的场景,我整理了几个实用的优化方案:

方案1:集中封装用户上下文,优化现有监听器架构

目前你用多个监听器处理不同板块的渲染逻辑,容易造成代码冗余。可以把用户权限、收藏菜单、会话超时这些核心数据集中到一个用户上下文服务里,让所有监听器共享这个服务的数据,避免重复查询或计算。

步骤1:创建用户上下文服务

// src/AppBundle/Service/UserContextProvider.php
namespace AppBundle\Service;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use AppBundle\Entity\User;
use AppBundle\Service\FavoriteMenuService; // 假设你有这个收藏菜单服务
use AppBundle\Service\SessionTimeoutService; // 超时检查服务

class UserContextProvider
{
    private $tokenStorage;
    private $favoriteMenuService;
    private $sessionTimeoutService;

    public function __construct(
        TokenStorageInterface $tokenStorage,
        FavoriteMenuService $favoriteMenuService,
        SessionTimeoutService $sessionTimeoutService
    ) {
        $this->tokenStorage = $tokenStorage;
        $this->favoriteMenuService = $favoriteMenuService;
        $this->sessionTimeoutService = $sessionTimeoutService;
    }

    // 统一返回用户所有动态上下文数据
    public function getContext(): array
    {
        $user = $this->tokenStorage->getToken()?->getUser();
        if (!$user instanceof User) {
            return [];
        }

        return [
            'permissions' => $user->getPermissions(),
            'favoriteMenus' => $this->favoriteMenuService->getUserFavorites($user),
            'isSessionTimedOut' => $this->sessionTimeoutService->checkTimeout(),
            // 其他需要的用户相关动态数据
        ];
    }
}

步骤2:改造监听器,复用上下文服务

原来的监听器不用再各自处理数据逻辑,直接注入这个服务获取数据即可:

// src/AppBundle/EventListener/SidebarMenuListener.php
namespace AppBundle\EventListener;

use AppBundle\Service\UserContextProvider;
use Symfony\Component\EventDispatcher\GenericEvent;

class SidebarMenuListener
{
    private $userContextProvider;

    public function __construct(UserContextProvider $userContextProvider)
    {
        $this->userContextProvider = $userContextProvider;
    }

    public function onRender(GenericEvent $event)
    {
        $context = $this->userContextProvider->getContext();
        // 将上下文数据传入事件,供模板渲染使用
        $event->setArgument('userContext', $context);
    }
}

额外优化:注册Twig全局变量

如果模板也需要直接访问这些数据,可以把上下文服务注册为Twig全局变量:

# app/config/config.yml
twig:
    globals:
        user_context: '@AppBundle\Service\UserContextProvider'

之后模板里就能直接调用:

{% for menu in user_context.getContext().favoriteMenus %}
    <li>{{ menu.name }}</li>
{% endfor %}

方案2:用Twig扩展封装动态渲染逻辑

对于菜单收藏这种需要在模板里频繁调用的动态内容,直接用Twig函数封装逻辑会更直观,减少事件传递的复杂度。

创建Twig扩展

// src/AppBundle/Twig/UserDynamicExtension.php
namespace AppBundle\Twig;

use AppBundle\Service\UserContextProvider;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class UserDynamicExtension extends AbstractExtension
{
    private $userContextProvider;

    public function __construct(UserContextProvider $userContextProvider)
    {
        $this->userContextProvider = $userContextProvider;
    }

    public function getFunctions(): array
    {
        return [
            new TwigFunction('get_user_favorites', [$this, 'getUserFavorites']),
            new TwigFunction('check_session_timeout', [$this, 'checkSessionTimeout']),
            new TwigFunction('has_permission', [$this, 'hasPermission']),
        ];
    }

    public function getUserFavorites(): array
    {
        return $this->userContextProvider->getContext()['favoriteMenus'] ?? [];
    }

    public function checkSessionTimeout(): bool
    {
        return $this->userContextProvider->getContext()['isSessionTimedOut'] ?? false;
    }

    public function hasPermission(string $permission): bool
    {
        return in_array($permission, $this->userContextProvider->getContext()['permissions'] ?? []);
    }
}

注册扩展服务

# app/config/services.yml
services:
    app.twig.user_dynamic_extension:
        class: AppBundle\Twig\UserDynamicExtension
        arguments: ['@AppBundle\Service\UserContextProvider']
        tags:
            - { name: twig.extension }

在模板中直接使用

这样模板里不用依赖事件数据,直接调用函数就能渲染动态内容:

{# 渲染收藏菜单 #}
<ul class="favorite-menus">
    {% for menu in get_user_favorites() %}
        <li><a href="{{ menu.url }}">{{ menu.name }}</a></li>
    {% endfor %}
</ul>

{# 检查会话超时 #}
{% if check_session_timeout() %}
    <div class="alert alert-warning">会话即将超时,请操作延长时间</div>
{% endif %}

方案3:拆分动态模板片段,实现模块化渲染

把侧边菜单、页脚这些动态部分拆成独立的Twig模板片段,每个片段直接调用上面的Twig函数或上下文服务,然后在主模板中嵌入,让代码结构更清晰。

比如创建_sidebar_favorite_menu.html.twig:

{# app/Resources/views/parts/_sidebar_favorite_menu.html.twig #}
<div class="favorite-section">
    <h3>我的收藏</h3>
    <ul>
        {% for menu in get_user_favorites() %}
            <li><a href="{{ path(menu.route) }}">{{ menu.label }}</a></li>
        {% else %}
            <li>暂无收藏菜单</li>
        {% endfor %}
    </ul>
</div>

然后在主模板中嵌入:

{# app/Resources/views/base.html.twig #}
<div class="sidebar">
    {% include 'parts/_sidebar_main_menu.html.twig' %}
    {% include 'parts/_sidebar_favorite_menu.html.twig' %}
</div>

核心思路总结

这些方案的核心是把用户相关的动态逻辑集中管理,避免重复代码,同时确保模板能实时获取最新数据(比如收藏菜单的变化)。相比原来的多监听器方案,不仅降低了维护成本,还能让动态内容的渲染更直接高效,完美解决Twig角色无法处理动态数据的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:24:48