PHP项目Symfony twig扩展自定义

wen PHP项目 3

本文目录导读:

PHP项目Symfony twig扩展自定义

  1. 创建自定义Twig扩展类
  2. 注册服务
  3. 在模板中使用
  4. 高级用法 - 带依赖注入
  5. 创建带全局变量的扩展
  6. 在模板中使用全局变量
  7. 测试自定义扩展
  8. 最佳实践建议

我来详细介绍如何在Symfony项目中创建自定义Twig扩展。

创建自定义Twig扩展类

首先创建一个继承 AbstractExtension 的类:

// src/Twig/AppExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
    // 注册过滤器
    public function getFilters(): array
    {
        return [
            new TwigFilter('price', [$this, 'formatPrice']),
            new TwigFilter('truncate', [$this, 'truncateText']),
        ];
    }
    // 注册函数
    public function getFunctions(): array
    {
        return [
            new TwigFunction('greet', [$this, 'greetUser']),
            new TwigFunction('random_color', [$this, 'getRandomColor']),
        ];
    }
    // 自定义过滤器方法
    public function formatPrice($number, $currency = '¥', $decimals = 2)
    {
        $formatted = number_format($number, $decimals);
        return $currency . ' ' . $formatted;
    }
    public function truncateText($text, $length = 100, $suffix = '...')
    {
        if (mb_strlen($text) <= $length) {
            return $text;
        }
        return mb_substr($text, 0, $length) . $suffix;
    }
    // 自定义函数方法
    public function greetUser($name)
    {
        $time = date('H');
        $greeting = 'Hello';
        if ($time < 12) {
            $greeting = 'Good morning';
        } elseif ($time < 18) {
            $greeting = 'Good afternoon';
        } else {
            $greeting = 'Good evening';
        }
        return sprintf('%s, %s!', $greeting, ucfirst($name));
    }
    public function getRandomColor()
    {
        $colors = ['primary', 'secondary', 'success', 'danger', 'warning', 'info'];
        return $colors[array_rand($colors)];
    }
}

注册服务

如果使用自动装配(Symfony 5+),扩展会自动注册,否则手动配置:

# config/services.yaml
services:
    App\Twig\AppExtension:
        tags: ['twig.extension']

在模板中使用

{# 使用自定义函数 #}
{{ greet('John') }}
{# 输出: Good morning, John! #}
{# 使用随机颜色函数 #}
<div class="alert alert-{{ random_color() }}">
    This is a random colored alert!
</div>
{# 使用自定义过滤器 #}
{{ 99.99|price }}
{# 输出: ¥ 99.99 #}
{{ 99.99|price('$', 0) }}
{# 输出: $ 100 #}
{{ 'This is a very long text...'|truncate(10) }}
{# 输出: This is a... #}

高级用法 - 带依赖注入

// src/Twig/AdvancedExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Routing\RouterInterface;
class AdvancedExtension extends AbstractExtension
{
    private $entityManager;
    private $security;
    private $router;
    public function __construct(
        EntityManagerInterface $entityManager,
        Security $security,
        RouterInterface $router
    ) {
        $this->entityManager = $entityManager;
        $this->security = $security;
        $this->router = $router;
    }
    public function getFunctions(): array
    {
        return [
            new TwigFunction('get_user_count', [$this, 'getUserCount']),
            new TwigFunction('is_admin', [$this, 'isAdmin']),
            new TwigFunction('active_route', [$this, 'isActiveRoute']),
        ];
    }
    public function getUserCount(): int
    {
        return $this->entityManager
            ->getRepository(User::class)
            ->count([]);
    }
    public function isAdmin(): bool
    {
        return $this->security->isGranted('ROLE_ADMIN');
    }
    public function isActiveRoute(string $routeName): bool
    {
        $currentRoute = $this->router->getRouteCollection()
            ->get($routeName);
        return $currentRoute !== null;
    }
}

创建带全局变量的扩展

// src/Twig/GlobalExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\Extension\GlobalsInterface;
class GlobalExtension extends AbstractExtension implements GlobalsInterface
{
    private $appName;
    private $appVersion;
    public function __construct(string $appName, string $appVersion)
    {
        $this->appName = $appName;
        $this->appVersion = $appVersion;
    }
    public function getGlobals(): array
    {
        return [
            'app_name' => $this->appName,
            'app_version' => $this->appVersion,
            'current_year' => date('Y'),
        ];
    }
}

在模板中使用全局变量

{# 直接使用全局变量 #}
<footer>
    &copy; {{ current_year }} {{ app_name }} v{{ app_version }}
</footer>

测试自定义扩展

// tests/Twig/AppExtensionTest.php
namespace App\Tests\Twig;
use App\Twig\AppExtension;
use PHPUnit\Framework\TestCase;
class AppExtensionTest extends TestCase
{
    private $extension;
    protected function setUp(): void
    {
        $this->extension = new AppExtension();
    }
    public function testFormatPrice()
    {
        $result = $this->extension->formatPrice(99.99);
        $this->assertEquals('¥ 99.99', $result);
    }
    public function testTruncateText()
    {
        $text = 'This is a very long text that needs to be truncated';
        $result = $this->extension->truncateText($text, 10);
        $this->assertEquals('This is a...', $result);
    }
    public function testGreetUser()
    {
        $result = $this->extension->greetUser('john');
        $this->assertStringContainsString('john', strtolower($result));
    }
}

最佳实践建议

  1. 命名规范:使用有意义的名称,添加后缀如 Extension
  2. 单一职责:每个扩展负责一组相关功能
  3. 缓存友好:避免在扩展中进行数据库查询(使用服务层)
  4. 类型提示:使用PHP类型声明
  5. 文档注释:为方法添加PHPDoc注释

这样你就可以在Symfony项目中轻松创建和使用自定义Twig扩展了!

抱歉,评论功能暂时关闭!