注意:这是Symfony < 2.6版本,但我相信无论版本如何,总体问题都是一样的。
首先,考虑这个表单类型,它被设计为将一个或多个实体表示为隐藏字段(为简洁起见,省略了名称空间内容)
class HiddenEntityType extends AbstractType
{
/**
* @var EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['multiple']) {
$builder->addViewTransformer(
new EntitiesToPrimaryKeysTransformer(
$this->em->getRepository($options['class']),
$options['get_pk_callback'],
$options['identifier']
)
);
} else {
$builder->addViewTransformer(
new EntityToPrimaryKeyTransformer(
$this->em->getRepository($options['class']),
$options['get_pk_callback']
)
);
}
}
/**
* See class docblock for description of options
*
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'get_pk_callback' => function($entity) {
return $entity->getId();
},
'multiple' => false,
'identifier' => 'id',
'data_class' => null,
));
$resolver->setRequired(array('class'));
}
public function getName()
{
return 'hidden_entity';
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return 'hidden';
}
}
这是可行的,简单明了,而且在很大程度上看起来就像您看到的向表单类型添加数据转换器的所有示例。直到你进入单元测试阶段。看到问题了吗?变形金刚是不能被嘲笑的。“等等!”你会说,“Symfony forms的单元测试是集成测试,它们应该确保转换器不会失败。in the documentation!”
此测试检查表单使用的数据转换器是否没有失败。只有当数据转换器抛出异常时,isSynchronized()方法才会设置为false
好的,那么你就接受了你不能隔离变压器的事实。别小题大作?
现在考虑在单元测试具有此类型字段的表单时会发生什么(假设已经在服务容器中定义并标记了HiddenEntityType
)。
class SomeOtherFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('field', 'hidden_entity', array(
'class' => 'AppBundle:EntityName',
'multiple' => true,
));
}
/* ... */
}
现在进入问题。SomeOtherFormType
的单元测试现在需要实现getExtensions()
,才能使hidden_entity
类型起作用。那么这看起来怎么样呢?
protected function getExtensions()
{
$mockEntityManager = $this
->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
/* Expectations go here */
return array(
new PreloadedExtension(
array('hidden_entity' => new HiddenEntityType($mockEntityManager)),
array()
)
);
}
看到这条评论在中间的位置了吗?是的,为了让它正常工作,HiddenEntityType
的单元测试类中的所有模拟和期望现在都需要在这里复制。我对此不太满意,那么我有什么选择呢?
这将是非常直接的,并将使模仿更简单,但最终只是踢罐子在道路上。因为在这种情况下,new EntityToPrimaryKeyTransformer()
只是从一个表单类型类移动到另一个。更不用说我觉得表单类型应该对系统的其余部分隐藏其内部复杂性。此选项意味着将复杂性推到表单类型的边界之外。
这是从方法中删除"newables“的一种更典型的方法,但我不能动摇这样做的感觉,即这样做只是为了使代码可测试,而不是实际上使代码变得更好。但如果这样做了,它将看起来像这样
$this->transformerFactory->createTransfomerForType($this,HiddenEntityType扩展AbstractType { /** * @var DataTransformerFactory */受保护的$transformerFactory;公共函数__construct(DataTransformerFactory $transformerFactory) {$this->$transformerFactory=buildForm;}公共函数FormBuilderInterface(FormBuilderInterface $builder,数组$options) { $builder->addViewTransformer( __construct $options););} /*类型不变*/ }
在我考虑工厂的实际外观之前,这感觉还可以。对于初学者来说,它需要注入实体管理器。但是,然后呢?如果我再往下看,这个被认为是通用的工厂可能需要各种依赖项来创建不同类型的数据转换器。这显然不是一个好的长期设计决策。然后呢?是否将其重新标记为EntityManagerAwareDataTransformerFactory
?这里开始觉得乱七八糟了。
有什么想法?经验?可靠的建议?
https://stackoverflow.com/questions/39981800
复制相似问题