Symfony 自定义字段类型与数据转换器在嵌入式集合表单中无法正常工作

问题描述 投票:0回答:1

我在 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 等,那么该字段中的值似乎会被传递。

任何帮助将不胜感激。

symfony symfony-forms
1个回答
0
投票

事实证明我的 DataTransformer 出现了错误。奇怪的是,主表单中的字段和集合/嵌入表单中的字段的错误表现不同。 基本上,我使用 round() 将数字(表示为 preg_match 中的字符串)转换为整数值,而我当然应该使用interval()。 现在一切似乎都很好 - 用户错误!

© www.soinside.com 2019 - 2024. All rights reserved.