I need some help with prototypes that are within prototypes. Symfony is very clever with generating form prototypes, but once you are one layer down (i.e. prototypes within prototypes), it reuses $$name$$ for both prototypes.
This is what a prototype field looks like for my entity. JQuery swaps out $$name$$ with the correct index value (based on number of child nodes)
<input type="text" id="entry_entities_$$name$$_contactFax" name="entry[entities][$$name$$][contactFax]" value="" />
So far so good. But when you go one level deeper, Symfony uses $$name$$ for the next level down too - here is a prototype for the entity property:
<div id="entry_entities_123_properties" data-prototype="
<label for="entry_entities_$$name$$_properties_$$name$$_name">Name</label>
<input type="text" id="entry_entities_$$name$$_properties_$$name$$_name" name="entry[entities][$$name$$][properties][$$name$$][name]" value="" />
This means that (in this example with entity id 123) that all properties get ID 123:
name="entry[entities][123][properties][123][name]"
name="entry[entities][123][properties][123][name]"
name="entry[entities][123][properties][123][name]"
etc.
In my opinion the best way to solve the issue would be to use $$somethingelse$$ for the property - does anyone know where this is set - or does anyone have a complete example with JS on how to solve this? I embarked on a horrible find/replace of the second $$name$$ on each line, but it got very messy. I'm sure there is an easy way to do this, but I couldn't find any guides on the internet.
This code is for symfony 2.0 (in 2.1+ you can just pass the name to prototype()
function):
You can create your own collection type with your required option:
<?php
namespace YourBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener;
use Symfony\Component\Form\Extension\Core\Type\CollectionType as BaseCollectionType;
class CollectionType extends BaseCollectionType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilder $builder, array $options)
{
if ($options['allow_add'] && $options['prototype']) {
$prototype = $builder->create($options['prototype_name'], $options['type'], $options['options']);
$builder->setAttribute('prototype', $prototype->getForm());
}
$listener = new ResizeFormListener(
$builder->getFormFactory(),
$options['type'],
$options['options'],
$options['allow_add'],
$options['allow_delete']
);
$builder
->addEventSubscriber($listener)
->setAttribute('allow_add', $options['allow_add'])
->setAttribute('allow_delete', $options['allow_delete'])
;
}
/**
* {@inheritdoc}
*/
public function getDefaultOptions(array $options)
{
$defaultOptions = parent::getDefaultOptions($options);
$defaultOptions['prototype_name'] = '$$name$$';
return $defaultOptions;
}
}
Then just define a service with:
tags:
- { name: form.type, alias: collection }
And use it as Symfony's collection but with prototype_name parameter.