You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
10 KiB
361 lines
10 KiB
<?php |
|
/** |
|
* Created by Error202 |
|
* Date: 24.08.2018 |
|
*/ |
|
|
|
namespace core\behaviors; |
|
|
|
use yii\base\Behavior; |
|
use yii\base\InvalidConfigException; |
|
use yii\db\ActiveRecord; |
|
use yii\db\ActiveQuery; |
|
|
|
class LanguageBehavior extends Behavior |
|
{ |
|
/** |
|
* Attributes for translate, ex.: ['name', 'content'] |
|
* @var array |
|
*/ |
|
public $attributes; |
|
|
|
/** |
|
* Class name for language active record entity |
|
* @var string |
|
*/ |
|
public $virtualClassName = 'VirtualTranslate'; |
|
|
|
/** |
|
* Field name for language in translate table |
|
* @var string |
|
*/ |
|
public $languageField = 'language'; |
|
|
|
/** |
|
* Field name for tables relative, ex.: 'post_id' |
|
* @var string |
|
*/ |
|
public $relativeField; |
|
|
|
/** |
|
* Available languages, ex.: ['en', 'ru'] |
|
* @var array |
|
*/ |
|
public $translatedLanguages; |
|
|
|
/** |
|
* Default language, ex.: 'en' |
|
* @var string |
|
*/ |
|
public $defaultLanguage; |
|
|
|
/** |
|
* Translate table name, ex.: 'post_lng' |
|
* @var string |
|
*/ |
|
public $tableName; |
|
|
|
/** |
|
* Abridge the language ID. |
|
* @var boolean whether to abridge the language ID. |
|
*/ |
|
public $abridge = true; |
|
|
|
/** |
|
* Delete relative if foreign key not set on delete: cascade |
|
* @var bool |
|
*/ |
|
public $forceDelete = false; |
|
|
|
private $ownerClassName; |
|
private $ownerClassShortName; |
|
private $ownerPrimaryKey; |
|
private $languageAttributes = []; |
|
|
|
/** |
|
* Control events firing |
|
* @return array |
|
*/ |
|
public function events() |
|
{ |
|
return [ |
|
ActiveRecord::EVENT_AFTER_FIND => 'afterFind', |
|
ActiveRecord::EVENT_AFTER_UPDATE => 'afterUpdate', |
|
ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert', |
|
ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete', |
|
//ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate', |
|
]; |
|
} |
|
|
|
public function attach( $owner ) { |
|
/** @var ActiveRecord $owner */ |
|
parent::attach($owner); |
|
if (empty($this->translatedLanguages) || !is_array($this->translatedLanguages)) { |
|
throw new InvalidConfigException('Please specify array of available languages for the ' . get_class($this) . ' in the ' |
|
. get_class($this->owner) . ' or in the application parameters', 101); |
|
} |
|
|
|
if (array_values($this->translatedLanguages) !== $this->translatedLanguages) { //associative array |
|
$this->translatedLanguages = array_keys($this->translatedLanguages); |
|
} |
|
|
|
if (!$this->defaultLanguage) { |
|
throw new InvalidConfigException('Please specify default language for the ' . get_class($this)); |
|
} |
|
|
|
if (empty($this->attributes) || !is_array($this->attributes)) { |
|
throw new InvalidConfigException('Please specify translated attributes for the ' . get_class($this) . ' in the ' |
|
. get_class($this->owner), 103); |
|
} |
|
|
|
$this->ownerClassName = get_class($this->owner); |
|
$this->ownerClassShortName = $this->getShortClassName($this->ownerClassName); |
|
|
|
/** @var ActiveRecord $className */ |
|
$className = $this->ownerClassName; |
|
$this->ownerPrimaryKey = $className::primaryKey()[0]; |
|
|
|
if (!isset($this->relativeField)) { |
|
throw new InvalidConfigException('Please specify relativeField for the ' . get_class($this) . ' in the ' |
|
. get_class($this->owner), 105); |
|
} |
|
|
|
//$rules = $owner->rules(); |
|
//$validators = $owner->getValidators(); |
|
/*foreach ($rules as $rule) { |
|
if (in_array($rule[1], $this->excludedValidators)) |
|
continue; |
|
$rule_attributes = is_array($rule[0]) ? $rule[0] : [$rule[0]]; |
|
$attributes = array_intersect($this->attributes, $rule_attributes); |
|
if (empty($attributes)) |
|
continue; |
|
$rule_attributes = []; |
|
foreach ($attributes as $key => $attribute) { |
|
foreach ($this->languages as $language) |
|
if ($language != $this->defaultLanguage) |
|
$rule_attributes[] = $this->getAttributeName($attribute, $language); |
|
} |
|
if (isset($rule['skipOnEmpty']) && !$rule['skipOnEmpty']) |
|
$rule['skipOnEmpty'] = !$this->requireTranslations; |
|
$params = array_slice($rule, 2); |
|
if ($rule[1] !== 'required' || $this->requireTranslations) { |
|
$validators[] = Validator::createValidator($rule[1], $owner, $rule_attributes, $params); |
|
} elseif ($rule[1] === 'required') { |
|
$validators[] = Validator::createValidator('safe', $owner, $rule_attributes, $params); |
|
} |
|
}*/ |
|
|
|
$this->createLanguageClass(); |
|
$translation = new $this->virtualClassName; |
|
|
|
foreach ($this->translatedLanguages as $language) { |
|
foreach ($this->attributes as $attribute) { |
|
$attributeName = $attribute; |
|
$this->setLanguageAttribute($attribute . '_' . $language, $translation->{$attributeName}); |
|
|
|
//$this->owner->__set($attribute . '_' . $language, $translation->{$attributeName}); |
|
//$this->owner->{$attribute . '_' . $language} = $translation->{$attributeName}; |
|
//$this->owner->createProperty($attribute . '_' . $language, $translation->{$attributeName}); |
|
|
|
if ($language == $this->defaultLanguage) { |
|
$this->setLanguageAttribute($attribute, $translation->{$attributeName}); |
|
//$this->owner->__set($attribute, $translation->{$attributeName}); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Insert event |
|
*/ |
|
public function afterInsert() |
|
{ |
|
$this->saveTranslations(); |
|
} |
|
|
|
/** |
|
* Update event |
|
*/ |
|
public function afterUpdate() |
|
{ |
|
/** @var ActiveRecord $owner */ |
|
$owner = $this->owner; |
|
//if ($owner->isRelationPopulated('translations')) { |
|
if ($translationRecords = $owner->translations) { |
|
$translations = $this->indexByLanguage($owner->getRelatedRecords()['translations']); |
|
//$translations = $this->indexByLanguage($translationRecords); |
|
$this->saveTranslations($translations); |
|
} |
|
} |
|
|
|
/** |
|
* Find event |
|
*/ |
|
public function afterFind() |
|
{ |
|
/** @var ActiveRecord $owner */ |
|
$owner = $this->owner; |
|
if ($owner->isRelationPopulated('translations') && $related = $owner->getRelatedRecords()['translations']) { |
|
$translations = $this->indexByLanguage($related); |
|
foreach ($this->translatedLanguages as $language) { |
|
foreach ($this->attributes as $attribute) { |
|
foreach ($translations as $translation) { |
|
if ($translation->{$this->languageField} == $language) { |
|
$attributeName = $attribute; |
|
$this->setLanguageAttribute($attribute . '_' . $language, $translation->{$attributeName}); |
|
if ($language == $this->defaultLanguage) { |
|
$this->setLanguageAttribute($attribute, $translation->{$attributeName}); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} else { |
|
if (!$owner->isRelationPopulated('translation')) { |
|
$owner->translation; |
|
} |
|
$translation = $owner->getRelatedRecords()['translation']; |
|
if ($translation) { |
|
foreach ($this->attributes as $attribute) { |
|
$attribute_name = $attribute; |
|
$owner->setLanguageAttribute($attribute, $translation->$attribute_name); |
|
} |
|
} |
|
} |
|
foreach ($this->attributes as $attribute) { |
|
if ($owner->hasAttribute($attribute) && $this->getLangAttribute($attribute)) { |
|
$owner->setAttribute($attribute, $this->getLangAttribute($attribute)); |
|
} |
|
} |
|
} |
|
|
|
public function afterDelete() |
|
{ |
|
if ($this->forceDelete) { |
|
/** @var ActiveRecord $owner */ |
|
$owner = $this->owner; |
|
$owner->unlinkAll('translations', true); |
|
} |
|
} |
|
|
|
public function createLanguageClass() |
|
{ |
|
if (!class_exists($this->virtualClassName, false)) { |
|
eval(' |
|
use yii\db\ActiveRecord; |
|
class ' . $this->virtualClassName . ' extends ActiveRecord |
|
{ |
|
public static function tableName() |
|
{ |
|
return \'' . $this->tableName . '\'; |
|
} |
|
}'); |
|
} |
|
} |
|
|
|
private function saveTranslations($translations = []) |
|
{ |
|
/** @var ActiveRecord $owner */ |
|
$owner = $this->owner; |
|
if (!isset($owner->_form) || !$owner->_form) { |
|
return; |
|
} |
|
foreach ($this->translatedLanguages as $language) { |
|
$isDefaultLanguage = $language == $this->defaultLanguage; |
|
if (!isset($translations[$language])) { |
|
/** @var ActiveRecord $translation */ |
|
$translation = new $this->virtualClassName; |
|
$translation->{$this->languageField} = $language; |
|
$translation->{$this->relativeField} = $owner->getPrimaryKey(); |
|
} else { |
|
$translation = $translations[$language]; |
|
} |
|
$save = false; |
|
foreach ($this->attributes as $attribute) { |
|
//$value = $isDefaultLanguage ? $owner->$attribute : $owner->{$attribute . '_' . $language}; |
|
$value = $isDefaultLanguage ? $owner->_form->$attribute : $owner->_form->{$attribute . '_' . $language}; |
|
if ($value !== null) { |
|
//$field = $isDefaultLanguage ? $attribute : $attribute . '_' . $language; |
|
$field = $attribute; |
|
$translation->$field = $value; |
|
$save = true; |
|
} |
|
} |
|
if ($translation->isNewRecord && !$save) { |
|
continue; |
|
} |
|
$translation->save(); |
|
} |
|
} |
|
|
|
private function getShortClassName($className) |
|
{ |
|
return substr($className, strrpos($className, '\\') + 1); |
|
} |
|
|
|
public function setLanguageAttribute($name, $value) |
|
{ |
|
$this->languageAttributes[$name] = $value; |
|
} |
|
|
|
protected function indexByLanguage(array $records) |
|
{ |
|
$sorted = []; |
|
foreach ($records as $record) { |
|
$sorted[$record->{$this->languageField}] = $record; |
|
} |
|
unset($records); |
|
return $sorted; |
|
} |
|
|
|
/** |
|
* Relation to model translations |
|
* @return ActiveQuery |
|
*/ |
|
public function getTranslations() |
|
{ |
|
/** @var ActiveRecord */ |
|
return $this->owner->hasMany($this->virtualClassName, [$this->relativeField => $this->ownerPrimaryKey]); |
|
} |
|
|
|
public function getTranslation($language = null) |
|
{ |
|
//if (basename(\Yii::$app->getBasePath()) === 'backend') { |
|
// $language = $language ?: $this->defaultLanguage; |
|
//} |
|
//else { |
|
$language = $language ?: \Yii::$app->language; |
|
//} |
|
// if translate exists |
|
$translate = $this->virtualClassName::find() |
|
->andWhere([$this->relativeField => $this->owner->id]) |
|
->andWhere([$this->languageField => $language]) |
|
->one(); |
|
|
|
$language = $translate ? $language : $this->defaultLanguage; |
|
|
|
return $this->owner->hasOne($this->virtualClassName, [$this->relativeField => $this->ownerPrimaryKey]) |
|
->where([$this->languageField => $language]); |
|
} |
|
|
|
public function findTranslation($language = null) |
|
{ |
|
$language = $language ?: $this->defaultLanguage; |
|
//$class = call_user_func(array($this->virtualClassName, 'getInstance')); |
|
return $this->virtualClassName::find() |
|
->andWhere([$this->relativeField => $this->owner->id]) |
|
->andWhere([$this->languageField => $language]) |
|
->one(); |
|
} |
|
|
|
public function hasLangAttribute($name) |
|
{ |
|
return array_key_exists($name, $this->languageAttributes); |
|
} |
|
|
|
public function getLangAttribute($name) |
|
{ |
|
return $this->hasLangAttribute($name) ? $this->languageAttributes[$name] : null; |
|
} |
|
|
|
}
|
|
|