2013-06-05 44 views
0

我正在創建一個表單,讓用戶在指定的日期,時間和時區安排事件。我想結合這三個表單域的輸入並將它們存儲在數據庫的一個日期時間列中。根據輸入我想將指定的日期和時間轉換爲UTC。用Zend Framework 2表格處理指定時區的日期和時間

但是我不完全確定如何爲此編寫表單代碼。我在寫擴展字段集一個字段類,添加三個字段此字段集:

<?php 
namespace Application\Form\Fieldset; 

use Zend\Form\Fieldset; 
use Zend\InputFilter\InputFilterInterface; 
use Zend\InputFilter\InputFilterProviderInterface; 
use Zend\Stdlib\Hydrator\ClassMethods; 

class SendDateFieldset extends Fieldset implements InputFilterProviderInterface 
{ 
    public function __construct() 
    { 
     parent::__construct('senddate'); 

     $this->add(array(
       'name' => 'date', 
       'type' => 'Text', 
       'options' => array(
         'label' => 'Date to send:', 
       ) 
      ) 
     ); 

     $this->add(array(
       'name' => 'time', 
       'type' => 'Text', 
       'options' => array(
         'label' => 'Time to send:', 
       ) 
      ) 
     ); 

     $this->add(array(
       'name' => 'timezone', 
       'type' => 'Select', 
       'options'  => array(
       'label'    => "Recipient's timezone", 
       'value_options'  => array(
        -12   => '(GMT-12:00) International Date Line West', 
        -11   => '(GMT-11:00) Midway Island, Samoa', 
        -10   => '(GMT-10:00) Hawaii', 
       ), 
      ), 
      ) 
     ); 
    } 

    public function getInputFilterSpecification() 
    { 
     return array(
       'date' => array(
        'required' => true, 
        'filters' => array(
         array('name' => 'StringTrim'), 
        ), 
        'validators' => array(
         array(
          'name' => 'Date', 
          'break_chain_on_failure' => true, 
          'options' => array(
           'message' => 'Invalid date' 
          ), 
         ), 
        ), 
       ), 

       'time' => array(
        'required' => true, 
        'filters' => array(
         array('name' => 'StringTrim'), 
        ), 
       ), 

       'timezone' => array(
        'required' => true, 
       ), 
     ); 
    } 
} 

我那麼這個字段集添加到我的形式,像這樣:

<?php 
namespace Application\Form; 

use Zend\Form\Form; 

class Order extends Form 
{ 
    public function __construct() 
    { 
     parent::__construct("new-order"); 
     $this->setAttribute('action', '/order'); 
     $this->setAttribute('method', 'post'); 

     $this->add(
       array(
        'type' => 'Application\Form\Fieldset\SendDateFieldset', 
        'options' => array(
          'use_as_base_fieldset' => false 
        ),  
       ) 
     ); 
    } 
} 

我當然會等字段集添加到表單,訂單信息本身的基本字段集以及包含收件人信息的另一個字段集。

我對這個兩個問題:

  1. 什麼是處理三個字段和 它們存儲爲1個日期時間在數據庫(轉換爲UTC)最優雅的方式?我有 訂單服務對象也將負責處理 新訂單,所以我可以在負責處理該服務類中的新訂單的方法中負責處理該問題,或者有更好的方法嗎?

  2. 我只在 SendDate字段集中發佈了一小段時區列表。有沒有更清晰的方法來呈現這個列表?

+0

你可能想看看['Zend \ Form \ Element \ DateTime'](https://github.com/zendframework/zf2/blob/master/library/Zend/Form/Element/DateTimeSelect.php )。實際上,你可能不得不擴展這個選項以允許選擇一個TimeZone,但實質上就是這樣。 Value-Output被定義在'filters'回調內部的底部;) – Sam

+0

感謝Sam的建議。我看了一下,但它看起來依賴於HTML5 datetime表單元素。由於該網站將關注廣泛的受衆羣體,因此我無法依賴使用能夠呈現此內容的瀏覽器的用戶。我想我會堅持我的fieldset,並用jQuery日期和時間選擇器來處理事物的客戶端。如果我下定決心想出一個解決方案,我會在這裏發佈。 – Ruben

+0

當沒有給出瀏覽器支持時,DateTimeElement(輸入)將被視爲普通的'type = text'。所以使用datetime元素是很安全的。然後,你可以使用'Modernizr'或類似的東西來檢查BrowserDateTimeElement的功能,當它沒有給出時,使用jQueryUI來代替:) – Sam

回答

0

好吧,如承諾,我會分享我的解決方案,以解決這個問題。希望它能幫助未來的其他人。

我結束了使用SendDateFieldset,我最初已經。

申請\表格\字段集\ SendDateFieldset:

<?php 
namespace Application\Form\Fieldset; 

use Application\Hydrator\SendDate as SendDateHydrator; 

use Zend\Form\Fieldset; 
use Zend\InputFilter\InputFilterInterface; 
use Zend\InputFilter\InputFilterProviderInterface; 

class SendDateFieldset extends Fieldset implements InputFilterProviderInterface 
{ 
    public function __construct() 
    { 
     parent::__construct('senddate'); 
     $this->setHydrator(new SendDateHydrator()); 
     $this->setObject(new \DateTime()); 

     $this->add(array(
       'name' => 'date', 
       'type' => 'Text', 
       'options' => array(
         'label' => 'Date to send:', 
       ) 
      ) 
     ); 

     $this->add(array(
       'name' => 'time', 
       'type' => 'Text', 
       'options' => array(
         'label' => 'Time to send:', 
       ) 
      ) 
     ); 

     $this->add(array(
       'name' => 'timezone', 
       'type' => 'Select', 
       'options'  => array(
       'label'    => "Recipient's timezone", 
       'value_options'  => array(
         // The list of timezones is being populated by the OrderFormFactory 
       ), 
      ), 
      ) 
     ); 
    } 

    public function getInputFilterSpecification() 
    { 
     return array(
       'date' => array(
        'required' => true, 
        'filters' => array(
         array('name' => 'StringTrim'), 
        ), 
        'validators' => array(
         array(
          'name' => 'Date', 
          'break_chain_on_failure' => true, 
          'options' => array(
           'message' => 'Invalid date' 
          ), 
         ), 
        ), 
       ), 

       'time' => array(
        'required' => true, 
        'filters' => array(
         array('name' => 'StringTrim'), 
        ), 
        'validators' => array(
         array(
          'name' => 'Callback', 
          'options' => array(
            'callback' => function($value, $context) 
            { 
             // @todo: check if date and time is in the future 
             return true; 
            } 
          ), 
         ), 
        ), 
       ), 

       'timezone' => array(
        'required' => true, 
       ), 
     ); 
    } 
} 

正如你可以在這個字段集看到我現在用一個簡單的DateTime對象爲實體。要填充我使用自定義水化這個字段集DateTime對象:SendDateHydrator,它看起來像這樣:

<?php 
namespace Application\Hydrator; 

use Zend\Stdlib\Hydrator\AbstractHydrator; 
use DateTime; 
use DateTimeZone; 

class SendDate extends AbstractHydrator 
{ 
    public function __construct($underscoreSeparatedKeys = true) 
    { 
     parent::__construct(); 
    } 

    /** 
    * Extract values from an object 
    * 
    * @param object $object 
    * @return array 
    * @throws Exception\BadMethodCallException for a non-object $object 
    */ 
    public function extract($object) 
    { 
     throw new Exception\BadMethodCallException(sprintf(
        '%s is not implemented yet)', __METHOD__ 
      )); 
    } 

    /** 
    * Hydrate data into DateTime object 
    * 
    * @param array $data 
    * @param object $object 
    * @return object 
    * @throws Exception\BadMethodCallException for a non-object $object 
    */ 
    public function hydrate(array $data, $object) 
    { 
     if (!$object instanceof DateTime) 
     { 
      throw new Exception\BadMethodCallException(sprintf(
        '%s expects the provided $object to be a DateTime object)', __METHOD__ 
      )); 
     } 

     $object = null; 
     $object = new DateTime(); 

     if (array_key_exists('date', $data) && array_key_exists('time', $data) && array_key_exists('timezone', $data)) 
     { 
      $object = new DateTime($data['date'] . ' ' . $data['time'], new DateTimeZone($data['timezone'])); 
     } 
     else 
     { 
      throw new Exception\BadMethodCallException(sprintf(
        '%s expects the provided $data to contain a date, time and timezone)', __METHOD__ 
      )); 
     } 

     return $object; 
    } 
} 

水合物方法負責創建使用使用選擇框用戶指定的時區的DateTime對象。

要生成帶有時區的選擇,我做了一個小服務,它使用DateTimeZone生成一個時區列表並很好地格式化它們。最終結果是可以傳遞給select的值選項的關聯數組。該數組的鍵是DateTimeZone可以處理的官方時區標識符。我通過這個名單在工廠類負責創建形式,其中我用這個選擇框:

應用\廠\ OrderFormFactory:

<?php 
namespace Application\Factory; 

use Application\Service\TimezoneService; 

use Zend\ServiceManager\FactoryInterface; 
use Zend\ServiceManager\ServiceLocatorInterface; 

use Application\Form\Order as OrderForm; 

class OrderFormFactory implements FactoryInterface 
{ 
    public function createService(ServiceLocatorInterface $serviceLocator) 
    { 
     $orderForm = new OrderForm(); 

     /* @var $timezoneSvc TimezoneService */ 
     $timezoneSvc = $serviceLocator->get('Application\Service\TimezoneService'); 

     // Set list of timezones in SendDate fieldset 
     $orderForm->get('order')->get('senddate')->get('timezone')->setValueOptions(
      $timezoneSvc->getListOfTimezones() 
     ); 
     return $orderForm; 
    } 
} 

形式生成的字段集是這樣的: Screenshot

保存訂單時,訂單服務將DateTime轉換爲UTC時間,然後將其存儲在數據庫中。