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.
		
		
		
		
		
			
		
			
				
					
					
						
							408 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
	
	
							408 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; | 
						|
 | 
						|
        //$class = call_user_func(array($this->virtualClassName, 'getInstance')); | 
						|
 | 
						|
        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; | 
						|
    } | 
						|
}
 | 
						|
 |