I’ve written a LocalizedBehavior that works as following:
Suppose you have a table (and model) Post, with fields id, title, content. The title and content fields store values in the primary or default language of the application.
Then you have a table (and model) PostLang, with fields id, postId, lang, title, content, which stores title and content in additional languages.
By adding the LocalizedBehavior to the Post model, you can do:
$posts = Post::model()->localized('es')->findAll($criteria);
What this will do is overwrite the value of the title and content attributes with their localized version. In case these values are empty it will leave the original ones. If the requested language is the same as the primary language, it will have no effect. You can also call it without the language parameter:
$posts = Post::model()->localized()->findAll($criteria);
And it will use the language currently set in the application.
It works fine, but maybe the implementation could be improved. What I’m doing internally is creating a relation, applying the with() method, and using afterFind() to process the results. All comments are welcome!
<?php
class LocalizedBehavior extends CActiveRecordBehavior {
/**
* Name of the field that stores the language in the translations table
*/
public $langField = 'lang';
/**
* Name of the model/table that stores the translations.
* For 'Post', it defaults to 'PostLang'.
*/
public $langClassName;
/**
* Name of the foreign key column of the translations table.
* For 'Post', it defaults to 'postId'.
*/
public $langForeignKey;
public $skipFields;
public $primaryLang;
public $overwriteIfEmpty = false;
public function localized($lang=null) {
$obj = $this->Owner;
if (!$lang) $lang = Yii::app()->language;
if ($lang == $this->getPrimaryLang()) return $obj;
$class = CActiveRecord::HAS_MANY;
$obj->getMetaData()->relations['localized'] = new $class('localized', $this->getLangClassName(), $this->getLangForeignKey(),
array('index'=>$this->langField, 'condition'=>"??.".$this->langField."='".$lang."'"));
return $obj->with('localized');
}
public function afterFind($event) {
$obj = $this->Owner;
if (isset($obj->localized) && sizeof($obj->localized)>0) {
$row = current($obj->localized);
$skipFields = $this->getSkipFields();
foreach ($row->getAttributes() as $field=>$value) {
if (!in_array($field, $skipFields)) {
if ($value || $this->overwriteIfEmpty) $obj->$field = $value;
}
}
}
}
private function getPrimaryLang() {
if (!$this->primaryLang)
return Yii::app()->sourceLanguage;
return $this->primaryLang;
}
private function getLangForeignKey() {
if (!$this->langForeignKey)
return strtolower($this->Owner->tableName()).'Id';
return $this->langForeignKey;
}
private function getlangClassName() {
if (!$this->langClassName)
return $this->Owner->tableName().'Lang';
return $this->langClassName;
}
private function getSkipFields() {
if (!$this->skipFields)
return array('id', $this->getLangForeignKey(), $this->langField);
return $this->skipFields;
}
}
(Note: for some reason, camelcase in the class names LocalizedBehavior and CActiveRecordBehavior appears messed up)