Então, depois de passar quase um dia lutando com esse mesmo problema em minha própria extensão, pensei em compartilhar minha solução aqui. Algo a observar é que minhas entidades não estão utilizando o sistema EAV, cada entidade possui sua própria tabela plana com uma coluna para cada atributo. Minha entidade possui quatro atributos de data e hora - dois dos quais são preenchidos a partir da entrada do usuário ( open_date
, close_date
) e dois são preenchidos automaticamente pelo código-fonte ( create_date
, close_date
).
Classe de modelo da entidade
Na classe de modelo da entidade, incluí o seguinte:
- Uma maneira de definir e buscar quais atributos são do tipo datetime e são preenchidos por dados inseridos pelo usuário fornecidos no horário local da loja.
- Um método que converte apenas esses campos do horário GMT no horário local da loja (mais sobre isso posteriormente).
- Um método _beforeSave () que define os atributos 'edit_date' e 'create_date' (que não são preenchidos via entrada do usuário) no GMT automaticamente para mim, ao salvar.
O código fonte:
/**
* Model's datetime attributes that are populated with user data supplied
* in the store's local time.
*
* @var array
*/
protected $_dateFields = array(
'open_date',
'close_date',
);
/**
* Return the model's datetime attributes that are populated with user
* data supplied in the store's local time.
*
* @return array
*/
public function getDateFields()
{
return $this->_dateFields;
}
/**
* (non-PHPdoc)
* @see Mage_Core_Model_Abstract::_beforeSave()
*/
protected function _beforeSave()
{
parent::_beforeSave();
$date = Mage::getModel('core/date')->gmtDate();
if (!$this->getId()) {
$this->setData('create_date', $date);
}
$this->setData('edit_date', $date);
return $this;
}
O saveAction do Admin Controller da entidade
No método saveAction do meu controlador, utilizei o método getDateFields () definido na classe de modelo para saber quais atributos eu preciso alterar da hora local da loja (que foi inserida pelo usuário) para a hora GMT antes de salvar no banco de dados. Observe que este é apenas um trecho parcial do meu método de salvamento:
....
$data = $this->getRequest()->getPost()
// Convert user input time from the store's local time to GMT time
$dateFields = $model->getDateFields();
if (is_array($dateFields) && count($dateFields)) {
$data = $this->_filterDateTime($data, $dateFields);
$store_timezone = new DateTimeZone(Mage::getStoreConfig('general/locale/timezone'));
$gmt_timezone = new DateTimeZone('Europe/London');
foreach ($dateFields as $key) if (isset($data[$key])) {
$dateTime = new DateTime($data[$key], $store_timezone);
$dateTime->setTimezone($gmt_timezone);
$data[$key] = $dateTime->format('Y-m-d H:i:s');
}
}
$model->addData($data);
try {
$model->save();
....
O bloco de formulário do administrador para editar a entidade
Ao contrário do widget de grade administrativa do Magento, que espera que os valores de data e hora das coleções sejam fornecidos no GMT, com a intenção de converter esses valores no horário local da loja antes de exibir a página, o widget de formulário administrativo do Magento não segue esse comportamento. Em vez disso, o widget de formulário aceitará o valor de data e hora como está e o exibirá sem ajustar automaticamente a hora. Portanto, como os valores são armazenados no banco de dados no GMT, primeiro devemos converter nossos atributos de data e hora inseridos pelo usuário no horário local da loja antes de fornecer esses dados ao formulário. É aqui que o nosso número 2 da classe Modelo da entidade entra em jogo.
Aqui está uma parte do método _prepareForm () da classe de bloco do meu formulário de administrador (que estende Mage_Adminhtml_Block_Widget_Form). Omiti a maioria da minha função, tentando incluir apenas o mínimo necessário para essa questão e ainda assim fornecer um método de classe válido:
protected function _prepareForm()
{
$form = new Varien_Data_Form();
$model = Mage::registry('YOUR_MODEL_CLASS');
$date_format = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM);
$time_zone = $this->__('Time Zone: %s', Mage::getStoreConfig('general/locale/timezone'));
$calendar_img = $this->getSkinUrl('images/grid-cal.gif');
$fieldset = $form->addFieldset('base_fieldset', array('legend'=> $this->__('General Information')));
$fieldset->addField('open_date', 'datetime', array(
'name' => 'open_date',
'label' => $this->__('Open Date'),
'title' => $this->__('Open Date'),
'time' => 'true',
'image' => $calendar_img,
'format' => $date_format,
'style' => 'width:160px',
'required' => true,
'note' => $time_zone
));
$fieldset->addField('close_date', 'datetime', array(
'name' => 'close_date',
'label' => $this->__('Close Date'),
'title' => $this->__('Close Date'),
'time' => 'true',
'image' => $calendar_img,
'format' => $date_format,
'style' => 'width:160px',
'required' => true,
'note' => $time_zone
));
if ($model->getId()) {
$form->setValues($model->getAdminFormData());
}
$this->setForm($form);
return parent::_prepareForm();
}
A maior parte disso segue qualquer outro widget de formulário para o Magento. No entanto, a principal coisa que é importante observar aqui é que, em vez de ligar $form->setValues($model->getData())
, chamamos $form->setValues($model->getAdminFormData())
. Que, se revisarmos meu código do primeiro segmento desta resposta, esse método leva a converter todos os atributos de data e hora, inseridos pelo usuário, de GMT para o horário local da loja.
Resultado final:
- Todos os valores salvos no DB no horário GMT.
- Os valores inseridos pelo usuário são convertidos de GMT para a hora local da loja, antes de serem editados para o formulário
- A grade do administrador funciona como sempre, recebendo valores GMT e convertendo para o horário local da loja antes de renderizar a grade na página.
Espero que isso seja um recurso valioso para ajudar alguém por aí algum dia. Na hora de trabalhar no desenvolvimento de front-end, lembre-se de que os valores de data e hora estão em GMT no DB!