将两个表单字段合并为一个实体值

时间:2013-07-01 20:20:46

标签: symfony

如何以一种形式(例如日期和时间)将两个单独的字段(必须分开)连接到一个实体的有效日期时间,以便在表单发布后保留?

有什么更好的方法?数据转换器?表格活动?表格模型?在持久化之前手动设置所有实体属性?

实体:

<?php namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="event")
 */
class EventEntity
{

    /**
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    // ...

    /**
     * @ORM\Column(name="date_time", type="datetime", nullable=false)
     */
    protected $datetime;

    public function getId()
    {
        return $this->id;
    }

    // ...

    public function getDateTime()
    {
        return $this->datetime;
    }

    public function setDateTime(\DateTime $datetime)
    {
        $this->datetime = $datetime;
    }

}

FormType:

<?php namespace Acme\DemoBundle\Form\Type;

use JMS\DiExtraBundle\Annotation as DI;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class EventType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            // ...
            ->add('date', 'date', [
                    'required' => true,
                    'widget'   => 'single_text',
                    'format'   => 'dd.MM.yyyy'
                ]
            )
            ->add('time', 'time', [
                    'required' => false,
                    'widget'   => 'single_text'
                ]
            );
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\DemoBundle\Entity\EventEntity' //Acme\DemoBundle\Form\Model\EventModel ?
        ));
    }

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

}

3 个答案:

答案 0 :(得分:7)

如果您在日期时间类型中单独设置日期和时间窗口小部件,则它们会单独呈现,但会验证为一个字段。

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('datetime', 'datetime', array(
            'date_widget' => 'single_text',
            'time_widget' => 'single_text',
            'date_format' => 'dd.MM.yyyy',
        ));
}

答案 1 :(得分:5)

我建议使用Pazis解决方案,因为这是最简单的解决方案。但对于DataTransformer来说,这也是一个完美的工作:

class MyDataTransformer implements DataTransformerInterface
{
  public function transform($value)
  {
    if (null === $value)
      return;

    if ($value instanceof \DateTime)
      return array(
        'date' => $value->format('d.m.Y'),
        'time' => $value->format('H:i:s')
      );

    return null;
  }

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

    if (is_array($value) && array_key_exists('date', $value) && array_key_exists('time', $value))
      return new \DateTime($value['date'] . ' ' . $value['time']);

    return null; 
  }
}

这有一个缺点,你需要使用这个变换器映射你实体中的每一个值,当然 - 你不想这样做。但是通过小巧的形式,可以避免这种情况。因此,您需要在表单中添加子表单,其中包括日期和时间字段以及添加的Transformer。您需要将DateTime对象映射(“property_path”-option)到此子表单,或者只是“正确”命名,因此表单框架可以按名称映射它。

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add(
        $builder->create('datetime', 'form')
                ->add('date', 'date', $optionsForDate)
                ->add('time', 'time', $optionsForTime)
                ->addViewTransformer(new MyDataTransformer())
    );
}

代码可能没有完美运行,但我希望将一个实体属性拆分为两个(或更多)表单字段背后的想法是明确的。

答案 2 :(得分:1)

héhé,这是一个很好的问题。

我会选择最简单,最通用,可重复使用的解决方案。 我不会为了表单映射而在我的模型上实现方法,但如果它有意义,为什么不简单地使用模型api?

<?php

class EventEntity
{
// assume $this->datetime is initialized and instance of DateTime

public function setDate(\DateTime $date)
{
    // i don't know if this works!
    $this->datetime->add($this->datetime->diff($date));
}


public function setTime(\DateTime $date)
{
    $this->datetime->add($this->datetime->diff($date));
}
}