namelesscoder / fluid-parameters
API,用于声明任何Fluid模板/部分的参数
Requires
- typo3fluid/fluid: ^2
Requires (Dev)
- php-coveralls/php-coveralls: ^2.5
- phpstan/phpstan: ^1.9
- phpunit/phpunit: ^9 || ^10.0
- squizlabs/php_codesniffer: ^3.7
README
TYPO3 Fluid扩展 fluid_parameters
API,用于在任意类型的文件中启用声明Fluid模板、部分、布局或部分的参数。
它做什么?
简而言之:允许您声明Fluid资源的必需和可选参数。它是通过模拟Fluid 3.0可能包含的功能的子集来实现的,即f:parameter
。在此基础上,它还增加了一些方便的小技巧。
安装
要安装,只需执行composer req namelesscoder/fluid-parameters
。就这么简单 - 无需配置。如果您在TYPO3 CMS安装中使用此包,则自动将f:parameter
、f:description
和f:parameter.mode
作为ViewHelper添加。如果您不在TYPO3 CMS安装中,则必须在模板中手动添加命名空间{namespace f=\NamelessCoder\FluidParameters\ViewHelpers}
,以便使用该功能。
功能集
- 可以为任何Fluid资源声明参数;模板、布局、部分或部分(在这些三种类型的Fluid资源中的任何一种)。
- 参数可以是必需的,在这种情况下,如果必需的变量没有作为模板变量分配或在
f:render
上通过arguments
传递给部分/部分,则将抛出错误。 - 参数可以是可选的,在这种情况下,如果它们未传递,则可以赋予默认值(就像ViewHelper参数一样),并在渲染资源时分配。
- 参数可以转换为适当的类型,如
string
或integer
。 - 参数可以指定一组允许的值。如果您声明了一个允许“foo”或“bar”等值的参数,但在渲染资源时变量值为“baz”,则可以抛出错误。
- 指定类型为
array
的参数允许您将CSV值作为变量传递,以便更容易在f:render
上的arguments
中作为数组值传递,而无需复杂的Fluid语法 - 或者直接从记录中消费存储在记录列中的CSV值。 - 可以渲染引用,描述给定Fluid资源或部分的参数。
- 可以为模板或部分提供描述,该描述可以作为参数引用的一部分进行渲染。
这些功能共同允许编写Fluid模板文件的开发者确保在渲染模板文件时必要的变量存在,并且可选变量可以具有默认值。它还允许渲染模板的集成者了解是否缺少必需的变量或数据类型不兼容 - 而无需详细了解每个模板文件的内容。
如何使用
无需配置 - 只需在Fluid模板中使用f:parameter
ViewHelper,其余操作将自动完成。f:parameter
ViewHelper可以在任何布局、模板或部分中使用,并在任何这些类型的文件中定义的任何部分中使用。声明的参数是针对文件或部分特定的
- 一旦渲染另一个文件或部分,只有该其他文件或部分的参数将被考虑。
添加必需参数
<f:parameter name="title" type="string" required="1" /> <h3>{title}</h3>
如果在渲染模板时没有分配{title}
作为模板变量,则会抛出错误。
可选参数
<f:parameter name="title" type="string" default="Default title" /> <h3>{title}</h3>
如果未指定 {title}
,则不会抛出错误。相反,它会自动将变量分配值为 默认标题
。
支持的参数类型
int
,integer
字符串
float
,double
,decimal
bool
,boolean
数组
DateTime
(本地将UNIXTIME时间戳和日期字符串转换为DateTime
)object
(通用对象,从array
转换为stdClass
)
此外,还支持自定义类名,可以通过全限定名(FQN)或简称指定。如果通过简称指定,将允许任何具有该简称的对象实例 - 且不进行类型转换。如果通过FQN指定,则只允许类的实例或其子类的实例 - 并且可以通过将非对象值作为单个构造函数参数传递给类(使用new $className($value)
)来进行类型转换。
每种类型(包括自定义对象)都支持“类型数组”,通过在类型后添加[]
来实现,例如string[]
。这甚至可以做到多层,例如string[][]
,以要求“字符串的数组数组”。
特定的必需值
<f:parameter name="color" type="string" oneOf="red,green,blue" default="red" /> <span class="color-{color]">Some text that's either red, green or blue</span>
如果分配变量{color}
的值不是red
、green
或blue
中的任何一个,则会抛出错误,并选择red
作为默认值。
未声明的变量
<f:parameter.mode>strict</f:parameter.mode> <f:parameter name="title" type="string" required="1" />
将参数处理模式设置为strict
,这意味着如果在此模板中渲染时存在未声明的变量,则会抛出错误
<f:render partial="StrictRequirementsPartial" arguments="{title: 'My title', unknownvariable: 'Some value'}" />
导致错误
Unxpected (undefined) template variable(s) encountered: unknownvariable
默认情况下,允许未声明的变量。通过将模式设置为strict
,这种行为会改变。
请注意,必须在任何f:parameter
出现之前使用f:parameter.mode
才能产生影响。
在部分中使用
<f:section name="MySection"> <f:parameter.mode>strict</f:parameter.mode> <f:parameter name="title" type="string" required="1" /> <f:parameter name="text" type="string" default="Default text" /> <h3>{title}</h3> <p>{text}</p> </f:section>
与在模板、部分或布局上声明参数具有完全相同的效果,但仅在渲染部分时适用。如果部分在渲染时带有未声明的变量分配,则会抛出错误(由于模式为strict
)。如果渲染部分时未分配{text}
,则它会自动分配值为默认文本
。
描述模板
<f:description> This is a description of the template file. You must always assign the "title" variable when rendering this template. </f:description> <f:parameter name="title" type="string" required="1" /> <h3>{title}</h3>
输出
<h3>Value of title variable</h3>
基本上,您在f:description
中使用的任何内容在渲染模板/部分时都不会作为输出渲染。注意:不要在描述块中使用任何Fluid代码!如果您这样做,描述文本将被截断,并且只包含第一个Fluid代码前的任何文本。此块中的无效Fluid代码仍然会导致模板解析错误!
提取参数/描述的“反射”
该包包含一个API,用于提取有关应用于给定模板的参数的信息,以及模板中f:description
块的内容。这可以用来构建您自己的风格指南或参考。
考虑以下位于/path/to/my/template.html
的示例模板文件
<f:description> Text from the description block in my template </f:description> <f:parameter.mode>strict</f:parameter.mode> <f:parameter name="title" type="string" description="A text to become the title" /> <f:parameter name="text" type="string" description="A text to become the paragraph body" required="1" /> <f:parameter name="level" type="integer" description="Optional header level 1-9" oneOf="1,2,3,4,5,6,7,8,9" /> <f:if condition="{title]"> <h{level}>{title}</h{level}> <p>{text}</p> </f:if> <f:section name="MySection"> <f:description> Text from the description block in section "MySection" in my template </f:description> <f:parameter name="content" type="string" description="Content string to render in the section" required="1" /> Additional content: {content} </f:section>
使用提取API相对简单
$templateFile = '/path/to/my/template.html'; $renderingContext = new \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext(); $extractor = new \NamelessCoder\FluidParameters\Reflection\ParameterExtractor($renderingContext); $reflection = $extractor->parseTemplate($templateFile);
$reflection
变量现在包含一个TemplateReflection实例,该实例包含描述模板的属性。您可以使用此反射实例来提取所有元数据
<?php function renderParameterDefinitions(array $parameterDefinitions): array { $output = []; foreach ($parameterDefinitions as $name => $definition) { $output[] = ' Parameter: ' . $name; $output[] = ' Description: ' . $definition->getDescription(); $output[] = ' Required: ' . ($definition->isRequired() ? 'Yes' : 'No'); $output[] = ' Type: ' . $definition->getType(); if (!$definition->isRequired()) { $output[] = ' Default: ' . var_export($definition->getDefaultValue(), true); } if (!empty($definition->getOneOf())) { $output[] = ' Allowed values: ' . implode(', ', $definition->getOneOf()); } $output[] = PHP_EOL; } return $output; } $description = $reflection->getDescription(); $parameterMode = $reflection->getParameterMode(); $output = [ 'Template: ' . $templateFile, 'Parameter mode: ' . $parameterMode, 'Description:', ' ' . $description, 'Parameters:', PHP_EOL, ]; $output = array_merge($output, renderParameterDefinitions($reflection->getParameterDefinitions()));
还可以提取模板文件中每个f:section
节点相同类型的引用。从$reflection->getSections()
返回的每个值都是一个SectionReflection实例
// You can extract a single known section and chain getters: $mySectionDescription = $reflection->fetchSection('MySection')->getDescription(); // Or iterate over all sections within the template: foreach ($reflection->getSections() as $sectionName => $sectionReflection) { $output[] = 'Template: ' . $templateFile . ', section: ' . $sectionName; $output[] = 'Parameter mode: ' . $parameterMode; $output[] = 'Description:'; $output[] = $description; $output[] = 'Parameters:'; $output = array_merge($output, renderParaterDefinitions($sectionReflection->getParameterDefinitions())); $output[] = PHP_EOL; } echo implode(PHP_EOL, $output);
这样就会产生如下输出
Template: /path/to/my/template.html
Parameter mode: strict
Description:
Text from the description block in my template
Parameters:
Parameter: title
Description: A text to become the title
Required: No
Type: string
Default: null
Parameter: text
Description: A text to become the paragraph body
Required: Yes
Type: string
Parameter: level
Description: Optional header level 1-9
Required: No
Type: integer
Default: 1
Allowed values: 1, 2, 3, 4, 5, 6, 7, 8, 9
Template: /path/to/my/template.html, section: MySection
Parameter mode: loose
Description:
Text from the description block in section "MySection" in my template
Parameters:
Parameter: content
Description: Content string to render in the section
Required: Yes
Type: string
当然,您也可以将$reflection
实例分配给Fluid模板以渲染引用。使用Fluid的标准迭代和变量访问,以实现您想要的精确输出,例如{reflection.description}
、<f:for each="{reflection.parameterDefinitions}" as="definition">...</f:for>
等等 - 就像您通常迭代数组和输出变量一样。
此扩展与fluid_components
之间的区别
您可能已经知道由sitegeist创建的一个扩展"Fluid Components" - 这两个扩展在允许Fluid模板定义所需变量方面有些相似,但在实现哲学上非常不同。
这两个扩展可以和平共存,并且可以在一定程度上混合使用(由Fluid Components渲染的部分可以与由Fluid Parameters声明的参数配合);然而,一个中声明的参数在另一个中是未知的。因此,在同一个项目中使用Fluid Components和Fluid Parameters的某些上下文是完全可行的。
以下表格显示了它们之间的区别
(* 如果您手动注册命名空间 {namespace f=\NamelessCoder\FluidParameters\ViewHelpers}
)
本质上是:如果您...
- 只需要声明可选/必需参数并提供默认值的能力。
- 希望最小的集成,可以在不覆盖内部Fluid类的情况下工作。
- 不想编写额外的配置。
- 不关心XSD集成以实现自动完成。
- 仍然想使用您的模板作为控制器操作模板或FLUIDTEMPLATE TS对象的模板文件。
- 想使用您的模板文件作为需要变量内容的元素或页面模板。
- 想在基于Flux的模板中使用此功能
- 想在使用Fluid的非TYPO3-CMS项目中使用此功能
那么请使用Fluid Components
...
- 不介意需要编写额外的配置。
- 不介意内部Fluid类被性能较低的替代品覆盖。
- 不介意受影响的模板不能再正常渲染。
- 需要/想要XSD集成以实现自动完成。
- 需要/想要一个“动态样式指南”(无需自己编写实现)。
致谢
本工作得到了Busy Noggin的慷慨赞助。