繁体   English   中英

Symfony2-收集形式的工厂方法

[英]Symfony2 - Factory method in collection form

我认为这个问题应该比较容易解决,但是我找不到任何答案。 我正在使用Symfony 2.3,并且有两个相互依赖的类,我们分别将它们称为ParentChild类。

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()
                    );
            }
        ));
    }

   //...
}

从理论上讲这应该可行,但是我有以下问题:

  1. Symfony强迫我声明addChildrenremoveChildren 没关系,我做到了。
  2. 完成此操作后,出现以下错误:

FatalErrorException:错误:在(route)\\ ChildType.php第22行中的非对象上调用成员函数createChild()

为了避免破坏正确的OOP编程并使表格正常工作,我在做错什么还是应该考虑一下吗? 谢谢。

哇,我实际上可以解决这个问题。 这是非常骇客的,但是在这种情况下,我设法不破坏适当的数据封装。 在这里,我将进行解释,希望这对任何人都有用。

如何以Symfony 2形式使用工厂方法模式而不尝试尝试

工厂方法模式

让我们首先回顾一下设计模式。 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;
}

如您所见, __constructParent具有紧密的耦合(我知道是反模式,但是业务规则要求我实际执行此操作,所以我不能承认这一点)。 因此,让我们给出它的要求-我们将在稍后解决。

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声明addChildrenremoveChildren 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());
}

我无法摆脱getFoogetBar ,但是请记住,在任何其他上下文中它们都可以是getBar ,当前我将对象设为只读。

尽管如此,我在这里要做的是根据addChild $child的规范构造一个新的Child ,就像克隆整个对象一样。 因此,我并不是真正地添加传入的Child ,而是将其内容复制到新的构造中。

结论

这样,我设法不破坏数据封装,方法是在ChildType创建一个Child ,并为其指定一个不相关的Parent ,然后在表单调用addChild时复制数据。 即使在看起来可能对此有一点限制的Symfony表单中,我也设法做到了不破坏良好的OO编程和设计惯例。

要获得有关如何在Symfony中使用empty_data的良好阅读,请阅读William Durand的文章。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM