To satisfy the requirement (Add support for custom form fields without any plugin or library), I developed a module which allows admin to perform below actions.
- Admin can create custom form fields.
- Fields data should be encrypted when stored into database.
- It can be mapped with multiple countries so if user belongs to that country, selected custom form fields will appear in profile.
Below is my code. Kindly suggest if there are any improvements needed.
Migration file to create custom form fields
public function up()
{
Schema::create('custom_forms', function (Blueprint $table) {
$table->id();
$table->string('title', 250);
$table->string('field_title', 250);
$table->string('field_name', 250);
$table->string('field_input_name', 250);
$table->enum(
'field_type',
[
'text',
'textarea',
'number',
'email',
'date',
'time',
'select',
'radio',
'checkbox',
'tel',
'url'
]
)->default('text');
$table->string('field_min_value', 10)->nullable();
$table->string('field_max_value', 10)->nullable();
$table->text('field_option_values')->nullable();
$table->string('field_validation_pattern', 250)->nullable();
$table->string('field_validation_message', 250)->nullable();
$table->string('field_placeholder', 250)->nullable();
$table->tinyInteger('is_required')->default(0);
$table->integer('country_id');
$table->tinyInteger('is_active')->default(1);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('custom_forms');
}
Migration file to store custom form field values
public function up()
{
Schema::create('custom_form_values', function (Blueprint $table) {
$table->id();
$table->foreignId('form_id')->references('id')->on('custom_forms');
$table->foreignId('user_id')->references('id')->on('users');
$table->mediumText('value')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('custom_form_values');
}
White creating a custom form field, admin can select multiple countries for single. So, I've created a loop for all selected countries and stored the data.
CustomFormController.php
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
foreach ($request->country_id as $country) {
$custom_form = new CustomForm();
$custom_form->title = $request->name;
$custom_form->field_title = $request->name . '-' . Str::random(10);
$custom_form->field_name = $request->field_name;
$custom_form->field_input_name = preg_replace('/\s+/', '', $request->name) . '-' . Str::random(10);
$custom_form->field_type = $request->field_type;
$custom_form->field_min_value = $request->field_min_value;
$custom_form->field_max_value = $request->field_max_value;
$custom_form->field_option_values = $request->field_option_values;
$custom_form->field_validation_pattern = $request->field_validation_pattern;
$custom_form->field_validation_message = $request->field_validation_message;
$custom_form->field_placeholder = $request->field_placeholder;
$custom_form->is_required = $request->field_is_required;
$custom_form->country_id = $country;
$custom_form->save();
}
return redirect()->route('admin.custom-forms.index');
}
In CustomForm.php model, added relation to the values to get data for selected custom form field.
public function values()
{
return $this->belongsTo(CustomFormValue::class, 'id', 'form_id')->select(['id', 'value','form_id']);
}
In CustomFormValue.php model, defined encrypted field to match requirement no. 2.
protected $casts = [
'value' => 'encrypted',
];
Below code is from edit_profile.blade.php to display all custom form fields.
@forelse ($custom_forms as $form)
<div class="form-group">
<label class="required"
for="{{ $form->field_input_name }}">{{ $form->field_name }}</label>
{{-- FOr Text area --}}
@if ($form->field_type === 'textarea')
<textarea class="form-control" type="{{ $form->field_type }}"
name="custom_form_{{ $form->id }}" id="{{ $form->field_input_name }}"
minlength="{{ $form->field_min_value }}"
maxlength="{{ $form->field_max_value }}"
@if($form->field_validation_pattern) pattern="{{ $form->field_validation_pattern }}" @endif
title="{{ $form->field_validation_message }}"
placeholder="{{ $form->field_placeholder }}"
{{ $form->is_required == 1 ? 'required' : '' }}>{{ $form->values ? $form->values->value : null }}</textarea>
<span class="help-block">{{ $form->field_placeholder }}</span>
{{-- for select option --}}
@elseif ($form->field_type === 'select')
@php
$option_values = explode(',', $form->field_option_values);
$is_selected = $form->values ? $form->values->value : '';
@endphp
<div class="form-group">
<select class="form-control" type="{{ $form->field_type }}"
name="custom_form_{{ $form->id }}"
id="{{ $form->field_input_name }}"
@if($form->field_validation_pattern) pattern="{{ $form->field_validation_pattern }}" @endif
title="{{ $form->field_validation_message }}"
placeholder="{{ $form->field_placeholder }}"
{{ $form->is_required == 1 ? 'required' : '' }}>
@forelse ($option_values as $value)
<option @if ($value == $is_selected) selected @endif value="{{ $value }}">
{{ $value }}</option>
@empty
@endforelse
</select>
<span class="help-block">{{ $form->field_placeholder }}</span>
</div>
{{-- for radio buttons --}}
@elseif ($form->field_type === 'radio')
@php
$option_values = explode(',', $form->field_option_values);
$is_selected = $form->values ? $form->values->value : '';
@endphp
<div class="col-sm-10">
@forelse ($option_values as $value)
<div>
<label>
<input class=" " type="{{ $form->field_type }}"
name="custom_form_{{ $form->id }}"
id="{{ $form->field_input_name }}"
@if($form->field_validation_pattern) pattern="{{ $form->field_validation_pattern }}" @endif
title="{{ $form->field_validation_message }}"
placeholder="{{ $form->field_placeholder }}"
value="{{ $value }}" @if ($value == $is_selected) checked @endif
{{ $form->is_required == 1 ? 'required' : '' }}>{{ $value }}
</label>
</div>
@empty
@endforelse
</div>
{{-- for checkbox --}}
@elseif ($form->field_type === 'checkbox')
@php
$option_values = explode(',', $form->field_option_values);
$is_selected = $form->values ? $form->values->value : '';
@endphp
<div class="col-sm-10">
@forelse ($option_values as $value)
<div>
<label>
<input class=" " type="{{ $form->field_type }}"
name="custom_form_{{ $form->id }}[]"
id="{{ $form->field_input_name }}"
@if($form->field_validation_pattern) pattern="{{ $form->field_validation_pattern }}" @endif
title="{{ $form->field_validation_message }}"
placeholder="{{ $form->field_placeholder }}"
value="{{ $value }}" @if (str_contains($is_selected,$value)) checked @endif
>{{ $value }}
</label>
</div>
@empty
@endforelse
</div>
@else
<input class="form-control " type="{{ $form->field_type }}"
name="custom_form_{{ $form->id }}" id="{{ $form->field_input_name }}"
minlength="{{ $form->field_min_value }}"
maxlength="{{ $form->field_max_value }}" {{-- pattern="{{ $form->field_validation_pattern }}" --}}
title="{{ $form->field_validation_message }}"
placeholder="{{ $form->field_placeholder }}"
value="{{ $form->values ? $form->values->value : null }}"
@if($form->field_validation_pattern) pattern="{{ $form->field_validation_pattern }}" @endif
{{ $form->is_required == 1 ? 'required' : '' }}>
<span class="help-block">{{ $form->field_placeholder }}</span>
@endif
</div>
@empty
{{-- Message to display if there is no custom field added--}}
@endforelse
To store user entered custom form field values, in UserController.php
foreach ($custom_values as $key => $value) {
$form_value = Str::contains($key, 'custom_form_');
if ($form_value == 1) {
$form_id = str_replace('custom_form_', '', $key);
$custom_form_value = CustomFormValue::updateOrCreate([
'form_id' => $form_id,
'user_id' => Auth::user()->id
], [
'form_id' => $form_id,
'user_id' => Auth::user()->id,
'value' => is_array($value) ? implode(', ', $value) : $value
]);
}
}
One user can have many custom form field values. So, in User.php model,
public function customFormValue()
{
return $this->hasMany(CustomFormValue::class, 'user_id');
}
And finally, to display custom form values, in profile.blade.php
@forelse ($custom_forms as $form)
<tr>
<td>
{{ $form->field_name }}: <strong class="pull-right">{{ $form->values ? $form->values->value : '' }}</strong>
</td>
</tr>
@empty
{{-- Message if any --}}
@endforelse
So, this is what I have done so far to add support for custom form field without any plugin or library. Let me know if there is any room for improvement.
I hope it may help others who are looking for something similar.