我在 Symfony 中在自定义表单字段上使用自定义数据转换器时遇到问题 - 但仅当它在我的主表单中的集合/嵌入表单中使用时才出现问题。如果我直接在主窗体中使用相同的自定义字段类型和字段转换器,那么它就可以完美工作。 基本上我有一个应用程序是我所有音乐/电影/等的数据库。每个项目都存储在名为 AVItem 的实体中。对于这些项目中的每一个,我都可以有零个或多个轨道,并且我使用 CollectionType 来执行此操作。在这个嵌入的表单/集合中,我有简单的文本字段和 EntityType 集合,并且在提交主表单时它们都可以完美工作;但是,只有一个使用我的自定义字段类型和转换器的字段不这样做。 我的自定义字段适用于“持续时间”。在数据库中,我将任何时间长度纯粹保存为秒的整数;但是,我显示它并希望将其作为 [HH:]mm:ss 形式的字符串输入。显示时,自定义字段和转换器工作,它从实体读取秒数,然后“转换”方法将其正确显示为 HH:mm:ss。但是,当我提交主表单并且其中一个轨道已输入内容时,当调用“reverseTransform”方法时,它收到的值始终为空! 然而,在我的主要实体中,我还有一个名为“totalRunningTime”的填充,它也使用相同的自定义字段类型和转换器,转换和反向转换始终完美工作。 这似乎纯粹是集合/嵌入形式中的问题。 有人有想法吗? 这是一些代码片段。
这是我的自定义数据转换器:
class TimeDurationTransformer implements DataTransformerInterface
{
private ?string $invalidMessage = null;
public function __construct( ?string $invalidMessage = null )
{
$this->invalidMessage = $invalidMessage;
}
/**
* Transform an integer (number of seconds) into a string of the form HH:mm:ss.
*
* @param mixed $value
* @return mixed
*/
public function transform( mixed $value ) : mixed
{
if ( ( null === $value ) || ( intval( $value ) == 0 ) ):
return null;
endif;
return sprintf( '%02d:%02d:%02d', ( $value/ 3600 ), ( $value / 60 % 60 ), ( $value% 60 ) );
}
/**
* Transform a string of the form HH:mm:ss into a number of seconds as an integer.
*
* @param mixed $value
* @return mixed
*/
public function reverseTransform( mixed $value ) : mixed
{
if ( ( null === $value ) || empty( $value ) || ( strlen( $value ) < 1 ) ) :
return null;
endif;
$matchResult = preg_match( pattern: '/^((\d{1,3}):)?(\d{1,2}):(\d{1,2})$/', subject: $value, matches: $matches, flags: PREG_UNMATCHED_AS_NULL );
// If the string entered does not match the pattern, throw an exception
if ( ( false === $matchResult) || ( 0 === $matchResult ) ) :
throw new TransformationFailedException( message: $this->invalidMessage, invalidMessage: $this->invalidMessage );
else:
if ( $matchResult === 1 && ( count( $matches ) == 5 ) ) :
if ( round( $matches[4] ) != null ):
return( round( $matches[4] ) + ( $matches[3] ? ( $matches[3] * 60 ) : 0 ) + ( $matches[3] ? ( $matches[2] * 3600 ) : 0 ) );
endif;
endif;
throw new TransformationFailedException( message: $this->invalidMessage, invalidMessage: $this->invalidMessage );
endif;
}
}
这是我的自定义字段类型:
class TimeDurationType extends AbstractType
{
private $translator;
public function __construct( TranslatorInterface $translator ) {
$this->translator = $translator;
}
public function buildForm( FormBuilderInterface $builder, array $options ) : void
{
$builder->addModelTransformer( new StuggiBearTimeDurationTransformer( invalidMessage: $options[ 'invalid_message' ] ) );
}
public function configureOptions( OptionsResolver $resolver ): void
{
$resolver->setDefaults( [
'data_class' => null,
'required' => false,
'constraints' => [
new PositiveOrZero(),
],
'invalid_message' => $this->translator->trans( 'message.assert.time_duration_format' ),
'attr' => [
'placeholer' => '',
],
] );
}
public function getParent(): string
{
return TextType::class;
}
}
在我的实体 AVItem 的主表单中,我有一个使用此自定义字段类型的字段 - 并且它工作正常:
public function buildForm( FormBuilderInterface $builder, array $options ): void
{
$this->editType = $options['edit_type'];
$this->isWishlistItem = $options[ 'isWishlistItem' ];
$builder
...
->add( 'totalRunningTime', TimeDurationType::class, [
'required' => false,
'label' => $this->translator->trans( 'form.field.label.total_running_time' ),
'label_html' => true,
'attr' => [
'placeholder' => 'HH:mm:ss',
],
'empty_data' => null,
] )
...
在此表格中,我收集了曲目:
$builder
...
->add( 'tracks', CollectionType::class, [
'label' => $this->translator->trans( 'form.field.label.tracks' ),
'label_html' => true,
'entry_type' => TrackEmbeddedFormType::class,
'entry_options' => [
'label' => false,
],
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'empty_data' => [],
] )
...
...然后是轨道的嵌入表单类型:
$builder
->add( 'seriesEpisodeNumber', null, [
'required' => false,
'label' => $this->translator->trans( 'form.field.label.atce_series_episode_number' ),
] )
->add( 'title', null, [
'required' => ( $this->editType == FormEditType::SEARCH ) ? false : true,
'label' => $this->translator->trans( 'form.field.label.atce_title' ),
'label_html' => true,
] )
->add( 'additionalArtistsActors', EntityType::class, [
'required' => false,
'label' => $this->translator->trans( 'form.field.label.atce_additional_artists_actors' ),
'mapped' => true,
'class' => PersonGroup::class,
'by_reference' => true,
'multiple' => true,
'expanded' => false,
'choices' => [],
] )
->add( 'comment', null, [
'required' => false,
'mapped' => true,
'label' => $this->translator->trans( 'form.field.label.atce_comment' ),
'label_html' => true,
] )
->add( 'trackChapterNumber', null, [
'required' => true,
'label' => $this->translator->trans( 'form.field.label.atce_number' ),
'label_html' => true,
] )
->add( 'duration', TimeDurationType::class, [
'required' => false,
'label' => $this->translator->trans( 'form.field.label.atce_duration' ),
'label_html' => true,
'attr' => [
'placeholder' => 'HH:mm:ss',
],
'empty_data' => null,
] );
...
因此,正如我所提到的,“totalRunningTime”字段与数据转换器完美配合,可以在整数秒和 HH:mm:ss 字符串之间进行转换。 但是,我的集合/嵌入曲目表单中的“持续时间”字段只能从数据库读取(“转换”函数有效),但是当我提交表单时,“reverseTransform”函数始终接收 null。
如果我将此字段更改为标准类型之一,例如 TextType 等,那么该字段中的值似乎会被传递。
任何帮助将不胜感激。
事实证明我的 DataTransformer 出现了错误。奇怪的是,主表单中的字段和集合/嵌入表单中的字段的错误表现不同。 基本上,我使用 round() 将数字(表示为 preg_match 中的字符串)转换为整数值,而我当然应该使用interval()。 现在一切似乎都很好 - 用户错误!