redant / twig-components
在Twig中定义可重用的组件。
Requires
- php: >=8.0
- symfony/property-access: ^5.4|^6.4|^7.0
- twig/twig: ^1.34|^2.6.1|^3.0
README
Twig组件是强大、可重用且自动文档化的元素,您可以使用新的component
标签在Twig模板中使用。Twig组件允许您快速构建和维护自己的UI工具包,其中每个按钮、表格或卡片只需设计一次,就可以在整个应用程序中使用。
这种方法在以下方面改善了开发人员的体验
- 渲染(使用)组件的简单语法;
- 当缺少必需参数时发出通知;
- 当参数名拼写错误时提供有用的建议;
- 当提供错误类型的数据时发出警告;
- 参数排序的灵活性。
还有一个实用的Symfony包可用。
快速入门
安装
此扩展可以通过Composer安装
$ composer require redant/twig-components
或者在您的composer.json
中直接安装
{ "require": { "redant/twig-components": "~1.0" } }
设置
您可以将扩展添加到Twig中,如下所示
use RedAnt\TwigComponents\Registry as ComponentsRegistry; use RedAnt\TwigComponents\Extension as ComponentsExtension; // Initialize Twig $loader = new \Twig\Loader\FilesystemLoader($templateDir); $loader->addPath($componentsDir, 'Ui'); // Creates a @Ui namespace for the specified dir $twig = new \Twig\Environment($loader); // Initialize Twig Components registry $componentsRegistry = new ComponentsRegistry($twig); $componentsRegistry->addComponent('ui.button', '@Ui/elements/button'); // ... add more components here // Add the extension to your Twig environment $componentsExtension = new ComponentsExtension($componentsRegistry); $twig->addExtension($componentsExtension);
什么是组件?
组件在某种程度上受到了普通编程语言函数的启发。组件具有一组固定的参数,因此您不能使用在组件上未定义的参数。所有参数都会进行类型检查并自带文档。然后,完整的组件文档可以自动组装成一个Markdown文件文件夹。每个组件都必须提供包含配置选项的嵌套哈希,类似于我们之前使用的宏定义。
为什么要在模板中对类型检查如此严格?原因之一是我们希望能够将这些组件(以及它们的文档,正如您将看到的)作为技术涉及的组件设计师和前端导向的开发人员之间交流的手段。
Twig在设计上在很多方面都非常宽容,如果您拥有完整的知识,这很好,但它可能会阻碍不同技能或信息水平的人之间的沟通。我们不希望组件用户每次都必须深入研究特定组件的实现——我们希望他们能够从类似于我们常规代码的公共API中获取信息。这就是为什么我们强制执行更严格的使用:现在我们能够帮助用户纠正小错误或类型不匹配。
但说够了,让我们看看一个示例组件定义:按钮组件。
Twig组件定义了一系列具有严格类型、默认值和注释的属性,并指定了哪些属性是必需的。在实现您的组件时,每个属性都可以渲染为一个属性。只有当您将其标记为必需时,属性才是必需的。否则,您可以设置默认值。
{% component button { container: { type: 'string', default: 'button', comment: 'HTML element, e.g. "button" (default) or "a"' }, label: { type: 'string', required: true, comment: 'Button text (rendered as raw HTML)' }, classes: { type: 'string[]', default: [ 'small' ], comment: 'Additional button classes'}, some_object: { type: 'Some\\Namespace\\SomeObject', comment: 'An implementation of a component' }, absent_object: { type: '?Some\\Namespace\\AbsentObject', comment: 'An implementation of a component which can be absent at any point' } } with options %}
您会注意到这个文件开始有一个注释,这是可选的。如果存在,它将在渲染此组件的文档时使用。紧接着是实际的组件定义,在{% component %}
标签中。为此按钮组件定义了三个参数,所有参数都是类型string
,其中只有一个必需(按钮的label
)。我们可以通过提供简短的描述(comment
)和在preview
参数中提供示例值来对每个属性进行文档化。
此外,Twig组件强制执行一些最佳实践以促进组件之间的一致性,例如
- 文件名必须与组件名相同(即,
button.html.twig
必须定义名为button
的组件); - 仅使用蛇形大小写(
snake_case
)变量名; - 不要在注释结尾处使用句号。
好的,上一个可能感觉有点随意或严格,但相信我,如果你的参数描述有统一性,你的文档看起来会好很多。
这个按钮组件将生成一个包含所有属性的Twig变量button
,其值来自options
变量或指定的默认值。
options
中的每个值都会被检查其名称、类型,以及对于必需属性,是否已定义。值可以是引用对象的类型。如果对象不一定总是可用,如absent_object
,你可以在其前加一个?
来标记类型为可空,在这种情况下,将避免错误。
用法
每个定义的组件都可以通过一个引用Twig组件服务的Twig全局变量访问,实际上将组件放在了component
命名空间中。例如,可以在任何Twig模板中使用component.button()
来访问button
组件。
如果你不喜欢“component”这个名字,我们在下一节中会解决这个问题。
{{ component.button({ container: 'a', label: 'Click me', classes: [ 'large' ] }) }}
由于所有指定的属性都会被检查,一个像{{ component.button({ lable: 'Click me' })
这样的拼写错误将被检测到。此外,{{ component.button({}) }}
将抛出运行时错误,因为label
属性是必需的。
给组件提供的哈希值首先会检查与组件定义的名称一致性。例如,如果你不小心把lable
写成了label
,Twig Components会给出一个友好的运行时错误消息:组件 "button" 不包含属性 "lable"。你是想指 "label" 吗?
。
它还进行类型检查:当你提供一个数组作为url
时,它会失败。每个参数都会使用is_string
、is_bool
、is_int
、is_float
和is_array
进行检查。参数还可以强制指定特定的PHP类,在这种情况下,会使用instanceof
检查。这确保了你会在没有正确使用组件时立即注意到。
注意:在可重用的包中,始终使用render_component()
函数,因为用户可能已经定义了一个不同的组件全局变量(见下文)。
{{ render_component('button', { container: 'a', label: 'Click me', classes: [ 'large' ] }) }}
一些示例包含在doc
文件夹中。
全局变量
如果你不喜欢定义组件的全局变量的名称,请使用扩展的$globalVariable
参数来更改它。
$componentsExtension = new ComponentsExtension($twig, 'ui');
这将注册按钮组件为ui.button()
。
注意:如果你将前缀设置为false
,则不会为定义的组件注册任何Twig全局变量。然后,你只能使用render_component
函数来渲染组件。
许可证
此库受MIT许可证许可 - 有关详细信息,请参阅LICENSE文件。
Twig组件由Gert Wijnalda gert@redant.nl构思,并受到Pierre Stoffe的这篇文章的启发。
本文档的部分内容首先出现在文章'Taming Twig'中,最初发表在2019年4月php[architect]杂志的第四期。
Twig组件的代码和本文档都得到了RedAnt同事宝贵的反馈,尤其是Vincent Vermeulen、Rico Humme、Florian Käding和Martijn van Beek。感谢你们,伙计们!