alexandre-fernandez/key-value-form-bundle
Installation
composer require alexandre-fernandez/key-value-form-bundle
Add to config/bundles.php (Symfony) or config/app.php (Laravel via bridge):
AlexandreFernandez\KeyValueFormBundle\AlexandreFernandezKeyValueFormBundle::class => ['all' => true],
Basic Usage Register the form type in a Symfony controller (or Laravel via bridge):
use AlexandreFernandez\KeyValueFormBundle\Form\Type\KeyValueType;
$builder->add('settings', KeyValueType::class, [
'label' => 'Configuration Settings',
'required' => false,
'entry_type' => TextType::class, // Default entry type (Symfony Form)
'entry_options' => ['attr' => ['class' => 'form-control']],
]);
First Use Case Store dynamic key-value pairs (e.g., user preferences, API configs):
// In a Laravel controller
public function updateSettings(Request $request) {
$form = $this->createForm(KeyValueType::class, $request->user()->settings);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$request->user()->update(['settings' => $form->getData()]);
return redirect()->back()->with('success', 'Settings saved!');
}
}
Dynamic Key-Value Management
// Laravel Form Request example
public function rules() {
return [
'dynamic_data' => ['required', KeyValueType::class, [
'entry_type' => TextareaType::class, // Customize entry fields
'allow_add' => true,
'allow_delete' => true,
'prototype' => true, // Enable AJAX row addition
]],
];
}
Integration with Laravel Collections
Convert form data to a Laravel Collection:
$data = $form->getData();
$collection = collect($data)->mapWithKeys(fn($value, $key) => [$key => $value]);
Validation Patterns
$builder->add('metadata', KeyValueType::class, [
'entry_options' => [
'constraints' => [
new NotBlank(),
new Length(['max' => 255]),
],
],
]);
Twig Template Customization
Override the default template in resources/views/form/key_value_widget.html.twig:
{% extends '@KeyValueForm/key_value_widget.html.twig' %}
{% block key_value_row %}
<div class="row">
<div class="col-md-3">{{ form_row(entry.key) }}</div>
<div class="col-md-6">{{ form_row(entry.value) }}</div>
</div>
{% endblock %}
CSRF Token Issues
$.ajaxSetup({
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }
});
Data Serialization
json:encode() may break nested structures. Use json_encode($data, JSON_UNESCAPED_UNICODE) for complex values.Prototype Cloning
prototype is enabled but rows don’t clone, verify:
prototype_name option is set (default: key_value_prototype).Symfony/Laravel Bridge Quirks
FormRequest may not auto-bind KeyValueType data. Explicitly cast:
$form->getData() === (array) $request->input('dynamic_data');
Dump Raw Data
dd($form->getData()); // Check structure before saving
Template Overrides
php artisan view:clear
Validation Errors
dump($form->getErrors()) to inspect nested errors.Custom Entry Types
$builder->add('tags', KeyValueType::class, [
'entry_type' => EntityType::class,
'entry_options' => [
'class' => Tag::class,
'choice_label' => 'name',
],
]);
Dynamic Entry Options
$builder->add('pricing', KeyValueType::class, [
'entry_options' => function ($key, $value, FormInterface $form) {
return ['attr' => ['data-key' => $key, 'data-value' => $value]];
},
]);
Laravel Service Provider Hooks Extend the bundle via a service provider:
public function boot() {
KeyValueType::defaultOptions(function (OptionsResolver $resolver) {
$resolver->setDefaults([
'attr' => ['class' => 'key-value-form'],
]);
});
}
How can I help you explore Laravel packages today?