codezero/laravel-form-field-prefixer

重复使用表单部分,并自动为您的表单字段添加可选前缀和数组键。

1.5.0 2023-03-01 12:14 UTC

This package is auto-updated.

Last update: 2024-08-29 15:20:43 UTC


README

GitHub release Laravel License Build Status Code Coverage Scrutinizer Code Quality Total Downloads

ko-fi

使用可选前缀和数组键重复使用表单部分。

当您有大型表单并且想将它们拆分为可重复使用的部分时,您可能需要在部分中执行一些复杂的逻辑。为了简化这一点,我将逻辑隐藏在 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>

输入字段

生成 nameidvalue 属性

您可以传递一个可选的默认值,该值将在表单加载时填充。

如果您提交表单并被重定向回带有输入的内容(例如,当您有验证错误时),则新值将覆盖默认值,并在幕后使用 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">

选择字段

为选择和选定的选项生成 nameid 属性

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

部分将包含与前面示例相同的模板,但这次请注意 nameiderror 键之间的区别

<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
  • 我们使用作用域插槽来访问addFormEntryremoveFormEntrygetError方法
  • 我们使用前缀clientsFormFieldPrefixer
  • 我们使用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 作为第二个参数传递给 nameidfor 方法,或者作为 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)。有关更多信息,请参阅 许可证文件