fewagency/fluent-form

此包已被废弃,不再维护。未建议替代包。

Fluent 接口风格 HTML 表单生成器

dev-master 2018-10-17 12:37 UTC

This package is auto-updated.

Last update: 2023-04-17 22:56:04 UTC


README

fluent-html 的扩展,用于构建可访问、格式良好且可定制的 HTML 表单。

// Generate a simple inline search form
echo FluentForm::create()
    ->inline()
    ->containingInputBlock('query', 'search')
    ->followedByButtonBlock('Search!');
<form class="form-block-container form-block-container--inline" method="POST">
<span class="form-block">
<span><label class="form-block__label" for="query">Query</label></span>
<span>
<input name="query" type="search" class="form-block__control" id="query">
</span>
</span>
<span class="form-block">
<span><button type="submit">Search!</button></span>
</span>
</form>

安装与配置

composer require fewagency/fluent-form

可选的伪装类

您可以将 Laravel 伪装类 添加到项目配置文件 config/app.php 中的 aliases 数组。

'FluentForm'  => FewAgency\FluentForm\Facades\FluentForm::class,

原则

FluentForm控制块 组成,这些控制块在 控制块容器 中分组。基本的 FluentForm 元素是这样的容器,包括 FieldsetElement(一个嵌套容器的示例)。InputBlockCheckboxBlock 是控制块的示例。

控制块

控制块包含一个到三个元素,顺序如下

  1. 标签包装器用于容纳控件的标签。标签包装器不一定总是存在,例如对于包含自身标签的复选框输入的块。
  2. 表单控件包装器存在于所有控制块中,并包含实际的表单控件。在某些情况下,可能存在多个表单控件在控制包装器中。
  3. 描述性元素仅当表单控件有描述或要向用户显示的消息时存在。

控制块容器

可以在容器级别设置许多控制属性,影响该容器内的表单控件。这些属性首先在单个元素上检查,如果没有指定,则向上检查 HTML 树,通过控制块、其块容器,直到 <form> 元素本身。这使得在表单的各个部分中设置和覆盖属性变得容易。

方法名称

命名原则基于 基本包 fewagency/fluent-html 的命名原则。此包中返回相对于当前元素的新元素的方法的示例是控制块容器上的 containing...Block() 方法,以及控制块的 followedBy...Block() 方法。

CSS 类名称

此包使用 BEM 命名方法

用法

FluentForm::create() 是新表单的基础。

表单控件可以使用点符号进行命名和引用。名为 person.name 的控件将渲染其 name 属性为 person[name]

如果输入是“multiple”类型,则名为的控件name属性将被渲染为带有空括号的pets[]

请注意,大多数方法接受集合和闭包作为参数,就像在fewagency/fluent-html的使用中一样。

根据您希望表单HTML输出的位置,您可以使用echo FluentForm::create()->...或使用PHP字符串转换来渲染它,即(string)FluentForm::create()->...

Blade模板中,如果将HTML放入echo标签中,则会渲染它:{{ FluentForm::create()->... }}。有关更多信息,请查看fewagency/fluent-html的Blade文档

表单上的便捷方法

withAction($url)设置<form>action属性。

withMethod($method, $name = '_method')更改<form>上的method属性(默认为POST)。如果$method不是GETPOST,这将帮助创建表单方法欺骗,使用隐藏输入,这对于那些PUTPATCHDELETE操作很有用。

withToken($token, $name = '_token')添加一个隐藏的token输入,用于您的CSRF保护。

// Form with options
echo FluentForm::create()
    ->withAction('/login')
    ->withMethod('DELETE')
    ->withToken('12345');
<form class="form-block-container" method="POST" action="/login">
<input name="_method" type="hidden" value="DELETE">
<input name="_token" type="hidden" value="12345">
</form>

可以使用FluentHtml的标准方法(如withAttribute()withClass())设置表单元素上任何其他所需的属性或行为。

添加表单控件

表单(或其他容器)上的第一个控件使用containing...Block()方法之一添加,例如containingInputBlock($name, $type = 'text')。此调用将返回新的块,因此您可以直接在之后链式调用任何修改该新块的方法。

后续控件使用followedBy...Block()方法添加到另一个控件块之后,例如followedByInputBlock($name, $type = 'text')

块类型及其参数

  • ...InputBlock($name, $type = 'text')
  • ...PasswordBlock($name = 'password')
  • ...SelectBlock($name, $options = null)
  • ...MultiSelectBlock($name, $options = null)
  • ...ButtonBlock($button_contents, $type = 'submit')
  • ...CheckboxBlock($name)

添加控件块的示例

// Form with controls
echo FluentForm::create()
    ->containingInputBlock('username')->withLabel('Your username')
    ->followedByPasswordBlock()->withLabel('Your password');
<form class="form-block-container" method="POST">
<div class="form-block">
<div>
<label class="form-block__label" for="username2">Your username</label>
</div>
<div>
<input name="username" type="text" class="form-block__control" id="username2">
</div>
</div>
<div class="form-block">
<div><label class="form-block__label" for="password2">Your password</label></div>
<div>
<input name="password" type="password" class="form-block__control" id="password2">
</div>
</div>
</form>

常见的控件块选项

withLabel($html_contents)向控件块的标签元素添加内容。如果没有调用,则默认标签将基于输入的名称。

withInputValue($value)在大多数控件块中可用,并将直接设置主底层输入的值。

withInputAttribute($attributes, $value = true)在大多数控件块中可用,并将直接设置主要底层输入元素上的属性。这里有一个格式化输入值属性(value attribute)的巧妙技巧(示例使用Carbon来格式化日期时间)

->withInputAttribute('value', function($input) {
  $value = $input->getValue();
  try {
    $value = \Carbon\Carbon::parse($value)->toW3cString();
  } catch(Exception $e) {
    // Do nothing on errors
  }
  return $value;
})

withDescription($html_contents)使用aria-describedby添加与输入相关的描述性内容。

// Element with description
echo FluentForm::create()
    ->containingInputBlock('name')->withDescription('Your full name');
<for class="form-block-container" method="POST">
<div class="form-block">
<div><label class="form-block__label" for="name">Name</label></div>
<div>
<input name="name" type="text" aria-describedby="name-desc" class="form-block__control" id="name">
</div>
<div class="form-block__description" id="name-desc">Your full name</div>
</div>
</form>

disabled($disabled = true)readonly($readonly = true)required($required = true)在表单控件上设置相应的HTML属性和在控件块上的相应CSS类。

withSuccess($has_success = true)在控件块元素上设置CSS类。

withError($messages)withWarning($messages) 将消息列表放入输入元素的描述性元素中,并在控制块上设置一个 CSS 类。添加的错误消息还将设置输入元素的 aria-invalid 属性。

// Element with error message
echo FluentForm::create()
    ->containingInputBlock('name')->withError('Must not contain numbers');
<form class="form-block-container" method="POST">
<div class="form-block form-block--error">
<div><label class="form-block__label" for="name2">Name</label></div>
<div>
<input name="name" type="text" aria-describedby="name2-desc" class="form-block__control" aria-invalid="true" id="name2">
</div>
<div class="form-block__description" id="name2-desc">
<ul class="form-block__messages form-block__messages--error">
<li>Must not contain numbers</li>
</ul>
</div>
</div>
</form>

控件类型和选项

文本输入

InputBlock($name, $type = 'text') 生成任何由 $type 指定的 <input>,包括 textarea

某些类型会得到特殊处理

  • password 除非您在输入元素上明确设置它,否则不会打印 value 属性。
  • emailtel 有一些预设的 autocapitalizeautocorrectautocomplete 属性,您可以使用 withInputAttribute() 来覆盖。
复选框

CheckboxBlock($name) 是一个具有默认 value 属性为 "1" 的复选框输入。

withInputValue($value) 可以用来在复选框上设置自定义 value

checked($checked = true)unchecked() 修改底层输入的 checked 属性。

可以使用 withCheckbox($name)containingCheckbox($name) 添加更多复选框。第一个复选框被视为块的默认输入,因此额外的复选框不会自动显示任何消息或描述。

// Checkboxes
echo FluentForm::create()
    ->containingCheckboxBlock('toc')->required()->unchecked()
    ->withCheckbox('other');
<form class="form-block-container" method="POST">
<div class="form-block form-block--required">
<div>
<div class="form-block__checkbox-wrapper">
<label>
<input name="toc" type="checkbox" value="1" required>
Toc
</label>
</div>
<div class="form-block__checkbox-wrapper">
<label>
<input name="other" type="checkbox" value="1" required>
Other
</label>
</div>
</div>
</div>
</form>
选择控件

SelectBlock($name, $options = null) 可以轻松地通过 multiple($multiple = true) 转换为多选。

可以在创建时提供 $options(任何按选项值键的选项显示字符串集合),或稍后通过 withOptions($options) 添加。

通过在 $options 中放置一个键为 optgroup 标签的选项集合,可以生成 <optgroup>

使用 withSelectedOptions($options) 选项,并使用 withDisabledOptions($options) 禁用选项。

// Select with optgroup, disabled and selected options
echo FluentForm::create()
    ->containingSelectBlock('pet')->withSelectedOptions('dog')->withDisabledOptions('cat')
    ->withOptions(['cat' => 'Cat', 'Reptiles' => ['turtle' => 'Turtle'], 'dog' => 'Dog']);
<form class="form-block-container" method="POST">
<div class="form-block">
<div><label class="form-block__label" for="pet">Pet</label></div>
<div>
<select name="pet" class="form-block__control" id="pet">
<option value="cat" disabled>Cat</option>
<optgroup label="Reptiles"><option value="turtle">Turtle</option></optgroup>
<option value="dog" selected>Dog</option>
</select>
</div>
</div>
</form>
按钮

ButtonBlock($button_contents, $type = 'submit') 是一个包含一个按钮的块。

可以使用 withButton($button_contents, $type = "submit")containingButton($button_contents, $type = "submit") 添加更多按钮。第一个按钮被视为块的默认输入,因此额外的按钮不会自动显示任何消息或描述。

// Buttons
echo FluentForm::create()
    ->containingButtonBlock('Submit')
    ->withButton('Reset', 'reset');
<form class="form-block-container" method="POST">
<div class="form-block">
<div>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</div>
</form>

块中的自定义 HTML

有时您可能想对控件块内的 HTML 元素进行特殊处理。

以下是一些可以用来在不同块上提取元素的方法

  • getInputElement()
  • getMainButtonElement()
  • getMainCheckboxElement()
  • getLabelElement()
  • getDescriptionElement()

通过这些元素,您可以使用 FluentHtmlElement 的方法 在块内及其周围添加和修改 HTML。

在完成自定义后,您可能发现自己深入到“正常”的 FluentHtmlElement 分支中,超出了本包的元素。您可以通过 FluentHtmlElement 的结构导航方法 找回表块或容器。特别是 getAncestorInstanceOf(\FewAgency\FluentForm\AbstractControlBlock::class)getAncestorInstanceOf(\FewAgency\FluentForm\AbstractControlBlockContainer::class) 可能特别有用。

// Custom HTML next to input
echo FluentForm::create()
    ->containingInputBlock('test')
    ->getInputElement()->followedByElement('span','extra content')
    ->getAncestorInstanceOf(\FewAgency\FluentForm\AbstractControlBlock::class)
    ->followedByInputBlock('after');
<form class="form-block-container" method="POST">
<div class="form-block">
<div><label class="form-block__label" for="test">Test</label></div>
<div>
<input name="test" type="text" class="form-block__control" id="test">
<span>extra content</span>
</div>
</div>
<div class="form-block">
<div><label class="form-block__label" for="after">After</label></div>
<div>
<input name="after" type="text" class="form-block__control" id="after">
</div>
</div>
</form>

容器选项

在控制块容器中,可以设置默认选项,这些选项将用于任何子表单控件。

withValues($map) 添加用于填充输入值和选中选项的键值映射。如果提供一个PHP对象,则输入值将从该对象的公共属性中提取。这些映射按顺序检查,从最后添加的到最先添加的,直到找到匹配的键。例如,您可以首先添加一个包含默认值的映射,如当前存储的数据,然后添加一个包含用户最后输入的映射。

withLabels($map) 添加用于填充输入标签的键值映射。

withErrors($messages)withWarnings($messages) 添加按控件名称键控的消息。

要在容器内的控件上设置 成功禁用只读必填 状态,请使用 withSuccess($map)withDisabled($map)withReadonly($map)withRequired($map)。这些方法的输入可以是控制名称的字符串或以控制名称为键的键布尔映射。

// Adding maps to containers
FluentForm::create()
    ->withSuccess('name', 'phone')
    ->withRequired(['name' => true, 'phone' => false]); 

要添加一个隐藏输入,只需在容器上调用 withHiddenInput($name, $value = null)

Laravel 表单选项示例

{{
FluentForm::create()
  // Put the Laravel CSRF token on the form
  ->withToken(csrf_token())
  // Use default values from an Eloquent model, and old user input if flashed into session
  ->withValues($model, old())
   // Add Laravel validation errors to the form
  ->withErrors($errors) 
  // Pick default labels from the validation language file 
  ->withLabels(trans('validation.attributes'))
}}

Eloquent 模型 添加到 withValues() 将通过其 数组表示形式 访问模型值,这意味着只有可见属性将用于表单。如果您想在表单中公开 Eloquent 模型的所有属性而不在数组或 json 表示形式中使它们可见,可以将 $model->getAttributes() 传递给 withValues()

容器布局

内联表单布局

在容器上调用 inline($inline = true) 将将所有表单控件块和包装器转换为 <span>,并尽可能避免任何块显示 HTML 元素。并非所有表单控件都适合内联显示,请自行决定是否使用。CSS 类也添加用于内联表单的样式。

包含与表单控件相关的消息的任何描述性元素都会分组并显示在内联内容之前,同时使用 aria-describedby 进行引用,以确保良好的可访问性。

对齐表单布局

使用 aligned($align = true) 在容器上配置水平对齐标签与它们的表单控件。对齐部分将渲染标签和表单控件的任何包装器为 <span> 并添加 CSS 类以进行样式设置。没有样式,标签和输入将只显示在同一行上,实际的对齐需要在 CSS 中完成。

包含与表单控件相关的消息的任何描述性元素在输入后保持块显示 HTML。

可以使用 withAlignmentClasses($classes1, $classes2, $classes3, $offset_classes2, $offset_classes3 = null) 在每个块容器上覆盖默认 CSS 类。当先前列不显示时,将打印 offset... 类,例如,对于没有标签包装器作为控件块第一个元素的复选框。

嵌套容器

在任何表单块容器上,如 <form>,可以使用 containingFieldset() 方法添加并返回一个嵌套的表单块容器。在任何表单块上,可以调用 followedByFieldset() 以实现相同的效果。

可以将字段集视为常规表单块容器,但它还有 withLegend($html_contents) 用于向其 <legend> 添加内容。

要在嵌套的容器外部添加更多的控制块,请在最后的项目上使用getControlBlockContainer(),然后使用followedBy...Block()。或者如果您想一直向上添加更多块到顶层容器,可以使用getForm()然后containing...Block()

// Fieldset
echo FluentForm::create()
    ->containingFieldset()->withLegend('A Group')
    ->containingInputBlock('inside')
    ->getControlBlockContainer()
    ->followedByInputBlock('outside');
<form class="form-block-container" method="POST">
<fieldset class="form-block-container">
<legend>A Group</legend>
<div class="form-block">
<div><label class="form-block__label" for="inside">Inside</label></div>
<div>
<input name="inside" type="text" class="form-block__control" id="inside">
</div>
</div>
</fieldset>
<div class="form-block">
<div><label class="form-block__label" for="outside">Outside</label></div>
<div>
<input name="outside" type="text" class="form-block__control" id="outside">
</div>
</div>
</form>

替代方案

https://github.com/formers/former 是另一个流畅风格的表单生成器,其主要不同之处在于它与 Laravel 的集成更为紧密,并且它仅对每个输入流畅,而不是整个表单。

作者

我是 Björn Nilsved,在 FEW 工作期间创建了此软件包。