Marc
Marc

Reputation: 45

Symfony Doctrine DateTime Form TimeType

Scenario

I have start and end fields in my entity of type DateTime.

/**
 * @ORM\Column(type="datetime")
 * @var \DateTime
 */
private $start;

/**
 * @ORM\Column(type="datetime", nullable=true)
 * @var \DateTime
 */
private $end;

I want to modify only the time of these values, so I build a form with fields declaration like that:

->add('start', TimeType::class, [
    'widget' => 'single_text',
])
->add('end', TimeType::class, [
    'widget' => 'single_text',
])

Sample values

Sample value of these fields before update are (formatted: 'Y-m-d h:i:s'): 2018-07-01 17:30:00

Form input

17:50

Expected result

2018-07-01 17:50:00

Actual result

1970-01-01 17:50:00

Is there a way to render and modify only the time part of the DateTime object with the form without touching the date part?

Upvotes: 0

Views: 2321

Answers (1)

Jovan Perovic
Jovan Perovic

Reputation: 20191

In theory you would need to write your own data transformer which would take your input, validate and merge specific fields with existing data. You would also need to cover an edge case where someone submits the time, but existing data is empty at the time.

From the looks of it, there is currently no existing transformer which meets your requirements, but it would be relatively easy to write your own. The problem that I see with this is that you will need to make your transformer aware of model-data and merge incoming data with existing one. For me, this would be sign of bad design and clear violation of responsibility.

If you're still up for it, you can check out these:

How to Use Data Transformers

And one of the existing date transformers:

DateTimeToStringTransformer

However, my approach would be to listen to SUBMIT event on a particular field and intercept the submitted data.

This is an example I managed to write quickly:

$inData = [
    'start_date' => new \DateTime(),
];

// Shows local date: 2018-07-07 23:28
dump($inData);

$form = $this->createForm(PartialTimeType::class, $inData);

$form->submit([
    'start_date' => '23:50',
]);

$outData = $form->getData();

// Should show 2018-07-07 23:50
dump($outData);

PartialTimeType:

$builder
    ->add('start_date', TimeType::class, [
        'label' => 'Start time',
        'widget' => 'single_text'
    ]);

$builder->get('start_date')->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
    /** @var \DateTime $modelData */
    $modelData = $event->getForm()->getData();
    /** @var \DateTime $data */
    $data = $event->getData();

    $data->setDate(
        (int)$modelData->format('Y'),
        (int)$modelData->format('m'),
        (int)$modelData->format('d')
    );
});

I am sure that you could write your own extension of TimeType which encapsulates this behavior our of box...

Hope this helps...

Upvotes: 1

Related Questions