我正在使用Symfony 2.2与Doctrine构建一个表单生成器。基本原理是,用户通过填写表单名称并在选择菜单中选择他想要的小部件来创建新表单。
我们可以想到WidgetInputText, WidgetSelect, WidgetFile等
这是我的模型的一个例子:
<?php
namespace IneatFormGeneratorBundleEntityWidget;
use SymfonyComponentValidatorConstraints as Assert;
use DoctrineORMMapping as ORM;
/**
* Widget
*
* @ORMTable(name="widget")
* @ORMEntity
* @ORMInheritanceType("SINGLE_TABLE")
* @ORMDiscriminatorColumn(name="discr", type="string")
* @ORMDiscriminatorMap({"widget_text" = "WidgetText", "widget_input_text" = "WidgetInputText", "widget_select" = "WidgetSelect"})
*/
abstract class Widget
{
/**
* @var integer
*
* @ORMColumn(name="id", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORMManyToOne(targetEntity="Form", inversedBy="widgets")
*/
private $form;
/**
* @var integer
*
* @ORMOneToOne(targetEntity="Question")
*/
private $question;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set form
*
* @param IneatFormGeneratorBundleEntityForm $form
* @return Widget
*/
public function setForm(IneatFormGeneratorBundleEntityForm $form = null)
{
$this->form = $form;
return $this;
}
/**
* Get form
*
* @return IneatFormGeneratorBundleEntityForm
*/
public function getForm()
{
return $this->form;
}
/**
* Set question
*
* @param IneatFormGeneratorBundleEntityQuestion $question
* @return Widget
*/
public function setQuestion(IneatFormGeneratorBundleEntityQuestion $question = null)
{
$this->question = $question;
return $this;
}
/**
* Get question
*
* @return IneatFormGeneratorBundleEntityQuestion
*/
public function getQuestion()
{
return $this->question;
}
}
<?php
namespace IneatFormGeneratorBundleEntityWidget;
use SymfonyComponentValidatorConstraints as Assert;
use DoctrineORMMapping as ORM;
/**
* Widget
*
* @ORMEntity
* @ORMTable(name="widget_text")
*/
class WidgetText extends Widget
{
/**
* @var string
*
* @ORMColumn(type="text")
*/
private $text;
/**
* Set text
*
* @param string $text
* @return WidgetText
*/
public function setText($text)
{
$this->text = $text;
return $this;
}
/**
* Get text
*
* @return string
*/
public function getText()
{
return $this->text;
}
}
<?php
namespace IneatFormGeneratorBundleEntity;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;
use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity;
use DoctrineCommonCollectionsArrayCollection;
/**
* Form
*
* @ORMTable(name="form")
* @ORMEntity(repositoryClass="IneatFormGeneratorBundleEntityFormRepository")
* @UniqueEntity("name")
* @UniqueEntity("slug")
*/
class Form
{
/**
* @var integer
*
* @ORMColumn(name="id", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORMColumn(name="name", type="string", length=255)
*/
private $name;
/**
* @var string
*
* @ORMColumn(name="slug", type="string", length=255)
*/
private $slug;
/**
* @var ArrayCollection
*
* @ORMOneToMany(targetEntity="Widget", mappedBy="form", cascade={"persist"})
*/
private $widgets;
public function __construct()
{
$this->widgets = new ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Form
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set slug
*
* @param string $slug
* @return Form
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* @return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Add widgets
*
* @param IneatFormGeneratorBundleEntityWidgetWidget $widget
* @return Form
*/
public function addWidget(IneatFormGeneratorBundleEntityWidgetWidget $widget)
{
$this->widgets[] = $widget;
return $this;
}
/**
* Remove widgets
*
* @param IneatFormGeneratorBundleEntityWidgetWidget $widget
*/
public function removeWidget(IneatFormGeneratorBundleEntityWidgetWidget $widget)
{
$this->widgets->removeElement($widget);
}
/**
* Get widgets
*
* @return DoctrineCommonCollectionsCollection
*/
public function getWidgets()
{
return $this->widgets;
}
/**
* Set widgets
*
* @param DoctrineCommonCollectionsCollection $widgets
*/
public function setWidget(DoctrineCommonCollectionsCollection $widgets)
{
$this->widgets = $widgets;
}
public function __set($name, $obj)
{
if (is_a($obj, 'IneatFormGeneratorBundleEntityWidgetWidget')) {
$this->addWidget($obj);
}
}
}
正如你所看到的,一个表单可以附加多个小部件。
我已经创建了一个抽象类Widget,因为所有的Widget都有共同的字段,并且都是Widget类型,因为在Form实体中,每个Widget类型都有一个集合似乎非常非常糟糕(糟糕和无聊)。
这个模型工作,我已经对它进行了单元测试,我能够将WidgetText附加到表单,然后检索它。
问题来了,当我试图使用它的形式。
<?php
namespace IneatFormGeneratorBundleForm;
use IneatFormGeneratorBundleEntityWidgetWidgetText;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
class FormType extends AbstractType
{
protected $widgets;
public function __construct(array $widgets = array())
{
$this->widgets = $widgets;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text')
->add('slug', 'text')
->add('WidgetText', 'collection', array(
'type' => new WidgetTextType(),
'allow_add' => true,
'attr' => array('class' => 'widget-text'),
'by_reference' => false
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'IneatFormGeneratorBundleEntityForm',
));
}
public function getName()
{
return 'ineat_formgeneratorbundle_formtype';
}
}
<?php
namespace IneatFormGeneratorBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
class WidgetTextType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('text', 'text')
;
}
public function getName()
{
return 'ineat_formgeneratorbundle_widgettexttype';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'IneatFormGeneratorBundleEntityWidgetWidgetText',
));
}
}
当我尝试显示表单时,我得到了以下错误:
属性"WidgetText"、方法"getWidgetText()"和方法"isWidgetText()"都不存在于类"IneatFormGeneratorBundleEntityForm"
好像Symfony不知道我的WidgetText也是Widget类型。
如果在控制器(由Symfony生成)我改变这一行:
$this->createForm(new FormType(), new Form())
:
$this->createForm(new FormType())
表单显示良好,但提交时我没有绑定数据。
我完全被困在那里,从面向对象的角度来看,我认为这应该工作,但我不确定Symfony是否允许我做我想做的。
正如您的问题评论中提到的,您应该将"WidgetText"字段的名称更改为"widgets"。这背后的原因是字段的名称应该与模型中的访问器匹配(即。"name"表示(set|get)Name()
,"widgets"表示(set|get)Widgets()
等)
如果您真的希望字段的名称与模型中的访问器不同,您还可以使用"property_path"选项(默认情况下设置为字段的名称):
$builder->add('WidgetText', ..., array(
...
'property_path' => 'widgets',
));