Installation:
composer require alsatian/form-bundle
Add to config/bundles.php (Symfony 4+):
return [
// ...
Alsatian\FormBundle\AlsatianFormBundle::class => ['all' => true],
];
Basic Configuration (config/packages/alsatian_form.yaml):
alsatian_form:
extensible_choice: ~
extensible_entity: ~
extensible_document: ~
First Use Case:
Replace a standard ChoiceType with ExtensibleChoiceType in a form builder:
use Alsatian\FormBundle\Form\ExtensibleChoiceType;
$builder->add('tags', ExtensibleChoiceType::class, [
'route' => 'ajax_tags',
'attr_class' => 'select2-tags', // Optional: Override default class
]);
config/packages/alsatian_form.yaml (Configuration)src/Form/ExtensibleChoiceType.php (Core type example)templates/form/fields.html.twig (Optional: Override Twig templates)route and route_params to define an AJAX endpoint:
$builder->add('user', ExtensibleEntityType::class, [
'class' => User::class,
'choice_label' => 'email',
'route' => 'app_user_autocomplete',
'route_params' => ['role' => 'admin'],
]);
$('.select2').select2({
ajax: {
url: $(this).data('ajax-url'), // Auto-populated by bundle
dataType: 'json',
delay: 250,
data: function(params) {
return { q: params.term, role: $(this).data('role') }; // Match route_params
},
processResults: function(data) {
return { results: data.map(item => ({ id: item.id, text: item.email })) };
}
}
});
ExtensibleChoiceType for tags/categories where choices are added dynamically:
$builder->add('categories', ExtensibleChoiceType::class, [
'multiple' => true,
'attr_class' => 'select2-categories',
'route' => 'ajax_categories',
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
// $data['categories'] now includes both pre-defined and dynamically added choices
}
DateType/DateTimeType with DatepickerType/DateTimepickerType:
$builder->add('birthday', DatepickerType::class);
pattern attribute auto-generated by the bundle.$builder->add('author', ExtensibleEntityType::class, [
'class' => Author::class,
'choice_label' => 'fullName',
'route' => 'app_author_autocomplete',
]);
$builder->add('document', ExtensibleDocumentType::class, [
'class' => ProductDocument::class,
'choice_label' => 'name',
'route' => 'app_product_autocomplete',
]);
$builder->add('search', AutocompleteType::class, [
'route' => 'app_search_autocomplete',
'attr_class' => 'autocomplete-search',
]);
alsatian_form:
extensible_choice:
attr_class: 'my-custom-select2'
$builder->add('field', ExtensibleChoiceType::class, [
'attr' => [
'data-custom' => 'value',
'data-role' => 'admin',
],
]);
$form->getData(). Example:
$form->submit($data);
$submittedChoices = $data['extensible_field']; // Array of all choices (pre-defined + dynamic)
{# templates/form/fields.html.twig #}
{% block alsatian_extensible_choice_widget %}
<div class="custom-wrapper">
{{ form_widget(form) }}
</div>
{% endblock %}
config/packages/alsatian_form.yaml manually.public function testAutocompleteRoute(): void
{
$client = static::createClient();
$client->request('GET', '/ajax/tags?q=test');
$this->assertJson($client->getResponse()->getContent());
}
$form->submit([
'extensible_field' => ['existing_choice', 'new_dynamic_choice'],
]);
$this->assertTrue($form->isValid());
route option is missing or the route doesn’t exist.# config/routes.yaml
ajax_tags:
path: /ajax/tags
controller: App\Controller\AjaxController::tagsAction
data-ajax--url. If empty, the route option is misconfigured.choice_label must match the entity property exactly (including case). E.g., fullName vs. fullname.property_path for complex labels:
$builder->add('user', ExtensibleEntityType::class, [
'class' => User::class,
'choice_label' => 'property_path(user.fullName)',
]);
ExtensibleDocumentType may not work with nested documents or complex queries.[id, label] pairs:
public function findByName($name)
{
return $this->createQueryBuilder()
->field('name')->equals(new \MongoDB\BSON\Regex($name))
->project(['id' => true, 'name' => true])
->getQuery()
->execute()
->toArray();
}
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
}
});
setDefaultOptions) may trigger deprecation warnings.use Alsatian\FormBundle\Form\ExtensibleChoiceType as BaseType;
class CustomExtensibleChoiceType extends BaseType
{
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'attr' => ['data-custom' => 'value'],
]);
}
}
How can I help you explore Laravel packages today?