Add new Form WeekType
This commit is contained in:
parent
3936b78830
commit
c4a2f026e0
@ -255,6 +255,17 @@
|
|||||||
{{ block('form_widget_simple') }}
|
{{ block('form_widget_simple') }}
|
||||||
{%- endblock color_widget -%}
|
{%- endblock color_widget -%}
|
||||||
|
|
||||||
|
{%- block week_widget -%}
|
||||||
|
{%- if widget == 'single_text' -%}
|
||||||
|
{{ block('form_widget_simple') }}
|
||||||
|
{%- else -%}
|
||||||
|
{%- set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} -%}
|
||||||
|
<div {{ block('widget_container_attributes') }}>
|
||||||
|
{{ form_widget(form.year, vars) }}-{{ form_widget(form.week, vars) }}
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endblock week_widget -%}
|
||||||
|
|
||||||
{# Labels #}
|
{# Labels #}
|
||||||
|
|
||||||
{%- block form_label -%}
|
{%- block form_label -%}
|
||||||
|
@ -2722,6 +2722,104 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
|
|||||||
[@name="name"]
|
[@name="name"]
|
||||||
[@class="my&class form-control"]
|
[@class="my&class form-control"]
|
||||||
[@value="#0000ff"]
|
[@value="#0000ff"]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekSingleText()
|
||||||
|
{
|
||||||
|
$this->requiresFeatureSet(404);
|
||||||
|
|
||||||
|
$form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/input
|
||||||
|
[@type="week"]
|
||||||
|
[@name="holidays"]
|
||||||
|
[@class="my&class form-control"]
|
||||||
|
[@value="1970-W01"]
|
||||||
|
[not(@maxlength)]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekSingleTextNoHtml5()
|
||||||
|
{
|
||||||
|
$this->requiresFeatureSet(404);
|
||||||
|
|
||||||
|
$form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
'html5' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/input
|
||||||
|
[@type="text"]
|
||||||
|
[@name="holidays"]
|
||||||
|
[@class="my&class form-control"]
|
||||||
|
[@value="1970-W01"]
|
||||||
|
[not(@maxlength)]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekChoices()
|
||||||
|
{
|
||||||
|
$this->requiresFeatureSet(404);
|
||||||
|
|
||||||
|
$data = ['year' => date('Y'), 'week' => 1];
|
||||||
|
|
||||||
|
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', $data, [
|
||||||
|
'input' => 'array',
|
||||||
|
'required' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/div
|
||||||
|
[@class="my&class"]
|
||||||
|
[
|
||||||
|
./select
|
||||||
|
[@id="name_year"]
|
||||||
|
[@class="form-control"]
|
||||||
|
[./option[@value="'.$data['year'].'"][@selected="selected"]]
|
||||||
|
/following-sibling::select
|
||||||
|
[@id="name_week"]
|
||||||
|
[@class="form-control"]
|
||||||
|
[./option[@value="'.$data['week'].'"][@selected="selected"]]
|
||||||
|
]
|
||||||
|
[count(.//select)=2]'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekText()
|
||||||
|
{
|
||||||
|
$this->requiresFeatureSet(404);
|
||||||
|
|
||||||
|
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '2000-W01', [
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'text',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/div
|
||||||
|
[@class="my&class"]
|
||||||
|
[
|
||||||
|
./input
|
||||||
|
[@id="name_year"]
|
||||||
|
[@type="number"]
|
||||||
|
[@class="form-control"]
|
||||||
|
[@value="2000"]
|
||||||
|
/following-sibling::input
|
||||||
|
[@id="name_week"]
|
||||||
|
[@type="number"]
|
||||||
|
[@class="form-control"]
|
||||||
|
[@value="1"]
|
||||||
|
]
|
||||||
|
[count(./input)=2]
|
||||||
'
|
'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php if ($widget == 'single_text'): ?>
|
||||||
|
<?php echo $view['form']->block($form, 'form_widget_simple'); ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php $vars = $widget == 'text' ? ['attr' => ['size' => 1]] : [] ?>
|
||||||
|
<div <?php echo $view['form']->block($form, 'widget_container_attributes') ?>>
|
||||||
|
<?php
|
||||||
|
// There should be no spaces between the colons and the widgets, that's why
|
||||||
|
// this block is written in a single PHP tag
|
||||||
|
echo $view['form']->widget($form['year'], $vars);
|
||||||
|
echo '-';
|
||||||
|
echo $view['form']->widget($form['week'], $vars);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
@ -4,6 +4,7 @@ CHANGELOG
|
|||||||
4.4.0
|
4.4.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* add new `WeekType`
|
||||||
* using different values for the "model_timezone" and "view_timezone" options of the `TimeType` without configuring a
|
* using different values for the "model_timezone" and "view_timezone" options of the `TimeType` without configuring a
|
||||||
reference date is deprecated
|
reference date is deprecated
|
||||||
* preferred choices are repeated in the list of all choices
|
* preferred choices are repeated in the list of all choices
|
||||||
|
@ -83,6 +83,7 @@ class CoreExtension extends AbstractExtension
|
|||||||
new Type\CurrencyType(),
|
new Type\CurrencyType(),
|
||||||
new Type\TelType(),
|
new Type\TelType(),
|
||||||
new Type\ColorType(),
|
new Type\ColorType(),
|
||||||
|
new Type\WeekType(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
|
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms between an ISO 8601 week date string and an array.
|
||||||
|
*
|
||||||
|
* @author Damien Fayet <damienf1521@gmail.com>
|
||||||
|
*/
|
||||||
|
class WeekToArrayTransformer implements DataTransformerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transforms a string containing an ISO 8601 week date into an array.
|
||||||
|
*
|
||||||
|
* @param string|null $value A week date string
|
||||||
|
*
|
||||||
|
* @return array A value containing year and week
|
||||||
|
*
|
||||||
|
* @throws TransformationFailedException If the given value is not a string,
|
||||||
|
* or if the given value does not follow the right format
|
||||||
|
*/
|
||||||
|
public function transform($value)
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return ['year' => null, 'week' => null];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_string($value)) {
|
||||||
|
throw new TransformationFailedException(sprintf('Value is expected to be a string but was "%s".', \is_object($value) ? \get_class($value) : \gettype($value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === preg_match('/^(?P<year>\d{4})-W(?P<week>\d{2})$/', $value, $matches)) {
|
||||||
|
throw new TransformationFailedException('Given data does not follow the date format "Y-\WW".');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'year' => (int) $matches['year'],
|
||||||
|
'week' => (int) $matches['week'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an array into a week date string.
|
||||||
|
*
|
||||||
|
* @param array $value An array containing a year and a week number
|
||||||
|
*
|
||||||
|
* @return string|null A week date string following the format Y-\WW
|
||||||
|
*
|
||||||
|
* @throws TransformationFailedException If the given value can not be merged in a valid week date string,
|
||||||
|
* or if the obtained week date does not exists
|
||||||
|
*/
|
||||||
|
public function reverseTransform($value)
|
||||||
|
{
|
||||||
|
if (null === $value || [] === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_array($value)) {
|
||||||
|
throw new TransformationFailedException(sprintf('Value is expected to be an array, but was "%s".', \is_object($value) ? \get_class($value) : \gettype($value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\array_key_exists('year', $value)) {
|
||||||
|
throw new TransformationFailedException('Key "year" is missing.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\array_key_exists('week', $value)) {
|
||||||
|
throw new TransformationFailedException('Key "week" is missing.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($additionalKeys = array_diff(array_keys($value), ['year', 'week'])) {
|
||||||
|
throw new TransformationFailedException(sprintf('Expected only keys "year" and "week" to be present, but also got ["%s"].', implode('", "', $additionalKeys)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value['year'] && null === $value['week']) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_int($value['year'])) {
|
||||||
|
throw new TransformationFailedException(sprintf('Year is expected to be an integer, but was "%s".', \is_object($value['year']) ? \get_class($value['year']) : \gettype($value['year'])));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_int($value['week'])) {
|
||||||
|
throw new TransformationFailedException(sprintf('Week is expected to be an integer, but was "%s".', \is_object($value['week']) ? \get_class($value['week']) : \gettype($value['week'])));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The 28th December is always in the last week of the year
|
||||||
|
if (date('W', strtotime('28th December '.$value['year'])) < $value['week']) {
|
||||||
|
throw new TransformationFailedException(sprintf('Week "%d" does not exist for year "%d".', $value['week'], $value['year']));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('%d-W%02d', $value['year'], $value['week']);
|
||||||
|
}
|
||||||
|
}
|
192
src/Symfony/Component/Form/Extension/Core/Type/WeekType.php
Normal file
192
src/Symfony/Component/Form/Extension/Core/Type/WeekType.php
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Exception\LogicException;
|
||||||
|
use Symfony\Component\Form\Extension\Core\DataTransformer\WeekToArrayTransformer;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormView;
|
||||||
|
use Symfony\Component\Form\ReversedTransformer;
|
||||||
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class WeekType extends AbstractType
|
||||||
|
{
|
||||||
|
private static $widgets = [
|
||||||
|
'text' => IntegerType::class,
|
||||||
|
'choice' => ChoiceType::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
if ('string' === $options['input']) {
|
||||||
|
$builder->addModelTransformer(new WeekToArrayTransformer());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('single_text' === $options['widget']) {
|
||||||
|
$builder->addViewTransformer(new ReversedTransformer(new WeekToArrayTransformer()));
|
||||||
|
} else {
|
||||||
|
$yearOptions = $weekOptions = [
|
||||||
|
'error_bubbling' => true,
|
||||||
|
'empty_data' => '',
|
||||||
|
];
|
||||||
|
// when the form is compound the entries of the array are ignored in favor of children data
|
||||||
|
// so we need to handle the cascade setting here
|
||||||
|
$emptyData = $builder->getEmptyData() ?: [];
|
||||||
|
|
||||||
|
$yearOptions['empty_data'] = $emptyData['year'] ?? '';
|
||||||
|
$weekOptions['empty_data'] = $emptyData['week'] ?? '';
|
||||||
|
|
||||||
|
if (isset($options['invalid_message'])) {
|
||||||
|
$yearOptions['invalid_message'] = $options['invalid_message'];
|
||||||
|
$weekOptions['invalid_message'] = $options['invalid_message'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['invalid_message_parameters'])) {
|
||||||
|
$yearOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||||
|
$weekOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('choice' === $options['widget']) {
|
||||||
|
// Only pass a subset of the options to children
|
||||||
|
$yearOptions['choices'] = array_combine($options['years'], $options['years']);
|
||||||
|
$yearOptions['placeholder'] = $options['placeholder']['year'];
|
||||||
|
$yearOptions['choice_translation_domain'] = $options['choice_translation_domain']['year'];
|
||||||
|
|
||||||
|
$weekOptions['choices'] = array_combine($options['weeks'], $options['weeks']);
|
||||||
|
$weekOptions['placeholder'] = $options['placeholder']['week'];
|
||||||
|
$weekOptions['choice_translation_domain'] = $options['choice_translation_domain']['week'];
|
||||||
|
|
||||||
|
// Append generic carry-along options
|
||||||
|
foreach (['required', 'translation_domain'] as $passOpt) {
|
||||||
|
$yearOptions[$passOpt] = $options[$passOpt];
|
||||||
|
$weekOptions[$passOpt] = $options[$passOpt];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$builder->add('year', self::$widgets[$options['widget']], $yearOptions);
|
||||||
|
$builder->add('week', self::$widgets[$options['widget']], $weekOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||||
|
{
|
||||||
|
$view->vars['widget'] = $options['widget'];
|
||||||
|
|
||||||
|
if ($options['html5']) {
|
||||||
|
$view->vars['type'] = 'week';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$compound = function (Options $options) {
|
||||||
|
return 'single_text' !== $options['widget'];
|
||||||
|
};
|
||||||
|
|
||||||
|
$placeholderDefault = function (Options $options) {
|
||||||
|
return $options['required'] ? null : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
$placeholderNormalizer = function (Options $options, $placeholder) use ($placeholderDefault) {
|
||||||
|
if (\is_array($placeholder)) {
|
||||||
|
$default = $placeholderDefault($options);
|
||||||
|
|
||||||
|
return array_merge(
|
||||||
|
['year' => $default, 'week' => $default],
|
||||||
|
$placeholder
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'year' => $placeholder,
|
||||||
|
'week' => $placeholder,
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
$choiceTranslationDomainNormalizer = function (Options $options, $choiceTranslationDomain) {
|
||||||
|
if (\is_array($choiceTranslationDomain)) {
|
||||||
|
$default = false;
|
||||||
|
|
||||||
|
return array_replace(
|
||||||
|
['year' => $default, 'week' => $default],
|
||||||
|
$choiceTranslationDomain
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'year' => $choiceTranslationDomain,
|
||||||
|
'week' => $choiceTranslationDomain,
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'years' => range(date('Y') - 10, date('Y') + 10),
|
||||||
|
'weeks' => array_combine(range(1, 53), range(1, 53)),
|
||||||
|
'widget' => 'single_text',
|
||||||
|
'input' => 'array',
|
||||||
|
'placeholder' => $placeholderDefault,
|
||||||
|
'html5' => static function (Options $options) {
|
||||||
|
return 'single_text' === $options['widget'];
|
||||||
|
},
|
||||||
|
'error_bubbling' => false,
|
||||||
|
'empty_data' => function (Options $options) {
|
||||||
|
return $options['compound'] ? [] : '';
|
||||||
|
},
|
||||||
|
'compound' => $compound,
|
||||||
|
'choice_translation_domain' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
|
||||||
|
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
|
||||||
|
$resolver->setNormalizer('html5', function (Options $options, $html5) {
|
||||||
|
if ($html5 && 'single_text' !== $options['widget']) {
|
||||||
|
throw new LogicException(sprintf('The "widget" option of %s must be set to "single_text" when the "html5" option is enabled.', self::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html5;
|
||||||
|
});
|
||||||
|
|
||||||
|
$resolver->setAllowedValues('input', [
|
||||||
|
'string',
|
||||||
|
'array',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedValues('widget', [
|
||||||
|
'single_text',
|
||||||
|
'text',
|
||||||
|
'choice',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('years', 'int[]');
|
||||||
|
$resolver->setAllowedTypes('weeks', 'int[]');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getBlockPrefix()
|
||||||
|
{
|
||||||
|
return 'week';
|
||||||
|
}
|
||||||
|
}
|
@ -2765,4 +2765,87 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
|
|||||||
[true],
|
[true],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWeekSingleText()
|
||||||
|
{
|
||||||
|
$form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/input
|
||||||
|
[@type="week"]
|
||||||
|
[@name="holidays"]
|
||||||
|
[@class="my&class"]
|
||||||
|
[@value="1970-W01"]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekSingleTextNoHtml5()
|
||||||
|
{
|
||||||
|
$form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
'html5' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/input
|
||||||
|
[@type="text"]
|
||||||
|
[@name="holidays"]
|
||||||
|
[@class="my&class"]
|
||||||
|
[@value="1970-W01"]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekChoices()
|
||||||
|
{
|
||||||
|
$data = ['year' => date('Y'), 'week' => 1];
|
||||||
|
|
||||||
|
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', $data, [
|
||||||
|
'input' => 'array',
|
||||||
|
'required' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/div
|
||||||
|
[@class="my&class"]
|
||||||
|
[
|
||||||
|
./select
|
||||||
|
[@id="name_year"]
|
||||||
|
[./option[@value="'.$data['year'].'"][@selected="selected"]]
|
||||||
|
/following-sibling::select
|
||||||
|
[@id="name_week"]
|
||||||
|
[./option[@value="'.$data['week'].'"][@selected="selected"]]
|
||||||
|
]
|
||||||
|
[count(.//select)=2]'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekText()
|
||||||
|
{
|
||||||
|
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '2000-W01', [
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'text',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
|
||||||
|
'/div
|
||||||
|
[@class="my&class"]
|
||||||
|
[
|
||||||
|
./input
|
||||||
|
[@id="name_year"]
|
||||||
|
[@type="number"]
|
||||||
|
[@value="2000"]
|
||||||
|
/following-sibling::input
|
||||||
|
[@id="name_week"]
|
||||||
|
[@type="number"]
|
||||||
|
[@value="1"]
|
||||||
|
]
|
||||||
|
[count(./input)=2]'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||||
|
use Symfony\Component\Form\Extension\Core\DataTransformer\WeekToArrayTransformer;
|
||||||
|
|
||||||
|
class WeekToArrayTransformerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testTransform()
|
||||||
|
{
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
|
||||||
|
$this->assertSame(['year' => 2019, 'week' => 1], $transformer->transform('2019-W01'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTransformEmpty()
|
||||||
|
{
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
|
||||||
|
$this->assertSame(['year' => null, 'week' => null], $transformer->transform(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider transformationFailuresProvider
|
||||||
|
*/
|
||||||
|
public function testTransformationFailures($input, string $message)
|
||||||
|
{
|
||||||
|
$this->expectException(TransformationFailedException::class);
|
||||||
|
$this->expectExceptionMessage($message);
|
||||||
|
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
$transformer->transform($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transformationFailuresProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'malformed string' => ['lorem', 'Given data does not follow the date format "Y-\WW".'],
|
||||||
|
'non-string' => [[], 'Value is expected to be a string but was "array".'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReverseTransform()
|
||||||
|
{
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
|
||||||
|
$input = [
|
||||||
|
'year' => 2019,
|
||||||
|
'week' => 1,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals('2019-W01', $transformer->reverseTransform($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReverseTransformCompletelyEmpty()
|
||||||
|
{
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
|
||||||
|
$input = [
|
||||||
|
'year' => null,
|
||||||
|
'week' => null,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertNull($transformer->reverseTransform($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReverseTransformNull()
|
||||||
|
{
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
|
||||||
|
$this->assertNull($transformer->reverseTransform(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReverseTransformEmpty()
|
||||||
|
{
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
|
||||||
|
$this->assertNull($transformer->reverseTransform([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider reverseTransformationFailuresProvider
|
||||||
|
*/
|
||||||
|
public function testReverseTransformFailures($input, string $message)
|
||||||
|
{
|
||||||
|
$this->expectException(TransformationFailedException::class);
|
||||||
|
$this->expectExceptionMessage($message);
|
||||||
|
|
||||||
|
$transformer = new WeekToArrayTransformer();
|
||||||
|
$transformer->reverseTransform($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseTransformationFailuresProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'missing year' => [['week' => 1], 'Key "year" is missing.'],
|
||||||
|
'missing week' => [['year' => 2019], 'Key "week" is missing.'],
|
||||||
|
'integer instead of array' => [0, 'Value is expected to be an array, but was "integer"'],
|
||||||
|
'string instead of array' => ['12345', 'Value is expected to be an array, but was "string"'],
|
||||||
|
'week invalid' => [['year' => 2019, 'week' => 66], 'Week "66" does not exist for year "2019".'],
|
||||||
|
'year null' => [['year' => null, 'week' => 1], 'Year is expected to be an integer, but was "NULL".'],
|
||||||
|
'week null' => [['year' => 2019, 'week' => null], 'Week is expected to be an integer, but was "NULL".'],
|
||||||
|
'year non-integer' => [['year' => '2019', 'week' => 1], 'Year is expected to be an integer, but was "string".'],
|
||||||
|
'week non-integer' => [['year' => 2019, 'week' => '1'], 'Week is expected to be an integer, but was "string".'],
|
||||||
|
'unexpected key' => [['year' => 2019, 'bar' => 'baz', 'week' => 1, 'foo' => 'foobar'], 'Expected only keys "year" and "week" to be present, but also got ["bar", "foo"].'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,323 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\FormError;
|
||||||
|
|
||||||
|
class WeekTypeTest extends BaseTypeTest
|
||||||
|
{
|
||||||
|
const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\WeekType';
|
||||||
|
|
||||||
|
public function testSubmitArray()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'choice',
|
||||||
|
'input' => 'array',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form->submit([
|
||||||
|
'year' => '2019',
|
||||||
|
'week' => '1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame(['year' => 2019, 'week' => 1], $form->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubmitString()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'years' => [2019],
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form->submit([
|
||||||
|
'year' => '2019',
|
||||||
|
'week' => '1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals('2019-W01', $form->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubmitStringSingleText()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'years' => [2019],
|
||||||
|
'input' => 'string',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form->submit('2019-W01');
|
||||||
|
|
||||||
|
$this->assertEquals('2019-W01', $form->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassDefaultPlaceholderToViewIfNotRequired()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'required' => false,
|
||||||
|
'widget' => 'choice',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertSame('', $view['year']->vars['placeholder']);
|
||||||
|
$this->assertSame('', $view['week']->vars['placeholder']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassNoPlaceholderToViewIfRequired()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'required' => true,
|
||||||
|
'widget' => 'choice',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertNull($view['year']->vars['placeholder']);
|
||||||
|
$this->assertNull($view['week']->vars['placeholder']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassPlaceholderAsString()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'placeholder' => 'Empty',
|
||||||
|
'widget' => 'choice',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertSame('Empty', $view['year']->vars['placeholder']);
|
||||||
|
$this->assertSame('Empty', $view['week']->vars['placeholder']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassPlaceholderAsArray()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'placeholder' => [
|
||||||
|
'year' => 'Empty year',
|
||||||
|
'week' => 'Empty week',
|
||||||
|
],
|
||||||
|
'widget' => 'choice',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertSame('Empty year', $view['year']->vars['placeholder']);
|
||||||
|
$this->assertSame('Empty week', $view['week']->vars['placeholder']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassPlaceholderAsPartialArrayAddEmptyIfNotRequired()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'required' => false,
|
||||||
|
'placeholder' => [
|
||||||
|
'year' => 'Empty year',
|
||||||
|
],
|
||||||
|
'widget' => 'choice',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertSame('Empty year', $view['year']->vars['placeholder']);
|
||||||
|
$this->assertSame('', $view['week']->vars['placeholder']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassPlaceholderAsPartialArrayAddNullIfRequired()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'required' => true,
|
||||||
|
'placeholder' => [
|
||||||
|
'year' => 'Empty year',
|
||||||
|
],
|
||||||
|
'widget' => 'choice',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertSame('Empty year', $view['year']->vars['placeholder']);
|
||||||
|
$this->assertNull($view['week']->vars['placeholder']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassHtml5TypeIfSingleTextAndHtml5Format()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'single_text',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertSame('week', $view->vars['type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontPassHtml5TypeIfHtml5NotAllowed()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'single_text',
|
||||||
|
'html5' => false,
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey('type', $view->vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontPassHtml5TypeIfNotSingleText()
|
||||||
|
{
|
||||||
|
$view = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'text',
|
||||||
|
])
|
||||||
|
->createView();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey('type', $view->vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testYearTypeChoiceErrorsBubbleUp()
|
||||||
|
{
|
||||||
|
$error = new FormError('Invalid!');
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form['year']->addError($error);
|
||||||
|
|
||||||
|
$this->assertSame([], iterator_to_array($form['year']->getErrors()));
|
||||||
|
$this->assertSame([$error], iterator_to_array($form->getErrors()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWeekTypeChoiceErrorsBubbleUp()
|
||||||
|
{
|
||||||
|
$error = new FormError('Invalid!');
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form['week']->addError($error);
|
||||||
|
|
||||||
|
$this->assertSame([], iterator_to_array($form['week']->getErrors()));
|
||||||
|
$this->assertSame([$error], iterator_to_array($form->getErrors()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassDefaultChoiceTranslationDomain()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$view = $form->createView();
|
||||||
|
|
||||||
|
$this->assertFalse($view['year']->vars['choice_translation_domain']);
|
||||||
|
$this->assertFalse($view['week']->vars['choice_translation_domain']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassChoiceTranslationDomainAsString()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'choice_translation_domain' => 'messages',
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$view = $form->createView();
|
||||||
|
$this->assertSame('messages', $view['year']->vars['choice_translation_domain']);
|
||||||
|
$this->assertSame('messages', $view['week']->vars['choice_translation_domain']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassChoiceTranslationDomainAsArray()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'choice_translation_domain' => [
|
||||||
|
'year' => 'foo',
|
||||||
|
'week' => 'test',
|
||||||
|
],
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$view = $form->createView();
|
||||||
|
$this->assertSame('foo', $view['year']->vars['choice_translation_domain']);
|
||||||
|
$this->assertSame('test', $view['week']->vars['choice_translation_domain']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubmitNull($expected = null, $norm = null, $view = null)
|
||||||
|
{
|
||||||
|
$form = $this->factory->create($this->getTestedType(), null, [
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
$form->submit(null);
|
||||||
|
|
||||||
|
$this->assertSame(['year' => null, 'week' => null], $form->getData());
|
||||||
|
$this->assertSame(['year' => null, 'week' => null], $form->getNormData());
|
||||||
|
$this->assertSame(['year' => null, 'week' => null], $form->getViewData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubmitFromChoiceEmpty()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'choice',
|
||||||
|
'required' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form->submit([
|
||||||
|
'year' => '',
|
||||||
|
'week' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame(['year' => null, 'week' => null], $form->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubmitNullWithText()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'text',
|
||||||
|
]);
|
||||||
|
$form->submit(null);
|
||||||
|
|
||||||
|
$this->assertSame(['year' => null, 'week' => null], $form->getViewData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubmitNullWithSingleText()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => 'single_text',
|
||||||
|
'input' => 'string',
|
||||||
|
]);
|
||||||
|
$form->submit(null);
|
||||||
|
|
||||||
|
$this->assertNull($form->getData());
|
||||||
|
$this->assertNull($form->getNormData());
|
||||||
|
$this->assertSame('', $form->getViewData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubmitNullUsesDefaultEmptyData($emptyData = [], $expectedData = null)
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'empty_data' => $emptyData,
|
||||||
|
'widget' => 'choice',
|
||||||
|
]);
|
||||||
|
$form->submit(null);
|
||||||
|
|
||||||
|
$this->assertSame(['year' => null, 'week' => null], $form->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideEmptyData
|
||||||
|
*/
|
||||||
|
public function testSubmitNullUsesDateEmptyDataString($widget, $emptyData, $expectedData)
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'widget' => $widget,
|
||||||
|
'empty_data' => $emptyData,
|
||||||
|
]);
|
||||||
|
$form->submit(null);
|
||||||
|
|
||||||
|
$this->assertSame($expectedData, $form->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideEmptyData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Compound text field' => ['text', ['year' => '2019', 'week' => '1'], ['year' => 2019, 'week' => 1]],
|
||||||
|
'Compound choice field' => ['choice', ['year' => '2019', 'week' => '1'], ['year' => 2019, 'week' => 1]],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user