codezero / laravel-form-field-prefixer
重复使用表单部分,并自动为您的表单字段添加可选前缀和数组键。
Requires
- php: ^7.3|^8.0
- illuminate/support: ^7.0|^8.0|^9.0|^10.0
Requires (Dev)
- orchestra/testbench: ^5.0|^6.0|^7.0|^8.0
- phpunit/phpunit: ^9.0
README
使用可选前缀和数组键重复使用表单部分。
当您有大型表单并且想将它们拆分为可重复使用的部分时,您可能需要在部分中执行一些复杂的逻辑。为了简化这一点,我将逻辑隐藏在 FormFieldPrefixer
类后面。
✅ 要求
- PHP >= 7.1
- Laravel >= 5.6
📦 安装
composer require codezero/laravel-form-field-prefixer
Laravel 将自动注册
FormFieldPrefixer
门面。
🛠 使用方法
基本用法 - 简单表单
将 FormFieldPrefixer
实例传递给您的表单部分。
可选地,您可以为 make
方法提供可选前缀
@include('forms.address', ['prefixer' => FormFieldPrefixer::make('client')])
如果您不提供前缀,以下示例输出将删除 client_
。
在部分内部,您现在可以使用 $prefixer
动态生成字段名称
标签
生成 for
属性
<label {{ $prefixer->for('address') }}>Address:</label>
这将编译为
<label for="client_address">Address:</label>
输入字段
生成 name
、id
和 value
属性
您可以传递一个可选的默认值,该值将在表单加载时填充。
如果您提交表单并被重定向回带有输入的内容(例如,当您有验证错误时),则新值将覆盖默认值,并在幕后使用 Session::getOldInput($field)
填充。
<input {{ $prefixer->name('address') }} {{ $prefixer->id('address') }} {{ $prefixer->value('address', 'default value') }} >
这将编译为
<input name="client_address" id="client_address" value="default value">
选择字段
为选择和选定的选项生成 name
和 id
属性
selected
方法需要选项的值,并接受一个可选的默认值以确定该选项是否为选定的值。在下面的示例中,Mr.
被设置为默认值。
与输入字段一样,旧输入优先于默认值。
<select {{ $prefixer->name('title') }} {{ $prefixer->id('title') }}> @foreach(['Mr.', 'Mrs.'] as $option) <option value="{{ $option }}" {{ $prefixer->selected('title', $option, 'Mr.') }}> {{ $option }} </option> @endforeach </select>
这将编译为
<select name="client_title" id="client_title"> <option value="mr" selected="selected">Mr.</option> <option value="mrs">Mrs.</option> </select>
验证错误
生成可以传递给 @error
的验证键
@error($prefixer->validationKey('address')) {{ $message }} @enderror
这将编译为
@error('client_address') {{ $message }} @enderror
扁平数组表单
如果您需要,例如,多个地址,可以为每个部分指定一个数组键
@foreach(['billing', 'shipping'] as $type) @include('forms.address', ['prefixer' => FormFieldPrefixer::make('client')->asArray($type)]) @endforeach
部分将包含与前面示例相同的模板,但这次请注意 name
、id
和 error
键之间的区别
<input name="client_address[billing]" id="client_address_billing" value=""> @error('client_address.billing') {{ $message }} @enderror
或者,您也可以通过不预先指定键来使用字段名称作为数组键
@include('forms.address', ['prefixer' => FormFieldPrefixer::make('client')->asArray()])
现在 address
字段看起来像这样
<input name="client[address]" id="client_address" value=""> @error('client.address') {{ $message }} @enderror
同样,如果您没有指定前缀,client_
将被删除。
多维数组表单
您还可以使用多维数组表示法。为此,您始终需要指定前缀,因为这将作为输入的基本名称。如果不这样做,结果将像前面的示例一样是一个扁平数组。
@foreach(['billing', 'shipping'] as $type) @include('forms.address', ['prefixer' => FormFieldPrefixer::make('client')->asMultiDimensionalArray($type)]) @endforeach
部分将包含与前面示例相同的模板,但这次请注意输入的名称现在是数组键。
<input name="client[billing][address]" id="client_billing_address" value=""> @error('client.billing.address') {{ $message }} @enderror
带有VueJS的混合表单
免责声明:作为一名后端开发者,我不想完全投身于JavaScript,但我想添加足够的JavaScript来使事物正常工作。我正在使用VueJS,因此这个解决方案目前只能与VueJS配合使用。使PHP和VueJS配合这些动态表单正常工作比我想的要复杂得多(需要大量操作属性)。但由于我不想重复所有表单,我一直在敲击键盘直到这个包出现。到目前为止,我对工作结果非常满意。
此代码受支持,但未包含在此包中,因为它特定于项目。
请随意复制示例并根据需要调整。
再次强调,这是一个非常简单的示例,仅足以使事物正常工作!
示例用例
假设我们在有人点击按钮时动态添加和删除表单字段。对于每个我们添加的虚构“客户”,我们都需要他们填写包含的表单字段。
我们需要数据绑定,以便在我们添加和删除字段时保持表单字段的同步。当我们在提交时收到验证错误时,我们还需要恢复之前动态添加的任何字段及其填写的“旧输入”。
额外设置
要使此功能正常工作,我们需要某种JS“表单管理器”,它可以索引添加的客户并记录他们的所有输入数据。让我们创建一个Vue组件来完成这项工作
/* components/FormManager.vue */ <script> export default { props: { values: { required: true }, errors: { required: true }, }, data() { return { formValues: [], formErrors: [], } }, mounted() { this.formValues = this.values this.formErrors = this.errors }, methods: { add() { this.formValues.push({}) }, remove(index) { this.formValues.splice(index, 1) this.formErrors = [] }, getError(key) { return (this.formErrors[key] || [])[0] } }, render() { return this.$scopedSlots.default({ formValues: this.formValues, addFormEntry: this.add, removeFormEntry: this.remove, getError: this.getError, }) }, } </script>
别忘了在您的app.js
文件中注册组件
/* app.js */ window.Vue = require('vue'); Vue.component('form-manager', require('./components/FormManager').default); const app = new Vue({ el: '#app', });
使用表单管理器
让我们用这个新组件包裹我们的动态表单部分。我们还需要将其“旧输入”和验证错误传递给它,以防在表单提交失败后重定向。
<form-manager :values="{{ json_encode(old('clients', [])) }}" :errors="{{ json_encode($errors->getMessages()) }}" v-slot:default="{ formValues: clients, addFormEntry, removeFormEntry, getError }" > <div v-for="(client, index) in clients" :key="index"> <h2>Client #@{{ index + 1 }}</h2> @include('forms.address', ['prefixer' => FormFieldPrefixer::make('clients')->asMultiDimensionalArray('${ index || 0 }')]) <button type="button" @click="removeFormEntry(index)"> Remove Client </button> </div> <button type="button" @click="addFormEntry()"> Add Client </button> </form-manager>
我们在这里做的事情是
- 我们将任何旧的
clients
输入值以JSON格式馈送给form-manager
- 我们还以JSON格式将
errors
数组馈送给form-manager
- 我们使用作用域插槽来访问和绑定通过
clients
别名馈送给的formValues
- 我们使用作用域插槽来访问
addFormEntry
、removeFormEntry
和getError
方法 - 我们使用前缀
clients
与FormFieldPrefixer
- 我们使用JS字符串作为多维数组键,它最终将打印数组索引
clients
将是多维数组的基名- 此包将使用前缀
clients
作为变量名来生成输入绑定 formValues
别名应与FormFieldPrefixer
前缀相同- 我们添加了一个按钮来添加和删除一组客户表单字段
- 在此示例中,for循环中的
client
变量未使用
模板增强
要使用表单字段模板同时与PHP和JS一起使用,您需要为您的选择字段添加一项
<select {{ $prefixer->name('title') }} {{ $prefixer->id('title') }} {{ $prefixer->select('title') /* Add this line for JS compatibility */ }} > @foreach(['Mr.', 'Mrs.'] as $option) <option value="{{ $option }}" {{ $prefixer->selected('title', $option, 'Mr.') }}> {{ $option }} </option> @endforeach </select>
这将编译为
<select :name="`clients[${ index || 0 }][title]`" :id="`clients_${ index || 0 }_title`" v-model="clients[index || 0]['title']" > <option value="mr">Mr.</option> <option value="mrs">Mrs.</option> </select>
请注意,选项上不再有selected
属性,而是现在有v-model
属性。
当您在未传递任何JS键的组件中使用相同的模板时,将不会渲染v-model
,而是渲染selected
属性。因此,您可以为此“增强”模板使用所有用例。
其他表单字段模板不需要更改。
文本输入将自动获得一个v-model
属性,而不是一个value
属性。
默认值将被忽略
如果您想在页面加载时在JavaScript驱动的表单字段中加载默认值,您需要以与提供“旧输入”相同的方式将这些值馈送给form-manager
。
当使用 JS 键时,传递给 FormFieldPrefixer
方法的任何默认值都将被忽略。
验证错误
我最终提取了表单错误的部分
@php $prefixer = $prefixer ?? FormFieldPrefixer::make(); @endphp {!!-- For JS enabled fields that render client side... --!!} @if ($prefixer->isJavaScript()) <div v-show="!!getError({{ $prefixer->validationKey($field) }})"> <div v-text="getError({{ $prefixer->validationKey($field) }})"></div> </div> @endif {!!-- For normal fields that render server side... --!!} @error($prefixer->validationKey($field)) <div>{{ $message }}</div> @enderror
然后只需包含它...
@include('partials.form-error', ['field' => 'field_name'])
浏览器兼容性(JS)
与所有现代浏览器兼容,除了 Internet Explorer,因为它使用模板字符串
https://mdn.org.cn/en-US/docs/Web/JavaScript/Reference/Template_literals
修改或删除属性名称
如果您只想获取属性的值,而不是名称和引号,可以将 null
作为第二个参数传递给 name
、id
和 for
方法,或者作为 value
方法的第三个参数。
<input name="{{ $prefixer->name('some_field', null) }}"> // will echo: some_field
您还可以指定不同的属性名称
<label {{ $prefixer->id('some_field', 'for') }}> // will echo: for="some_field"
提供备用 FormFieldPrefixer
如果您希望在未传递 FormFieldPrefixer
实例的情况下使用您的部分,您可以将以下代码片段添加到部分顶部
@php $prefixer = $prefixer ?? FormFieldPrefixer::make(); @endphp
这将确保始终有一个可用的 $prefixer
变量。
当然,您可以选择另一个变量名称。
🚧 测试
composer test
☕️ 致谢
🔓 安全性
如果您发现任何与安全相关的问题,请通过电子邮件 联系我,而不是使用问题跟踪器。
📑 更新日志
在 更新日志 中查看重要更改列表。
📜 许可证
MIT 许可证(MIT)。有关更多信息,请参阅 许可证文件。