I have a form which contains a collection. So I have:
/* my type */
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('photos','collection',array(
'type'=> new PhotoType(),
'allow_add'=>true));
}
/*Photo Type*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('photoname')
->add('size')
}
But I want to access the data inside the photo, so I tried inside the PhotoType:
$data = $builder->getData();
But it seems that it doesn't work, even if I am editting the form, so the photo collection has data. Why can't I access to the $builder->getData() in a form called by another?? Because I'm trying not to do and eventListener...
To understand what is happening here you have to understand data mapping first. When you call
the form's data mapper is responsible for taking the given array (or object) and writing the nested values into the fields of the form, i.e. calling
But in your example, you are not dealing with
Form
, but withFormBuilder
objects.FormBuilder
is responsible for collecting the configuration of a form and using this information to produce aForm
instance. As such,FormBuilder
also lets you store the default data for the form. But since it's a simple configuration object only, it will not invoke the data mapper as of yet. For example:This example will output:
because data mapping takes place later, when we turn the
FormBuilder
into aForm
instance. We can use this fact to set separate default values for the individual fields:And the output:
Data locking is required to prevent the data mapper from overriding the default data you just stored. This is done automatically if you pass the "data" option.
At last, you will build the form. Now,
FormBuilder
callsForm::setData()
where necessary, which in turn will invoke the data mapper:In a submit or if you are editing, you can access the data when you turn the FormBuilder into a Form instance. And for a collection type you can try this:
In my case did not need the data necessarily when building the form, but when building the view (later). Just next to the buildForm function of my subform type class, I added the buildView function:
Because buildView is called later, the data is available there. In this example I used it to change the label of the form row of each item in the collection. Check out the list of possible vars.
To add to Chadwick Meyer, (in Symfony 4, but probably applies to earlier versions), an event listener is needed to access the data in a collection because many times data hasn't been created yet and/or hasn't been associated or embedded to the collection. However, there are some intricacies associated with actually getting at the data in the collection via the event listener that becomes important in everyday usage.
In your photo form builder, you'll have to include an event listener:
However... if you want to do something with it, you need to make sure you test for nulls as the event is triggered multiple times before and after the data is actually created dynamically. For example, if you want to modify the form on the fly like add some sort of submit button:
Finally, please note that in certain cases, not all the data will be associated with a particular entity until it is actually persisted in the database. For example, if the entity is being newly created, it will not have its id yet which is usually automatically generated by doctrine or similar during persist. Therefore, in order to associate a submit button or similar to this entity in the collection before it is persisted, you'll probably have to make the 'name' field unique or create a separate field for the entity to hold a unique type parameter and generate it in a unique manner prior to persisting in order to associate something like a submit button to the entity during form creation.
As Bernhard indicated, listeners are the only way to do this because the data is not available in the sub form yet. I used the eventListener to solve a similar requirement. Below is a simplified version of my code that I hope will be helpful:
I have a parent form for my
View
entity which has a lot of fields, as well as collection of other forms. One of the sub forms is for an associated entityViewVersion
, which actually needs to load another form collection for a dynamic entity that is the content type associated with theView
. This content type could by one of many different types of entities, e.g Article, Profile, etc. So I need to find out what contentType is set in theView
data and then find the dynamic path to that bundle, and include that formType.Once you know how to do it, it's actually easy!
That's it. I'm new to Symfony, so the idea of doing everything in an EventListener is foreign to me (and seems unnecessarily complex). But I hope once I understand the framework better, it will seem more intuitive. As this example indicates, it's not that complicated to do it with an Event Listener, you just wrap your code in that closure (or put it in it's own separate function as described in the docs).
I hope that helps someone!