mediagone/twig-powerpack

为您的Twig模板提供代码质量助手。

0.2.5 2021-04-11 10:44 UTC

This package is auto-updated.

Last update: 2024-09-11 18:26:43 UTC


README

⚠️ 此项目处于实验阶段。

Latest Version on Packagist Total Downloads Software License

此包为您的Twig模板提供代码质量功能

  1. 模板上下文变量的类型检查.
  2. 从任何模板注册全局数据/资源.
  3. 在模板中实例化类.

以及新功能

  • |json_decode 过滤器

安装

此包需要 PHP 7.4+Twig 2+

将其作为Composer依赖项添加

$ composer require mediagone/twig-powerpack

如果您使用Symfony,请在 services.yaml 中启用扩展

services:
    
    Mediagone\Twig\PowerPack\TwigPowerPackExtension:
        tags: [twig.extension]

简介

Twig模板引擎在类型方面严重不足...

功能

1) 上下文变量类型检查

模板通常需要特定的外部数据,但没有内置的方式检查提供变量的类型。使用 expect 标签可以在您的Twig文件中声明所需的变量,使它们也变得 自文档化。如果数据无效,将抛出异常。

原始类型

支持的标量类型包括: bool, float, intstring

{% extends 'layout.twig' %}

{% expect 'string' as TITLE %}
{% expect 'bool' as ENABLED %}
{% expect 'float' as AMOUNT %}
{% expect 'int' as COUNT %}

注意:TITLE, ENABLED, AMOUNT 和 COUNT 代表所需变量的名称。

对象

由于它们不保证任何数据结构,匿名对象 (stdClass) 不受支持。然而,强烈建议使用命名类来在模板中公开数据。因此,也可以提供 完全限定类名 (FQCN)

{% expect 'App\\UI\\ViewModels\\Foo' as FOO %}

{{ FOO.bar }}

可为空

有时,您可能想确保一个变量已定义,同时通过使用 nullable 关键字使其可选

{% expect nullable 'App\\UI\\ViewModels\\Foo' as FOO %}

{% if FOO != null %}
...
{% endif %}

数组

您还可以使用 array of 关键字检查变量是否是给定类型的数组

{% expect array of 'App\\UI\\ViewModels\\Foo' as ARRAY %}

{% for foo in ARRAY %}
...
{% endfor %}

数组也可以是可为空的

{% expect nullable array of 'App\\UI\\ViewModels\\Foo' as ARRAY %}

{% if ARRAY != null %}
...
{% endif %}

或包含可为空的元素

{% expect array of nullable 'App\\UI\\ViewModels\\Foo' as ARRAY %}

{% for foo in ARRAY %}
    {% if foo != null %}
    ...
    {% endif %}
{% endfor %}

甚至可为空的元素数组!

{% expect nullable array of nullable 'App\\UI\\ViewModels\\Foo' as ARRAY %}

注意:检查数组项的类型可能会引起轻微的性能开销,但除非您有数千个元素,否则应该是可以忽略不计的。

2) 从任何模板注册全局数据

您有时可能需要在模板中声明特定的数据,用于全局范围。例如,如果您的模板动态地将CSS类添加到HTML body中,或者如果它们需要仅在需要时包含可选的CSS或JavaScript资源。

字符串数据

可以使用 {% register <data> in <registry> %} 标签从模板的任何地方注册简短字符串数据

// Page.twig

{% extends 'Layout.twig' %}

{% register 'has-menu' in 'bodyClasses' %}
{% register 'responsive' in 'bodyClasses' %}

{% register '/css/few-styles.css' in 'styles' %}
{% register '/css/some-styles.css' in 'styles' %}

{% register '/js/custom-scripts.js' in 'scripts' %}

...

并通过 registry() 函数在其他地方检索

// Layout.twig

<html>
    <head>
        ...
        
        {% for css in registry('styles') %}
        <link rel="stylesheet" href="{{ css }}" />
        {% endfor %}
        <!-- <link rel="stylesheet" href="/css/few-styles.css" /> -->
        <!-- <link rel="stylesheet" href="/css/some-styles.css" /> -->
    </head>
    <body class="{{ registry('bodyClasses')|join(' ') }}">
    <!-- <body class="has-menu responsive"> -->
        ...
        
        {% for js in registry('scripts') %}
        <script src="{{ js }}"></script>
        {% endfor %}
        <!-- <script src="/js/custom-scripts.js"></script> -->
    </body>
</html>

可选注册子句

为了方便,当数据表示 具有扩展名的路径 时,可以自动推断注册名称,因此使用 in <registry> 是可选的。以下行是等效的

{% register '/styles.css' in 'css' %}
{% register '/styles.css' %}

主体数据

由于您可能需要更长或动态生成的数据,该标签还支持块语法,允许提供内容主体。在这种情况下,您 不能 在打开标签中定义数据,并且 注册子句是强制性的{% register in <registry> %} <body data> {% endregister %}

例如,如果您想从模板中声明内联脚本

// Page.twig
{% extends 'Layout.twig' %}

{% set name = 'world' %}

{% register in 'inlineJs' %}
    alert('Hello {{ name }}');
{% endregister %}

并在HTML页面的末尾包含它

// Layout.twig

<html>
    <body>
        ...
    
        <script>
        {% for js in registry('inlineJs') %}
            {{ js|raw }}
        {% endfor %}
        <!-- alert('Hello world'); -->
        </script>
    </body>
</html>

唯一性

数据可以被声明为唯一的,所以如果多个模板注册相同的值,它只会被包含一次。这在大多数情况下都是必要的,只需在标签中添加once关键字即可。

{% register once '/styles.css' %} 

// Subsequent identical statements will be ignored
{% register once '/styles.css' %}

它也适用于体数据。

{% register once '/styles.css' %}
{% register once in 'css' %}/styles.css{% register %}  // ignored

然而,唯一性只在同一个注册范围内生效,因此以下两个语句都将被考虑。

{% register once '/styles.css' in 'css' %}
{% register once '/styles.css' in 'styles' %}

优先级

由于您无法总是预测数据将被注册的顺序,有时您需要确保某些数据首先注册,例如在其他脚本库中需要的情况。然后,在您的标签末尾添加priority关键字,后跟优先级数字(数值越小越优先*)。

没有优先级的标签总是排在优先级标签之后。

注意:具有相同优先级(或未定义)的数据的顺序没有保证。

{% register '/last.js' %}
{% register '/second.js' priority 2 %}
{% register '/first.js' priority 1 %}

<!-- <script src="/first.js"></script> -->
<!-- <script src="/second.js"></script> -->
<!-- <script src="/last.js"></script> -->

3) 在模板中实例化类

尽管在可能的情况下最好在控制器中执行,但您可能需要在模板中直接创建类实例。new(string $fqcn, ...$args)函数允许您调用给定类的构造函数。

{% include('Partials/Menu.twig') with {Menu: new('App\\UI\\Partials\\Menu',
    'Main menu',
    [
        {Label: 'Item 1', Href: '/url/to/item1'},
        {Label: 'Item 2', Href: '/url/to/item2'},
    ],
)} %}

以下是一个视图模型类

namespace App\UI\Partials;

final class Menu
{
    private string $name;
    private array $items;
    
    public function __construct(string $name, array $items)
    {
        $this->name = $name;
        $this->items = array_map(static fn($item) => new MenuItem($item), $items);
    }
}

许可证

Twig PowerPack》遵循MIT许可证。请参阅LICENSE文件。