[Form] Ported DateTimeField to FormFactory

This commit is contained in:
Bernhard Schussek 2011-02-23 23:33:54 +01:00
parent b5671c1be0
commit 8e2d0bae90
3 changed files with 126 additions and 195 deletions

View File

@ -1,171 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form;
use Symfony\Component\Form\ValueTransformer\ReversedTransformer;
use Symfony\Component\Form\ValueTransformer\DateTimeToStringTransformer;
use Symfony\Component\Form\ValueTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\ValueTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\ValueTransformer\ValueTransformerChain;
/**
* A field for editing a date and a time simultaneously.
*
* Available options:
*
* * date_widget: How to render the date field ("input" or "choice"). Default: "choice".
* * time_widget: How to render the time field ("input" or "choice"). Default: "choice".
* * type: The type of the date stored on the object. Default: "datetime":
* * datetime: A DateTime object;
* * string: A raw string (e.g. 2011-05-01 12:30:00, Y-m-d H:i:s);
* * timestamp: A unix timestamp (e.g. 1304208000).
* * date_pattern: The pattern for the select boxes when date "widget" is "choice".
* You can use the placeholders "{{ year }}", "{{ month }}" and "{{ day }}".
* Default: locale dependent.
* * with_seconds Whether or not to create a field for seconds. Default: false.
*
* * years: An array of years for the year select tag.
* * months: An array of months for the month select tag.
* * days: An array of days for the day select tag.
* * hours: An array of hours for the hour select tag.
* * minutes: An array of minutes for the minute select tag.
* * seconds: An array of seconds for the second select tag.
*
* * date_format: The date format type to use for displaying the date. Default: medium.
* * data_timezone: The timezone of the data. Default: UTC.
* * user_timezone: The timezone of the user entering a new value. Default: UTC.
*
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
*/
class DateTimeField extends Form
{
const DATETIME = 'datetime';
const STRING = 'string';
const TIMESTAMP = 'timestamp';
protected static $types = array(
self::DATETIME,
self::STRING,
self::TIMESTAMP,
);
protected static $dateFormats = array(
DateField::FULL,
DateField::LONG,
DateField::MEDIUM,
DateField::SHORT,
);
protected static $dateWidgets = array(
DateField::CHOICE,
DateField::INPUT,
);
protected static $timeWidgets = array(
TimeField::CHOICE,
TimeField::INPUT,
);
/**
* {@inheritDoc}
*/
public function __construct($key, array $options = array())
{
// Override parent option
// \DateTime objects are never edited by reference, because
// we treat them like value objects
$this->addOption('by_reference', false);
parent::__construct($key, $options);
}
protected function configure()
{
$this->addOption('date_widget', DateField::CHOICE, self::$dateWidgets);
$this->addOption('time_widget', TimeField::CHOICE, self::$timeWidgets);
$this->addOption('type', self::DATETIME, self::$types);
$this->addOption('date_pattern');
$this->addOption('with_seconds', false);
$this->addOption('years', array());
$this->addOption('months', array());
$this->addOption('days', array());
$this->addOption('hours', array());
$this->addOption('minutes', array());
$this->addOption('seconds', array());
$this->addOption('data_timezone', date_default_timezone_get());
$this->addOption('user_timezone', date_default_timezone_get());
$this->addOption('date_format', DateField::MEDIUM, self::$dateFormats);
$this->add(new DateField('date', array(
'type' => DateField::RAW,
'widget' => $this->getOption('date_widget'),
'format' => $this->getOption('date_format'),
'data_timezone' => $this->getOption('user_timezone'),
'user_timezone' => $this->getOption('user_timezone'),
'years' => $this->getOption('years'),
'months' => $this->getOption('months'),
'days' => $this->getOption('days'),
'pattern' => $this->getOption('date_pattern'),
)));
$this->add(new TimeField('time', array(
'type' => TimeField::RAW,
'widget' => $this->getOption('time_widget'),
'data_timezone' => $this->getOption('user_timezone'),
'user_timezone' => $this->getOption('user_timezone'),
'with_seconds' => $this->getOption('with_seconds'),
'hours' => $this->getOption('hours'),
'minutes' => $this->getOption('minutes'),
'seconds' => $this->getOption('seconds'),
)));
if ($this->getOption('type') == self::STRING) {
$this->setNormalizationTransformer(new ReversedTransformer(
new DateTimeToStringTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
))
));
} else if ($this->getOption('type') == self::TIMESTAMP) {
$this->setNormalizationTransformer(new ReversedTransformer(
new DateTimeToTimestampTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('data_timezone'),
))
));
}
$this->setValueTransformer(new DateTimeToArrayTransformer(array(
'input_timezone' => $this->getOption('data_timezone'),
'output_timezone' => $this->getOption('user_timezone'),
)));
}
/**
* {@inheritDoc}
*/
protected function transform($value)
{
$value = parent::transform($value);
return array('date' => $value, 'time' => $value);
}
/**
* {@inheritDoc}
*/
protected function reverseTransform($value)
{
return parent::reverseTransform(array_merge($value['date'], $value['time']));
}
}

View File

@ -49,6 +49,7 @@ use Symfony\Component\Form\ValueTransformer\EntityToIdTransformer;
use Symfony\Component\Form\ValueTransformer\EntitiesToArrayTransformer;
use Symfony\Component\Form\ValueTransformer\ValueTransformerChain;
use Symfony\Component\Form\ValueTransformer\ArrayToChoicesTransformer;
use Symfony\Component\Form\ValueTransformer\ArrayToPartsTransformer;
use Symfony\Component\Validator\ValidatorInterface;
use Symfony\Component\Locale\Locale;
@ -678,7 +679,7 @@ class FormFactory
'widget',
)));
$children = array('hour', 'minute');
$parts = array('hour', 'minute');
$field = $this->getForm($key, $options)
->add($this->getHourField('hour', $childOptions))
->add($this->getMinuteField('minute', $childOptions))
@ -687,7 +688,7 @@ class FormFactory
->setModifyByReference(false);
if ($options['with_seconds']) {
$children[] = 'second';
$parts[] = 'second';
$field->add($this->getSecondField('second', $childOptions));
}
@ -711,7 +712,7 @@ class FormFactory
new DateTimeToArrayTransformer(array(
'input_timezone' => $options['data_timezone'],
'output_timezone' => $options['data_timezone'],
'fields' => $children,
'fields' => $parts,
))
));
}
@ -723,11 +724,112 @@ class FormFactory
// if the field is rendered as choice field, the values should be trimmed
// of trailing zeros to render the selected choices correctly
'pad' => $options['widget'] === 'text',
'fields' => $children,
'fields' => $parts,
)))
->setRendererVar('widget', $options['widget'])
->setRendererVar('with_seconds', $options['with_seconds']);
return $field;
}
public function getDateTimeField($key, array $options = array())
{
$options = array_merge(array(
'template' => 'datetime',
'type' => 'datetime',
'with_seconds' => false,
'data_timezone' => date_default_timezone_get(),
'user_timezone' => date_default_timezone_get(),
), $options);
// Only pass a subset of the options to children
$dateFieldOptions = array_intersect_key($options, array_flip(array(
'years',
'months',
'days',
)));
$timeFieldOptions = array_intersect_key($options, array_flip(array(
'hours',
'minutes',
'seconds',
'with_seconds',
)));
if (isset($options['date_pattern'])) {
$dateFieldOptions['pattern'] = $options['date_pattern'];
}
if (isset($options['date_widget'])) {
$dateFieldOptions['widget'] = $options['date_widget'];
}
if (isset($options['date_format'])) {
$dateFieldOptions['format'] = $options['date_format'];
}
$dateFieldOptions['type'] = 'array';
if (isset($options['time_pattern'])) {
$timeFieldOptions['pattern'] = $options['time_pattern'];
}
if (isset($options['time_widget'])) {
$timeFieldOptions['widget'] = $options['time_widget'];
}
if (isset($options['time_format'])) {
$timeFieldOptions['format'] = $options['time_format'];
}
$timeFieldOptions['type'] = 'array';
$parts = array('year', 'month', 'day', 'hour', 'minute');
$timeParts = array('hour', 'minute');
if ($options['with_seconds']) {
$parts[] = 'second';
$timeParts[] = 'second';
}
$field = $this->getForm($key, $options)
->setValueTransformer(new ValueTransformerChain(array(
new DateTimeToArrayTransformer(array(
'input_timezone' => $options['data_timezone'],
'output_timezone' => $options['user_timezone'],
)),
new ArrayToPartsTransformer(array(
'date' => array('year', 'month', 'day'),
'time' => $timeParts,
)),
)))
->add($this->getDateField('date', $dateFieldOptions))
->add($this->getTimeField('time', $timeFieldOptions))
// Don't modify \DateTime classes by reference, we treat
// them like immutable value objects
->setModifyByReference(false)
->setData(null); // hack: should be invoked automatically
if ($options['type'] == 'string') {
$field->setNormalizationTransformer(new ReversedTransformer(
new DateTimeToStringTransformer(array(
'format' => 'Y-m-d H:i:s',
'input_timezone' => $options['data_timezone'],
'output_timezone' => $options['data_timezone'],
))
));
} else if ($options['type'] == 'timestamp') {
$field->setNormalizationTransformer(new ReversedTransformer(
new DateTimeToTimestampTransformer(array(
'input_timezone' => $options['data_timezone'],
'output_timezone' => $options['data_timezone'],
))
));
} else if ($options['type'] === 'array') {
$field->setNormalizationTransformer(new ReversedTransformer(
new DateTimeToArrayTransformer(array(
'input_timezone' => $options['data_timezone'],
'output_timezone' => $options['data_timezone'],
'fields' => $parts,
))
));
}
return $field;
}
}

View File

@ -21,12 +21,12 @@ class DateTimeFieldTest extends DateTimeTestCase
{
public function testSubmit_dateTime()
{
$field = new DateTimeField('name', array(
$field = $this->factory->getDateTimeField('name', array(
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'date_widget' => DateField::CHOICE,
'time_widget' => TimeField::CHOICE,
'type' => DateTimeField::DATETIME,
'date_widget' => 'choice',
'time_widget' => 'choice',
'type' => 'datetime',
));
$field->submit(array(
@ -48,12 +48,12 @@ class DateTimeFieldTest extends DateTimeTestCase
public function testSubmit_string()
{
$field = new DateTimeField('name', array(
$field = $this->factory->getDateTimeField('name', array(
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'type' => DateTimeField::STRING,
'date_widget' => DateField::CHOICE,
'time_widget' => TimeField::CHOICE,
'type' => 'string',
'date_widget' => 'choice',
'time_widget' => 'choice',
));
$field->submit(array(
@ -73,12 +73,12 @@ class DateTimeFieldTest extends DateTimeTestCase
public function testSubmit_timestamp()
{
$field = new DateTimeField('name', array(
$field = $this->factory->getDateTimeField('name', array(
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'type' => DateTimeField::TIMESTAMP,
'date_widget' => DateField::CHOICE,
'time_widget' => TimeField::CHOICE,
'type' => 'timestamp',
'date_widget' => 'choice',
'time_widget' => 'choice',
));
$field->submit(array(
@ -100,12 +100,12 @@ class DateTimeFieldTest extends DateTimeTestCase
public function testSubmit_withSeconds()
{
$field = new DateTimeField('name', array(
$field = $this->factory->getDateTimeField('name', array(
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'date_widget' => DateField::CHOICE,
'time_widget' => TimeField::CHOICE,
'type' => DateTimeField::DATETIME,
'date_widget' => 'choice',
'time_widget' => 'choice',
'type' => 'datetime',
'with_seconds' => true,
));
@ -131,13 +131,13 @@ class DateTimeFieldTest extends DateTimeTestCase
public function testSubmit_differentTimezones()
{
$field = new DateTimeField('name', array(
$field = $this->factory->getDateTimeField('name', array(
'data_timezone' => 'America/New_York',
'user_timezone' => 'Pacific/Tahiti',
'date_widget' => DateField::CHOICE,
'time_widget' => TimeField::CHOICE,
'date_widget' => 'choice',
'time_widget' => 'choice',
// don't do this test with DateTime, because it leads to wrong results!
'type' => DateTimeField::STRING,
'type' => 'string',
'with_seconds' => true,
));