Antonio, Horacio,
a ver que pensáis sobre todo lo recopilado hasta ahora.
El issue que había puesto si creo que sirve para el propósito. En este issue el estado se deja en wontfix (no necesita arreglar porque no es un bug) y comenta que depende mucho de la BD y que por tanto hay que usar otros mecanismos aparte de cambiar el core de Yii para no hacerlo dependiente de la BD.
Todo lo que hablamos se basa en un lenguaje origen y un lenguaje destino. El origen es, normalmente, el en_us y el destino puede ser variado y se configuran en el main.php en las variables "sourceLanguage" y "language".
return array(
'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
'name'=>'Asociados',
'defaultController'=>'site',
'sourceLanguage'=>'en_us',
'language'=>'es_es',
'layout'=>'asociados',
...
Hay que destacar que el lenguaje origen debería coincidir con el lenguaje de los productos usados por la aplicación, en este caso hablamos de la base de datos. Queremos que en las vistas se presenten los datos en el formato del lenguaje destino pero se almacenen en la BD y se traten los datos en Yii en el lenguaje origen.
Sobre todos estos supuestos entonces tenemos un camino bidireccional, Source Language <==> Target Language (Locale).
Siguiendo lo que qiang.xue responde en el issue lo primero que habría que realizar son las funciones GET y SET del atributo del modelo que necesite un formato según la localización (normalmente son los numéricos y los referentes al tiempo, date & datetime).
Voy a poner un modelo ejemplo:
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('id_asociado, id_estado, fecha', 'required'),
array('id_asociado, id_estado', 'length', 'max'=>10),
array('fecha', 'type', 'type'=>'date' ,'dateFormat'=>Yii::app()->locale->dateFormat),
array('notas', 'safe'),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id_historial, id_asociado, id_estado, fecha, notas, timestamp', 'safe', 'on'=>'search'),
);
}
Vemos que tengo el atributo fecha que es de tipo date y le aplico como regla de validación que siga el formato del lenguaje objetivo, es decir, el locale.
Ahora agregamos dos funciones nuevas en el modelo para el trato con el atributo fecha y así poder realizar las conversiones entre los lenguages origen y destino según la necesidad:
public function getFecha()
{
return Yii::app()->locale->dateFormatter->formatDateTime($this->fecha,'medium',null);
}
public function setFecha($value)
{
//Aquí podemos usar cualquier función para pasar al formato aceptado por la BD
//$this->fecha=date_format(new DateTime($value),'Y-m-d');
$this->fecha=date_format(DateTime::createFromFormat('j/n/Y',$value),'Y-m-d');
}
Ya tenemos la función getFecha que la podemos usar para recoger su valor original en el modelo pero con el formato definido en el locale que queremos.
También tenemos la funcion setFecha que la podemos usar para cambiar el valor del atributo en el modelo y dejarlo en el formato del lenguaje origen. En este caso como no tenemos funciones para el lenguaje origen tenemos que definir un formato de forma implicita, por eso indicamos ‘Y-m-d’, que es el formato aceptado por la BD, en mi caso MySQL.
Bien, ya tenemos armadas las funciones de los atributos que necesitan cambiar el formato según el locale. Ahora vamos a ver cuando usarlas.
Lenguaje origen -> Lenguaje destino (Locale):
=============================================
Normalmente las situaciones en las que queremos tener los atributos en el formato del lenguaje destino van a ser en las vistas, pero el trato de estos atributos y las operaciones internas que deseemos utilizar las haremos en el lenguaje origen.
Para ello antes de enviar a la vista el atributo o el modelo entero con el atributo debemos modificar su valor.
En la función del controlador haríamos las consultas necesarias y antes de pasarle el modelo u otro resultado (un objeto Json) debemos hacer la conversión.
Si es un modelo se podría cambiar el atributo directamente:
//Primero recogeríamos los datos del origen necesario $_POST, $_GET u otro sitio y rellenaríamos la variable $model
//Por ejemplo recoger de un POST
$model->attributes=$_POST['formName'];
$model->fecha=$model->getFecha();
//Y ahora enviaríamos el modelo a la vista
$this->render('nameView',array('model'=>$model));
En la vista veríamos el atributo con el formato correcto:
<div class="form">
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'formName',
'enableAjaxValidation'=>false,
)); ?>
...
<div class="row">
<?php echo $form->labelEx($model,'fecha'); ?> (DD/MM/AAAA)
<?php
echo $form->textField($model,'fecha');
?>
<?php echo $form->error($model,'fecha'); ?>
</div>
...
Recordar que en las reglas de validación hemos puesto que valide según el formato del locale que tengamos configurado, lo digo para luego la parte del CRUD.
Ya hemos terminado con el paso de VER en el locale correcto los atributos.
Lenguaje destino (Locale) -> Lenguaje origen:
=============================================
Aquí suponemos que tenemos una vista, un formulario por ejemplo, y vamos a realizar algo con los datos introducidos en ella. El flujo sería, introducir datos o modificar datos en la vista, darle al botón que hace submit del formulario e iremos a la acción del controlador seleccionada y haremos algo con ellos, por ejemplos, se actualizarán los datos en la BD.
Aquí tenemos dos caminos.
1.- Uno de ellos es el que comenta Horacio de usar Behaviours (beforeSave, etc…) en el modelo y usar la funcion set del atributo necesario. En este ejemplo lo que hacemos es llamar en la acción al método save del modelo como siempre y luego en el modelo sobreescribimos la función beforeSave para que cambie el valor antes de realizar el save() del modelo.
public function beforeSave()
{
$this->setFecha($this->fecha);
return parent::beforeSave();
}
2.- En el controlador antes de llamar a las funciones donde sea necesario tener el formato del origen (la base de datos) realizamos el cambio del valor que viene de la vista.
public function actionUpdateModel()
{
$model=new Model();
$model->setScenario('update');
if(isset($_POST['formName']))
{
$model=Model::model()->findByPk($_POST['formName']['id_field']);
$model->attributes=$_POST['formName'];
$model->setFecha($model->fecha);
$model->save(true);
}
}
OR
public function actionUpdateModel()
{
$model=new Model();
$model->setScenario('update');
if(isset($_POST['formName']))
{
$model=Model::model()->findByPk($_POST['formName']['id_field']);
$model->attribute1=$_POST['formName']['attribute1'];
$model->attribute2=$_POST['formName']['attribute2'];
$model->setFecha($model->fecha); //aquí se podría haber hecho otra función en el modelo que devuelva el valor en formateado en vez de cambiarlo en el modelo directamente, getFechaOrigen. $model->fecha=$model->getFechaOrigen();
$model->save(true);
}
}
Todo esto lo acabo de poner sobre la marcha, si podéis probarlo estaría bien para afinar la solución.
Lo único que falta es que se implementen funciones/propiedades para el lenguaje origen y de esta manera poder definir el formato de origen y no tener que poner el formato de forma implicita en las funciones SET.
Espero que os sirva.
Saludos.