Symfony 2.3 add form collections of entities - forms

I'm triying to make a dynamic form adding collections inside an entity.
I have followed the code example in the Symfony's documentation, and it works, but what I want to do is add a new form (the form of the entity collections).
So, if I have an entity A that contains a collection of entities B, I want to add new entities B dynamically in the form, but I don't know how to do it.
The entity A form should be something like:
$builder->add('entityB', 'collection', array(
'type' => 'HOW TO PUT THE FORM OF THE ENTITY B???',
'options' => array(
'required' => false,
),
'allow_add'=>true,
));

Taken from the Cookbook:
$builder->add('entityB', 'collection', array(
'type' => new EntityBType(),
'options' => array(
'required' => false
),
'allow_add' => true
));
This is assuming that you have created a Form Type Class for EntityB (not manually creating it when needed in your controller). The linked cookbook entry gives a lot of good examples based on per-case situations.

Related

Provide default data in collection child form

I have a nested form with prototype feature in Symfony 2. Here is the parent form which contains the collection:
$builder
->add('rooms', 'collection', array(
'type' => new RoomForm(),
'allow_add' => true,
'allow_delete' => true,
'data' => array(new RoomForm()),
))
As you can see, no data_class is defined. After the form submission $form->getData() correctly return associative array.
RoomForm is a simple form class composed by two fields:
$builder
->add(
$builder->create('dateAvailabilityStart', 'text', array(
'label' => 'label.from'
)))
->add(
$builder->create('dateAvailabilityEnd', 'text', array(
'label' => 'label.until'
)))
I would like find a way to populate my collection with existing RoomForm (for edit mode) and associate data in correct fields.
Any ideas?
You could do it from within your controller. Given that above form type is named as RoomFormCollection you could do something like this:
// This should be an array
$rooms = ... // Either from database or...
$form = $this->createForm(new RoomFormCollection(), array(
'rooms' => rooms
));
Another thing, 'data' => array(new RoomForm()), is not valid. RoomForm as its name suggests is a form type, not data struct. You should remove it...
Hope this helps...

Symfony - Combine two properties in a single entity form field

I have an entity for publications with many different fields as booktitle, conference etc. I want to build a search form and one of the feature requests is to combine two search parameters in a single choice field. So far I have something like this in the form builder:
$builder->add('booktitle', 'entity', array(
'required' => false,
'label' => 'Conference/Booktitle',
'property' => 'booktitle',
'class' => 'indPubBundle:Publication',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('p')
->groupBy('p.booktitle')
->orderBy('p.booktitle', 'ASC');
}
));
Basically I am displaying all booktitles as a choice field. What I want now is to have the conferences as well in the same choice field. Is there a way to achieve that?
The Entity field type is a child of the Choice field type. So you can provide Data via the "choices" parameter. Combine that with a (e.g. repository) method that returns an array with the data you need might be a solution that works for you.
$builder->add('booktitle', 'entity', array(
'required' => false,
'label' => 'Conference/Booktitle',
'class' => 'indPubBundle:Publication',
'choices' => $this->getDoctrine()->getRepository('indPubBundle:Publication')->getData(),
));

create a form for several entities symfony2.3 (best practice)

I want to create a form from several entities (article, category, adress, attributes, gallery) with custom fields (remove fields, customize css, ...),
what is the best practice with Symfony2 and forms
thank you.
Typically you want to you an embedded collection of forms to do this. An example shown bellow:
$builder->add('subject','text', array(
'required' => false,
));
$builder->add('body','textarea', array(
'required' => false,
));
$builder->add('files','collection', array(
'type' => new DocumentForm(),
'allow_add' => true,
'allow_delete' => true,
'label' => false,
));
This form binds to my message entity, however, you still have a collection type which relates to a different form that is attached to my file entity.
You want to embed entities and forms. You can find more information on this here:
http://symfony.com/doc/current/cookbook/form/form_collections.html

Remove null values coming from empty collection form item

I'm trying to implement a ManyToMany relation in a form between 2 entities (say, Product and Category to make simpe) and use the method described in the docs with prototype and javascript (http://symfony.com/doc/current/cookbook/form/form_collections.html).
Here is the line from ProductType that create the category collection :
$builder->add('categories', 'collection', array(
'type' => 'entity',
'options' => array(
'class' => 'AppBundle:Category',
'property'=>'name',
'empty_value' => 'Select a category',
'required' => false),
'allow_add' => true,
'allow_delete' => true,
));
When I had a new item, a new select appear set to the empty value 'Select a category'. The problem is that if I don't change the empty value, it is sent to the server and after a $form->bind() my Product object get some null values in the $category ArrayCollection.
I first though to test the value in the setter in Product entity, and add 'by_reference'=>false in the ProductType, but in this case I get an exception stating that null is not an instance of Category.
How can I make sure the empty values are ignored ?
Citing the documentation on 'delete_empty':
If you want to explicitly remove entirely empty collection entries from your form you have to set this option to true
$builder->add('categories', 'collection', array(
'type' => 'entity',
'options' => array(
'class' => 'AppBundle:Category',
'property'=>'name',
'empty_value' => 'Select a category'),
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true
));
Since you use embedded forms, you could run in some issues such as Warning: spl_object_hash() expects parameter 1 to be object, null given when passing empty collections.
Removing required=>false as explained on this answer did not work for me.
A similar issue is referenced here on github and resolved by the PR 9773
I finally found a way to handle that with Event listeners.
This discussion give the meaning of all FormEvents.
In this case, PRE_BIND (replaced by PRE_SUBMIT in 2.1 and later) will allow us to modify the data before it is bind to the Entity.
Looking at the implementation of Form in Symfony source is the only source of information I found on how to use those Events. For PRE_BIND, we see that the form data will be updated by the event data, so we can alter it with $event->setData(...). The following snippet will loop through the data, unset all null values and set it back.
$builder->addEventListener(FormEvents::PRE_BIND, function(FormEvent $event){
$data = $event->getData();
if(isset($data["categories"])) {
foreach($data as $key=>$value) {
if(!isset($value) || $value == "")
unset($data[$key]);
}
$event->setData($data);
});
Hope this can help others !
Since Symfony 3.4 you can pass a closure to delete_empty:
$builder
->add('authors', CollectionType::class, [
'delete_empty' => function ($author) {
return empty($author['firstName']);
},
]);
https://github.com/symfony/symfony/commit/c0d99d13c023f9a5c87338581c2a4a674b78f85f

Example of Zend Form with Collection Element using Forms not Fieldsets in Zend Framework2

I need a straight forward working example how I can include a collection element in Zend Form, I have seen some examples from Zend Framework 2 site and from previous posts in StackOverflow where most of them pointed to this link. But right now I am not using Fieldsets and staying with Forms, so in case if someone can direct me in the right way, how I can include a simple collection element when the user gets a page where the user can choose multiple choices from the shown collection form. Much better would be populating the collection form from database.
I have searched in the internet for quite a sometime now and thought I would post here, so that Zend profis can give their suggestions.
Just For Information:
Normally one can include a static dropdownbox in Zend Form in this fashion
$this->add(
array(
'name' => "countr",
'type' => 'Zend\Form\Element\Select',
'options' => array(
'label' => "Countries",
'options' => array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
)
)
)
);
So I am expecting a simple example which could give me a basic idea how this can be done.
To be honest, I don't see your problem here. Since form collections extend Fieldset which extends Element, you can just add it to the form as a regular element. The view helpers will take care of the rendering recursively.
Step 1: Create a form collection (create an instance of Zend\Form\Element\Collection). If the elements have to be added dynamically in some way, I'd create a factory class for this purpose.
Step 2: Add it to the form. (For example using $form->add($myCollectionInstance).)
Step 3: Render it. Zend\Form\View\Helper\Collection is a pretty good view helper to render the whole form without any pain.
You can also create a new class extending Zend\Form\Element\Collection and use the constructor to add the fields you need. Thus, you can add it to the form using the array you've pasted in your question. Also, you could directly use it in annotations.
Hope this helps.
If you just want to fill in a select list with option values you can add the array to the select list in a controller:
$form = new MyForm();
$form->get('countr')->setOptions(array('value_options'=>array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
));
the array can be fetched from db.
this is a different example for using form collections in the simplest way.
In this example it creates input text elements in a collection and fills them in. The number of elements depends on the array:
class MyForm extends \Zend\Form\Form
{
$this->add(array(
'type' => '\Zend\Form\Element\Collection',
'name' => 'myCollection',
'options' => array(
'label' => 'My collection',
'allow_add' => true,
)
));
}
class IndexController extends AbstractActionController
{
public function indexAction
{
$form = new MyForm();
$this->addElementsFromArray($form, array(
'country1' => 'Brazil',
'country2' => 'USA',
'country3' => 'Mexico',
'country4' => 'France',
));
//the above line can be replaced if fetching the array from a db table:
//$arrayFromDb = getArrayFromDb();
//$this->addElementsFromArray($form, $arrayFromDb);
return array(
'form' => $form
);
}
private function addElementsFromArray($form, $array)
{
foreach ($array as $key=>$value)
{
$form->get('myCollection')->add(array(
//'type' => '\Zend\Form\Element\SomeElement',
'name' => $key,
'options' => array(
'label' => $key,
),
'attributes' => array(
'value' => $value,
)
));
}
}
}
index.phtml:
$form->setAttribute('action', $this->url('home'))
->prepare();
echo $this->form()->openTag($form);
echo $this->formCollection($form->get('myCollection'));
echo $this->form()->closeTag();