FormType
In your case we need to create formType for PersonaDocumento
. Imagine, that this entity has field documentName:
class PersonaDocumentoType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
* @SuppressWarnings(unused)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('documentName', TextType::class, [
'label' => false,
'translation_domain' => 'messages'
])
;
}
/**
* @return string
*/
public function getName()
{
return 'app_persona_documento_type';
}
/**
* @return null|string
*/
public function getBlockPrefix()
{
return 'app_persona_documento';
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => PersonaDocumento::class,
'csrf_protection' => true,
'validation' => true,
));
}
}
Form that contain collection
Consider you entity Formulario
. It has a OneToMany relation to PersonaDocumento. And Form will be:
class FormularioFormType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder
->add('documentos', CollectionType::class, [
'entry_type' => PersonaDocumentoType::class,
'entry_options' => [
'label' => false,
],
'label' => false,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false, // Very important thing!
])
;
}
// ...
}
Widget for collection
We have a form (FormularioFormType) that contain collection of small forms with type PersonaDocumentoType.
New widget you can create in file with standard widgets, and name of the file is fields.html.twig
: path_to_your_project/src/AppBundle/Resources/views/Form/fields.html.twig
.
Name of the block will be app_persona_documento_widget
.
Thus, example of fields.html.twig
:
{% trans_default_domain 'messages' %}
{% block app_persona_documento_widget %}
{% spaceless %}
<div class="form-group" {{ block('widget_container_attributes') }}>
<div class="col-sm-12">
{{ form_widget(form.name, {'attr' : { 'placeholder' : 'app.form.label.name'|trans , 'class' : 'form-control' }}) }}
</div>
</div>
{% endspaceless %}
{% endblock app_persona_documento_widget %}
Also pay attention that "app_persona_documento_widget" - assembled from the getBlockPrefix()
of you PersonaDocumentoType plus string "_widget"
Register new form themes in config.yml
# Twig Configuration
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
form_themes:
# other form themes
# ...
- 'AppBundle:Form:fields.html.twig'
Render collection in parent form
{{ form_start(formulario_form) }}
<div class="form-group">
<label for="" class="col-sm-2 control-label">
Label
</label>
<div class="col-sm-10 documentos" data-prototype="{{ form_widget(formulario_form.documentos.vars.prototype)|e('html_attr') }}">
{% for document in formulario_form.documentos %}
<div>
{{ form_widget(document) }}
</div>
{% endfor %}
</div>
</div>
<span>
{{ form_errors(formulario_form) }}
</span>
{# Here you can add another fields of form #}
{{ form_end(formulario_form) }}
Of course, you also need buttons: one "Add another document" button and "Remove" buttons for each "Documento" item.
Symfony documentation suggests that we use JavaScript for this purpose.
You can read more here in official docs
Also you can install Ninsuo/symfony-collection - A jQuery plugin that manages adding, deleting and moving elements from a Symfony collection