'afterFind', ActiveRecord::EVENT_AFTER_UPDATE => 'afterUpdate', ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert', ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete', ]; } 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; if (is_array($this->relativeField)) { foreach ($this->relativeField as $field) { $translation->{$field} = $owner->{$field}; } } else { $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 */ $condition = []; if (is_array($this->relativeField)) { foreach ($this->relativeField as $field) { //$condition[$field] = $this->owner->{$field}; $condition[$field] = $field; } return $this->owner->hasMany($this->virtualClassName, $condition); } else { 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 if (is_array($this->relativeField)) { $condition = []; foreach ($this->relativeField as $field) { $condition[$field] = $this->owner->{$field}; } $translate = $this->virtualClassName::find() ->andWhere($condition) ->andWhere([$this->languageField => $language]) ->one(); } else { $translate = $this->virtualClassName::find() ->andWhere([$this->relativeField => $this->owner->id]) ->andWhere([$this->languageField => $language]) ->one(); } $language = $translate ? $language : $this->defaultLanguage; if (is_array($this->relativeField)) { $condition = []; foreach ($this->relativeField as $field) { $condition[$field] = $field; } return $this->owner->hasOne($this->virtualClassName, $condition) ->where([$this->languageField => $language]); } else { return $this->owner->hasOne($this->virtualClassName, [$this->relativeField => $this->_ownerPrimaryKey]) ->where([$this->languageField => $language]); } } public function findTranslation($language = null) { $language = $language ?: $this->defaultLanguage; if (is_array($this->relativeField)) { $condition = []; foreach ($this->relativeField as $field) { $condition[$field] = $this->owner[$field]; } return $this->virtualClassName::find() ->andWhere($condition) ->andWhere([$this->languageField => $language]) ->one(); } else { 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; } }