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.
		
		
		
		
		
			
		
			
				
					
					
						
							405 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
	
	
							405 lines
						
					
					
						
							14 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 array|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', | |
|         ]; | |
|     } | |
|  | |
|     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; | |
|     } | |
| }
 | |
| 
 |