Примечание: Это 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 - это интеграционные тесты, они должны быть уверены, что трансформаторы не сбой. Даже говорит, что в документации!"
Этот тест проверяет, что ни один из ваших преобразователей данных, используемый формой не смогли. Метод 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,
));
}
/* ... */
}
Теперь возникает проблема. unit test для 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()
)
);
}
Посмотрите, где этот комментарий посередине? Да, поэтому для правильной работы все издевательства и ожидания, которые находятся в классе unit test для HiddenEntityType
, теперь теперь должны быть дублированы здесь. Я не в порядке с этим, так что мои варианты?
-
Вставьте трансформатор в качестве одной из опций
Это было бы очень просто и сделало бы насмешкой более простым, но в итоге просто ударил бы по дороге вниз. Потому что в этом случае
new EntityToPrimaryKeyTransformer()
просто переместится из одного класса типа формы в другой. Не говоря уже о том, что я чувствую, что типы форм должны скрывать свою внутреннюю сложность от остальной системы. Этот параметр означает, что эта сложность выходит за пределы формы формы. -
Внесите трансформатор factory сортировки в тип формы
Это более типичный подход к удалению "новых возможностей" из метода, но я не могу поколебать ощущение, что это делается только для того, чтобы сделать код пригодным для тестирования, и на самом деле не делает код лучше. Но если это будет сделано, это будет выглядеть примерно так.
class HiddenEntityType extends AbstractType { /** * @var DataTransformerFactory */ protected $transformerFactory; public function __construct(DataTransformerFactory $transformerFactory) { $this->transformerFactory = $transformerFactory; } public function buildForm(FormBuilderInterface $builder, array $options) { $builder->addViewTransformer( $this->transformerFactory->createTransfomerForType($this, $options); ); } /* Rest of type unchanged */ }
Это нормально, пока я не рассмотрю, как будет выглядеть factory. Для начала понадобится менеджер организации. Но что тогда? Если я буду смотреть дальше по дороге, этому предположительно-родовому factory могут понадобиться всевозможные зависимости для создания трансформаторов данных разных типов. Это явно не очень хорошее долгосрочное дизайнерское решение. Итак, что? Повторно назовите это как
EntityManagerAwareDataTransformerFactory
? Это начинает чувствовать себя грязным здесь. -
Материал, о котором я не думаю...
Мысли? Опыт? Твердые советы?