[英]Symfony2 - Factory method in collection form
我认为这个问题应该比较容易解决,但是我找不到任何答案。 我正在使用Symfony 2.3,并且有两个相互依赖的类,我们分别将它们称为Parent
和Child
类。
Child
的生命周期取决于存在的Parent
,因此在构造函数中我声明:
public function __construct(Parent $parent, $foo, $bar)
{
$this->parent = $parent;
$this->foo = $foo;
$this->bar = $baz;
}
通过这种方式,我使用了工厂方法模式,以便在给定两个类的依赖的情况下创建Child
。 在Parent
我有这种方法:
public function createChild($foo, $bar)
{
$c = new Child($this, $foo, $bar);
$this->addChildren($c); //The children are stored in a Doctrine's ArrayCollection
return $c;
}
现在,我正在制作表单以创建Parent
和几个Child
。 ParentType
具有:
class ParentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('baz', 'text') //Merely a property of Parent
->add('children', 'collection', array(
'type' => 'childrentype',
'allow_add' => true,
'label' => false
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'My\Bundle\Parent',
'empty_data' => function (FormInterface $form) {
return new \My\Bundle\Parent(
$form->get('baz')->getData()
);
}
));
}
//...
}
ChildType
具有:
class ChildType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo', 'text', array('label' => false)
)->add('bar', 'text', array('label' => false));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'My\Bundle\Child',
'empty_data' => function (FormInterface $form) {
return $form->getParent()->getData()->createChild(
$form->get('foo')->getData(),
$form->get('bar')->getData()
);
}
));
}
//...
}
从理论上讲这应该可行,但是我有以下问题:
addChildren
和removeChildren
。 没关系,我做到了。 FatalErrorException:错误:在(route)\\ ChildType.php第22行中的非对象上调用成员函数createChild()
为了避免破坏正确的OOP编程并使表格正常工作,我在做错什么还是应该考虑一下吗? 谢谢。
哇,我实际上可以解决这个问题。 这是非常骇客的,但是在这种情况下,我设法不破坏适当的数据封装。 在这里,我将进行解释,希望这对任何人都有用。
让我们首先回顾一下设计模式。 SourceMaking对此有很好的文章 ,但我会写一个摘要。 简而言之,Factory Method设计模式允许我们在父类中创建一个方法来创建另一个类的对象,并且有可能被扩展和覆盖,因此子类可以具有自己的实现。 这是一种创建模式 ,因此可以处理创建对象。 因此,工厂方法使我们能够出于任何意图封装对象的创建。
在我的问题中,我对Factory方法的使用来自于Child
的生命周期紧密依赖于Parent
的事实。 这Parent
,没有Parent
,没有Child
,并且Child
不能独立于父母生活。 因此, Parent
有一个工厂方法createChild
,它将创建一个直接绑定到它的Child
。 此外, Child
没有setParent
,以便不破坏适当的数据封装。
在该示例中,无论何时或如何调用getParent
,当我尝试getData
时,我都会一直为null
。 那是因为直到SUBMIT
才有数据,并且那时我不能添加更多表单字段。 这也是为什么我在任何时候都无法取得任何结果的原因。
让我们看一下Child
的构造方法。
public function __construct(Parent $parent, $foo, $bar)
{
$this->parent = $parent;
$this->foo = $foo;
$this->bar = $baz;
}
如您所见, __construct
与Parent
具有紧密的耦合(我知道是反模式,但是业务规则要求我实际执行此操作,所以我不能承认这一点)。 因此,让我们给出它的要求-我们将在稍后解决。
在ChildType
:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'My\Bundle\Child',
'empty_data' => function (FormInterface $form) {
return new My\Bundle\Child(
new My\Bundle\Parent(null),
$form->get('foo')->getData(),
$form->get('bar')->getData()
);
}
));
}
不,我不是在开玩笑。 我基本上是为要构造的Child
设置一个完全不相关的Parent
。 不用担心,我们会尽快处理。
当我在集合中工作时,Symfony格式迫使我在Parent
声明addChildren
和removeChildren
。 removeChildren
易于实现,但是存在一个问题:我无法真正添加addChildren
因为我无法在Child
设置Parent
,将其与创建它的Parent
耦合! 因此,我做了一个小变通办法,它可以解决所有问题!
在Parent
:
public function createChild($foo, $bar)
{
$child = new Child($this, $foo, $bar);
$this->getChildren()->add($child);
return $child;
}
public function addChild(Child $child)
{
return $this->createChild($child->getFoo(), $child->getBar());
}
我无法摆脱getFoo
和getBar
,但是请记住,在任何其他上下文中它们都可以是getBar
,当前我将对象设为只读。
尽管如此,我在这里要做的是根据addChild
$child
的规范构造一个新的Child
,就像克隆整个对象一样。 因此,我并不是真正地添加传入的Child
,而是将其内容复制到新的构造中。
这样,我设法不破坏数据封装,方法是在ChildType
创建一个Child
,并为其指定一个不相关的Parent
,然后在表单调用addChild
时复制数据。 即使在看起来可能对此有一点限制的Symfony表单中,我也设法做到了不破坏良好的OO编程和设计惯例。
要获得有关如何在Symfony中使用empty_data
的良好阅读,请阅读William Durand的文章。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.