Symfony2:如何将使用工厂构建器创建的表单添加到另一个表单的集合类型字段中?
I am creating forms from yml at runtime. I go through the elements in yml file and add appropriate fields to the form. The root form is created like this
$form = $factory->createBuilder("form", $this->userData);
The yml will have an option to define a collection field as well.
The collection field requires type
option to be supplied which must be of type string
, Symfony\Component\Form\ResolvedFormTypeInterface
, Symfony\Component\Form\FormTypeInterface
But since I am building the embedded form as well at runtime, I wont have a type and neither FormTypeInterface
Here is the sample code of what I need to do
$options = isset($config["options"]) ? $config["options"]: [];
if ($config['type'] == 'collection') {
$options['type'] = $this->buildForm($options['template'], $factory);
unset($options['template']);
$form->add($config["name"], $config["type"], $options);
}
Here $options['template']
is how the type for the embedded form is defined in yml file. So that form is also build at runtime. How do I embed it in the root form?
Edit:
In fact, if I only have a single field, say email
in the collection field, then it works fine. But the yml spec will allow users to define multiple fields within collection fields. In symfony, this would be done by defining the embedded form type and setting type
option of collection field type to that form type. But how do I do it when creating forms at runtime?
I solved it by defining a form type class, that builds the form at runtime. The config(form design) is passed into the constructor and buildForm
adds respective fields according to the design.
class RuntimeFormType extends AbstractType
{
/**
* @var string
*/
private $name;
/**
* @var array
*/
private $config;
/**
* RuntimeFormType constructor.
* @param string $name
* @param array $config
*/
public function __construct($name, array $config)
{
$this->name = $name;
$this->config = $config;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach($this->config as $config){
$options = isset($config["options"]) ? $config["options"]: [];
if($config['type'] == 'image'){
$options['data_class'] = null;
$options['multiple'] = false;
$options['attr'] = [
"accept" => "images/*"
];
$builder->add($config["name"], "file", $options);
}else{
$builder->add($config["name"], $config["type"], $options);
}
}
}
public function getName()
{
return $this->name;
}
}
and in the builder, when the type is collection
type initialize this form type and add to the root form.
if ($config['type'] == 'collection') {
$template = $options['template'];
unset($options['template']);
$options['type'] = new RuntimeFormType($config["name"], $template);
$form->add($themeConfig["name"], $themeConfig["type"], $options);
}