-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #16809 [Form][FrameworkBundle][Bridge] Add a DateInterval for…
…m type (MisatoTremor) This PR was merged into the 3.2-dev branch. Discussion ---------- [Form][FrameworkBundle][Bridge] Add a DateInterval form type | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #13389 | License | MIT | Doc PR | symfony/symfony-docs#4817 Replaces #15030 Commits ------- f7669be [Form] Add a DateInterval form type Also add dateinterval widget to twig templates.
- Loading branch information
Showing
11 changed files
with
1,363 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
173 changes: 173 additions & 0 deletions
173
src/Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
<?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; | ||
use Symfony\Component\Form\Exception\UnexpectedTypeException; | ||
|
||
/** | ||
* Transforms between a normalized date interval and an interval string/array. | ||
* | ||
* @author Steffen Roßkamp <steffen.rosskamp@gimmickmedia.de> | ||
*/ | ||
class DateIntervalToArrayTransformer implements DataTransformerInterface | ||
{ | ||
const YEARS = 'years'; | ||
const MONTHS = 'months'; | ||
const DAYS = 'days'; | ||
const HOURS = 'hours'; | ||
const MINUTES = 'minutes'; | ||
const SECONDS = 'seconds'; | ||
const INVERT = 'invert'; | ||
|
||
private static $availableFields = array( | ||
self::YEARS => 'y', | ||
self::MONTHS => 'm', | ||
self::DAYS => 'd', | ||
self::HOURS => 'h', | ||
self::MINUTES => 'i', | ||
self::SECONDS => 's', | ||
self::INVERT => 'r', | ||
); | ||
private $fields; | ||
|
||
/** | ||
* @param string[] $fields The date fields | ||
* @param bool $pad Whether to use padding | ||
*/ | ||
public function __construct(array $fields = null, $pad = false) | ||
{ | ||
if (null === $fields) { | ||
$fields = array('years', 'months', 'days', 'hours', 'minutes', 'seconds', 'invert'); | ||
} | ||
$this->fields = $fields; | ||
$this->pad = (bool) $pad; | ||
} | ||
|
||
/** | ||
* Transforms a normalized date interval into an interval array. | ||
* | ||
* @param \DateInterval $dateInterval Normalized date interval. | ||
* | ||
* @return array Interval array. | ||
* | ||
* @throws UnexpectedTypeException If the given value is not a \DateInterval instance. | ||
*/ | ||
public function transform($dateInterval) | ||
{ | ||
if (null === $dateInterval) { | ||
return array_intersect_key( | ||
array( | ||
'years' => '', | ||
'months' => '', | ||
'weeks' => '', | ||
'days' => '', | ||
'hours' => '', | ||
'minutes' => '', | ||
'seconds' => '', | ||
'invert' => false, | ||
), | ||
array_flip($this->fields) | ||
); | ||
} | ||
if (!$dateInterval instanceof \DateInterval) { | ||
throw new UnexpectedTypeException($dateInterval, '\DateInterval'); | ||
} | ||
$result = array(); | ||
foreach (self::$availableFields as $field => $char) { | ||
$result[$field] = $dateInterval->format('%'.($this->pad ? strtoupper($char) : $char)); | ||
} | ||
if (in_array('weeks', $this->fields, true)) { | ||
$result['weeks'] = 0; | ||
if (isset($result['days']) && (int) $result['days'] >= 7) { | ||
$result['weeks'] = (string) floor($result['days'] / 7); | ||
$result['days'] = (string) ($result['days'] % 7); | ||
} | ||
} | ||
$result['invert'] = '-' === $result['invert']; | ||
$result = array_intersect_key($result, array_flip($this->fields)); | ||
|
||
return $result; | ||
} | ||
|
||
/** | ||
* Transforms an interval array into a normalized date interval. | ||
* | ||
* @param array $value Interval array | ||
* | ||
* @return \DateInterval Normalized date interval | ||
* | ||
* @throws UnexpectedTypeException If the given value is not an array. | ||
* @throws TransformationFailedException If the value could not be transformed. | ||
*/ | ||
public function reverseTransform($value) | ||
{ | ||
if (null === $value) { | ||
return; | ||
} | ||
if (!is_array($value)) { | ||
throw new UnexpectedTypeException($value, 'array'); | ||
} | ||
if ('' === implode('', $value)) { | ||
return; | ||
} | ||
$emptyFields = array(); | ||
foreach ($this->fields as $field) { | ||
if (!isset($value[$field])) { | ||
$emptyFields[] = $field; | ||
} | ||
} | ||
if (count($emptyFields) > 0) { | ||
throw new TransformationFailedException(sprintf('The fields "%s" should not be empty', implode('", "', $emptyFields))); | ||
} | ||
if (isset($value['invert']) && !is_bool($value['invert'])) { | ||
throw new TransformationFailedException('The value of "invert" must be boolean'); | ||
} | ||
foreach (self::$availableFields as $field => $char) { | ||
if ($field !== 'invert' && isset($value[$field]) && !ctype_digit((string) $value[$field])) { | ||
throw new TransformationFailedException(sprintf('This amount of "%s" is invalid', $field)); | ||
} | ||
} | ||
try { | ||
if (!empty($value['weeks'])) { | ||
$interval = sprintf( | ||
'P%sY%sM%sWT%sH%sM%sS', | ||
empty($value['years']) ? '0' : $value['years'], | ||
empty($value['months']) ? '0' : $value['months'], | ||
empty($value['weeks']) ? '0' : $value['weeks'], | ||
empty($value['hours']) ? '0' : $value['hours'], | ||
empty($value['minutes']) ? '0' : $value['minutes'], | ||
empty($value['seconds']) ? '0' : $value['seconds'] | ||
); | ||
} else { | ||
$interval = sprintf( | ||
'P%sY%sM%sDT%sH%sM%sS', | ||
empty($value['years']) ? '0' : $value['years'], | ||
empty($value['months']) ? '0' : $value['months'], | ||
empty($value['days']) ? '0' : $value['days'], | ||
empty($value['hours']) ? '0' : $value['hours'], | ||
empty($value['minutes']) ? '0' : $value['minutes'], | ||
empty($value['seconds']) ? '0' : $value['seconds'] | ||
); | ||
} | ||
$dateInterval = new \DateInterval($interval); | ||
if (isset($value['invert'])) { | ||
$dateInterval->invert = $value['invert'] ? 1 : 0; | ||
} | ||
} catch (\Exception $e) { | ||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); | ||
} | ||
|
||
return $dateInterval; | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
...Symfony/Component/Form/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?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; | ||
use Symfony\Component\Form\Exception\UnexpectedTypeException; | ||
|
||
/** | ||
* Transforms between a date string and a DateInterval object. | ||
* | ||
* @author Steffen Roßkamp <steffen.rosskamp@gimmickmedia.de> | ||
*/ | ||
class DateIntervalToStringTransformer implements DataTransformerInterface | ||
{ | ||
private $format; | ||
private $parseSigned; | ||
|
||
/** | ||
* Transforms a \DateInterval instance to a string. | ||
* | ||
* @see \DateInterval::format() for supported formats | ||
* | ||
* @param string $format The date format | ||
* @param bool $parseSigned Whether to parse as a signed interval | ||
*/ | ||
public function __construct($format = 'P%yY%mM%dDT%hH%iM%sS', $parseSigned = false) | ||
{ | ||
$this->format = $format; | ||
$this->parseSigned = $parseSigned; | ||
} | ||
|
||
/** | ||
* Transforms a DateInterval object into a date string with the configured format. | ||
* | ||
* @param \DateInterval $value A DateInterval object | ||
* | ||
* @return string An ISO 8601 or relative date string like date interval presentation | ||
* | ||
* @throws UnexpectedTypeException If the given value is not a \DateInterval instance. | ||
*/ | ||
public function transform($value) | ||
{ | ||
if (null === $value) { | ||
return ''; | ||
} | ||
if (!$value instanceof \DateInterval) { | ||
throw new UnexpectedTypeException($value, '\DateInterval'); | ||
} | ||
|
||
return $value->format($this->format); | ||
} | ||
|
||
/** | ||
* Transforms a date string in the configured format into a DateInterval object. | ||
* | ||
* @param string $value An ISO 8601 or date string like date interval presentation | ||
* | ||
* @return \DateInterval An instance of \DateInterval | ||
* | ||
* @throws UnexpectedTypeException If the given value is not a string. | ||
* @throws TransformationFailedException If the date interval could not be parsed. | ||
*/ | ||
public function reverseTransform($value) | ||
{ | ||
if (null === $value) { | ||
return; | ||
} | ||
if (!is_string($value)) { | ||
throw new UnexpectedTypeException($value, 'string'); | ||
} | ||
if ('' === $value) { | ||
return; | ||
} | ||
if (!$this->isISO8601($value)) { | ||
throw new TransformationFailedException('Non ISO 8601 date strings are not supported yet'); | ||
} | ||
$valuePattern = '/^'.preg_replace('/%([yYmMdDhHiIsSwW])(\w)/', '(?P<$1>\d+)$2', $this->format).'$/'; | ||
if (!preg_match($valuePattern, $value)) { | ||
throw new TransformationFailedException(sprintf('Value "%s" contains intervals not accepted by format "%s".', $value, $this->format)); | ||
} | ||
try { | ||
$dateInterval = new \DateInterval($value); | ||
} catch (\Exception $e) { | ||
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); | ||
} | ||
|
||
return $dateInterval; | ||
} | ||
|
||
private function isISO8601($string) | ||
{ | ||
return preg_match('/^P(?=\w*(?:\d|%\w))(?:\d+Y|%[yY]Y)?(?:\d+M|%[mM]M)?(?:(?:\d+D|%[dD]D)|(?:\d+W|%[wW]W))?(?:T(?:\d+H|[hH]H)?(?:\d+M|[iI]M)?(?:\d+S|[sS]S)?)?$/', $string); | ||
} | ||
} |
Oops, something went wrong.