Morphodo.com
  • Home
  • Contact
  • Imprint
  • Partner
  • morphing ideas into solutions - TYPO3, FLOW3,
    Mobile Apps ...

  • FLOW3 Tutorials
  • FLOW3 Testing
  • How to use the DateTimeConverter in FLOW3

    In FLOW3 converters are used to transform POST data into different data types. The target type of an converted value is configured in the PHPdoc comment of a method or member declaration. Possible convertions range from primitive data to abstract or defined objects.

    The DateTimeConverter is part of the FLOW3 subpackage "Property" and that is only one of severall useful converters. I'll write about some further interesting converters and there special handling here.

    Let's see how a model with a date member variable can be defined as a type of  \DateTime.

    <?php
    namespace T3DD11\Events\Domain\Model;
    
    ...
    
    /**
     * Model to handle Event 
     *
     * @scope prototype
     * @entity
     */
    class Event {
    
      /**
       * @var string
       * @validate StringLength(minimum=3, maximum=25)
       */
      protected $title;
    
      /**
       * @var \DateTime
       */
      protected $date;
    }
    

    That's how an event model is defined as argument of an action in your controller.

    <?php
    namespace T3DD11\Events\Controller;
    
    ...
    
    /**
     * Event controller for the T3DD11.Events package
     *
     *
     * @author Michael Klapper <development@morphodo.com>
     */
    class EventController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
    
        /**
         * @var \T3DD11\Events\Domain\Repository\EventRepository
         * @inject
         */
        protected $eventRepository;
    
    
        /**
         * Create and Persist a new Event.
         *
         * @param  \T3DD11\Events\Domain\Model\Event $event
         * @return void
         * @author Michael Klapper <development@morphodo.com>
         * 
         */
        public function createAction(\T3DD11\Events\Domain\Model\Event $event) {
                // Add the new Event to the Repository
            $this->eventRepository->add($event);
            $this->redirect('index');
        }
    }
    

    The default DateTimeConverter works pretty well and you can define a custom date time format for each of your class members. Before you can use the DateTimeConverter in action you must configure property mapping instruction inside the "initializeCreateAction" as below.

        /**
         * Configure the date format on Event.
         *
         * @return void
         * @author Michael Klapper <development@morphodo.com>
         * 
         */
        public function initializeCreateAction() {
            $this->arguments['event']
                ->getPropertyMappingConfiguration()
                ->forProperty('date')
                ->setTypeConverterOption('TYPO3\FLOW3\Property\TypeConverter\DateTimeConverter', \TYPO3\FLOW3\Property\TypeConverter\DateTimeConverter::CONFIGURATION_DATE_FORMAT, 'Y-m-d');
        }
    

    Now you  are ready to convert string formatted date time values into an \DateTime object.

    There is only one downside if your use the default DateTimeConverter when you try to use only server side validation. As the DateTimeConverter is really strict and throws Exception on invalid user input like
    an empty or invalid string it is recommend to create a new DateTimeConverter in your package. The advantage of an custom DateTimeValidator is that you can use Error objects to bring the error message to the user interface instead of throwing Exceptions.

    Here is how the new DateTimeConverter should look like.

    <?php
    namespace T3DD11\Events\Property\TypeConverter;
    
    ...
    
    /**
     * Converts string, array into an DateTime object
     *
     * @date 02.07.11
     * @time 01:19
     * 
     * @scope singleton
     * @author Michael Klapper <development@morphodo.com>
     * 
     */
    class DateTimeConverter extends \TYPO3\FLOW3\Property\TypeConverter\DateTimeConverter {
    
        /**
         * @var integer
         */
        protected $priority = 5;
    
        /**
         * Empty strings can't be converted
         *
         * @param string $source
         * @param string $targetType
         * @return boolean
         * @author Michael Klapper <development@morphodo.com>
         * 
         */
        public function canConvertFrom($source, $targetType) {
            if ($targetType !== 'DateTime') {
                return FALSE;
            }
            if (is_array($source)) {
                return TRUE;
            }
            return is_string($source);
        }
    
        /**
         * Converts $source to a \DateTime using the configured dateFormat
         *
         * @param string $source the string to be converted to a \DateTime object
         * @param string $targetType must be "DateTime"
         * @param array $convertedChildProperties not used currently
         * @param \TYPO3\FLOW3\Property\PropertyMappingConfigurationInterface $configuration
         * @return \DateTime
         * @author Michael Klapper <development@morphodo.com>
         *
         */
        public function convertFrom($source, $targetType, array $convertedChildProperties = array(), \TYPO3\FLOW3\Property\PropertyMappingConfigurationInterface $configuration = NULL) {
            $dateFormat = $this->getDefaultDateFormat($configuration);
            if (is_string($source)) {
                $dateAsString = $source;
            } else {
                if (!isset($source['date']) || !is_string($source['date'])) {
                    return new \TYPO3\FLOW3\Error\Error( 'Could not convert the given source into a DateTime object because it was not an array with a valid date as a string', 1308003914);
                }
                $dateAsString = $source['date'];
                if (isset($source['dateFormat']) && strlen($source['dateFormat']) > 0) {
                    $dateFormat = $source['dateFormat'];
                }
            }
            $date = \DateTime::createFromFormat($dateFormat, $dateAsString);
            if ($date === FALSE || $dateAsString === '') {
                return new \TYPO3\FLOW3\Error\Error('The string "' . $dateAsString . '" could not be converted to DateTime with format "' . $dateFormat . '"', 1307719788);
            }
            if (is_array($source)) {
                $this->overrideTimeIfSpecified($date, $source);
                $this->overrideTimezoneIfSpecified($date, $source);
            }
            return $date;
        }
    }
    
    

    What is changed?

    We overwrite the canConvertFrom method to allow empty strings, this enables the convertFrom method to deal with it. The next important change is that we simply replaced the "throws new Exception" into "return new Error". At least we increase the "$proiority" member in order to avoid conflicts with the original shipped DateTimeConverter.

    What do we expect now?

    If the user fill the form with invalid date time string values we expect to see an validation error message provided by the Fluid ViewHelper "form.validationResults".

    <f:form.validationResults for="{for}">
            <f:if condition="{validationResults.flattenedErrors}">
                    <div class="error">
                        <ul>
                            <f:for each="{validationResults.flattenedErrors}" key="propertyPath" as="errors">
                                <li>{propertyPath}: <f:for each="{errors}" as="error">{error.message}</f:for></li>
                            </f:for>
                        </ul>
                    </div>
            </f:if>
    </f:form.validationResults>
    
    blog comments powered by Disqus