我创建了一个自定义表单字段类型“duration”,以及2个字段“hour”和“minutes”

class DurationType extends AbstractType
{

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults([]);
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('hours', new DurationSmallType(), [])
            ->add('minutes', new DurationSmallType(), [])
        ;
    }

    public function finishView(FormView $view, FormInterface $form, array $options)
    {
    }

    public function getName()
    {
        return 'duration';
    }
}

DurationSmallType:
class DurationSmallType extends AbstractType
{

    public function getName()
    {
        return 'duration_small';
    }
}

两种类型的模板:
{% block duration_small_widget -%}
<div class="input-group" style="display: inline-block;width:100px;height: 34px;margin-right: 20px;">
    <input type="text" {{ block('widget_attributes') }} class="form-control" style="width:50px;" {% if value is not empty %}value="{{ value }}" {% endif %}>
    <span class="input-group-addon" style="height: 34px;"></span>
</div>
{%- endblock duration_small_widget %}

{% block duration_widget -%}

    {{ form_widget(form.hours) }}
    {{ form_widget(form.minutes) }}

{%- endblock duration_widget %}

在以分钟为单位保存的实体持续时间(以整数形式)中,我在表单构建器中创建了一个DataTransformer:
class EventType extends AbstractType
    {
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $dataTransformer = new DurationToMinutesTransformer();

        $builder
            ->add('name', NULL, array('label' => 'Название'))
            ->add('type', NULL, array('label' => 'Раздел'))
            ->add($builder
                ->create('duration', new DurationType(), array('label' => 'Продолжительность'))
                ->addModelTransformer($dataTransformer)
            )
        ->add('enabled', NULL, array('label' => 'Включено'));
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'TourConstructor\MainBundle\Entity\Event',
            'csrf_protection' => true,
            'csrf_field_name' => '_token',
            'intention' => 'events_token'
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'mybundle_event';
    }
}

DurationToMinutesTransformer:
class DurationToMinutesTransformer implements DataTransformerInterface
{
    public function transform($value)
    {

        if (!$value) {
            return null;
        }

        $hours = ceil($value / 60);
        $minutes = $value % 60;

        return [
            "hours" => $hours,
            "minutes" => $minutes
        ];
    }

    public function reverseTransform($value)
    {
        if (!$value) {
            return null;
        }

        return $value["hours"]*60 + $value["minutes"];
    }
}

转换-工作,我在编辑字段中有几小时和几分钟,但是reverseTransform不起作用,提交后我将工期字段作为数组。

Symfony错误:
Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[duration].children[hours] = 3

Caused by:

Symfony\Component\Form\Exception\TransformationFailedException
Compound forms expect an array or NULL on submission.

Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[duration].children[minutes] = 0

Caused by:

Symfony\Component\Form\Exception\TransformationFailedException
Compound forms expect an array or NULL on submission.

请帮我。

最佳答案

我发现错误,DurationSmallType需要选项compound = false,默认为true,symfony尝试将我的2字段用作内部表单。
然后从实体表单中删除modelTransformer并将其放在DurationType中。

我的表单构建者的最终代码:

事件类型:

class EventType extends AbstractType
    {
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $dataTransformer = new DurationToMinutesTransformer();

        $builder
            ->add('name', NULL, array('label' => 'Название'))
            ->add('type', NULL, array('label' => 'Раздел'))
            ->add($builder
                ->create('duration', new DurationType(), array('label' => 'Продолжительность'))
                ->addModelTransformer($dataTransformer)
            )
        ->add('enabled', NULL, array('label' => 'Включено'));
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'TourConstructor\MainBundle\Entity\Event',
            'csrf_protection' => true,
            'csrf_field_name' => '_token',
            'intention' => 'events_token'
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'mybundle_event';
    }
}

持续时间类型:
class DurationType extends AbstractType
{

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {

        $resolver->setDefaults(array(
            'html5' => true,
            'error_bubbling' => false,
            'compound' => true,
        ));
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('hours', new DurationSmallType(), [
                "label"=>"ч."
            ])
            ->add('minutes', new DurationSmallType(), [
                "label"=>"мин."
            ])
            ->addViewTransformer(new DurationToMinutesTransformer())
        ;
    }

    public function finishView(FormView $view, FormInterface $form, array $options)
    {

    }

    public function getName()
    {
        return 'duration';
    }
}

DurationSmallType:
class DurationSmallType extends AbstractType
{

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'compound' => false,
        ));
    }

    public function getName()
    {
        return 'duration_small';
    }
}

09-20 03:12