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.
352 lines
10 KiB
352 lines
10 KiB
6 years ago
|
<?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')) {
|
||
|
$translations = $this->indexByLanguage($owner->getRelatedRecords()['translations']);
|
||
|
$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;
|
||
|
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)
|
||
|
{
|
||
|
$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 : \Yii::$app->params['defaultLanguage'];
|
||
|
|
||
|
return $this->owner->hasOne($this->virtualClassName, [$this->relativeField => $this->ownerPrimaryKey])
|
||
|
->where([$this->languageField => $language]);
|
||
|
}
|
||
|
|
||
|
public function findTranslation($language = null)
|
||
|
{
|
||
|
$language = $language ?: \Yii::$app->language;
|
||
|
//$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;
|
||
|
}
|
||
|
|
||
|
}
|