Last active
January 24, 2017 12:01
-
-
Save voskobovich/7df1c603d9a0dacfc22930157157f6a2 to your computer and use it in GitHub Desktop.
Yii2 Multilang Toolkit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class UrlManager extends \yii\web\UrlManager | |
{ | |
/** | |
* @inheritdoc | |
*/ | |
public function createUrl($params) | |
{ | |
//Получаем сформированный URL(без префикса идентификатора языка) | |
$url = parent::createUrl($params); | |
$currentLanguageCode = Language::getCurrent('code'); | |
$defaultLanguageCode = Language::getDefault('code'); | |
if ($currentLanguageCode != $defaultLanguageCode) { | |
if ($url == '/') { | |
$url = '/' . $currentLanguageCode; | |
} else { | |
$url = '/' . $currentLanguageCode . $url; | |
} | |
} | |
return $url; | |
} | |
} | |
class Request extends \yii\web\Request | |
{ | |
/** | |
* language code | |
* @var string | |
*/ | |
private $_language_code = null; | |
/** | |
* All languages models | |
* @var Language[] | |
*/ | |
private $_languages = null; | |
/** | |
* Get all language models | |
* @return string | |
*/ | |
private function getLanguages() | |
{ | |
if ($this->_languages == null) { | |
$this->_languages = Language::findAll(); | |
} | |
return $this->_languages; | |
} | |
/** | |
* get language code from current url | |
* @return string | |
*/ | |
private function parseLanguageCode() | |
{ | |
$url = parent::getUrl(); | |
if ($this->_language_code == null) { | |
$urlList = explode('/', $url); | |
$languageCode = isset($urlList[1]) ? $urlList[1] : null; | |
$languages = $this->getLanguages(); | |
if (isset($languages[$languageCode])) { | |
$this->_language_code = $languageCode; | |
} | |
} | |
return $this->_language_code; | |
} | |
/** | |
* @return string | |
*/ | |
public function getUrl() | |
{ | |
$url = parent::getUrl(); | |
$this->_language_code = $this->parseLanguageCode(); | |
if ($this->_language_code != null) { | |
$url = substr($url, strlen($this->_language_code) + 1); | |
} | |
return $url; | |
} | |
/** | |
* Get current language code | |
* @return string | |
*/ | |
public function getLanguageCode() | |
{ | |
$languageCode = $this->parseLanguageCode(); | |
if ($languageCode == null) { | |
$languageCode = Language::getDefault('code'); | |
} | |
return $languageCode; | |
} | |
} | |
class BaseLanguage extends ActiveRecord | |
{ | |
/** | |
* Instance default model | |
* @var BaseLanguage | |
*/ | |
private static $_defaultModel = null; | |
/** | |
* Instance current model | |
* @var BaseLanguage | |
*/ | |
private static $_currentModel = null; | |
/** | |
* Instances all model | |
* @var BaseLanguage | |
*/ | |
private static $_allModels = null; | |
/** | |
* @var UploadedFile | |
*/ | |
public $file; | |
/** | |
* @inheritdoc | |
*/ | |
public static function tableName() | |
{ | |
return '{{%language}}'; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function rules() | |
{ | |
return [ | |
[['code', 'locale', 'name'], 'required'], | |
[['position', 'is_default'], 'integer'], | |
[['code'], 'string', 'max' => 5], | |
[['locale'], 'string', 'max' => 10], | |
[['name'], 'string', 'max' => 40], | |
[['icon_filename'], 'string', 'max' => 255], | |
['file', 'file', 'extensions' => ['png', 'gif', 'jpg', 'svg']], | |
]; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function attributeLabels() | |
{ | |
return [ | |
'id' => Yii::t('language', 'ID'), | |
'code' => Yii::t('language', 'Code'), | |
'locale' => Yii::t('language', 'locale'), | |
'name' => Yii::t('language', 'Name'), | |
'icon_filename' => Yii::t('language', 'Icon File'), | |
'file' => Yii::t('language', 'Icon'), | |
'position' => Yii::t('language', 'Position'), | |
'is_default' => Yii::t('language', 'Is Default'), | |
]; | |
} | |
/** | |
* @return bool|void | |
*/ | |
public function beforeValidate() | |
{ | |
$this->file = UploadedFile::getInstance($this, 'file'); | |
if ($this->file) { | |
$this->icon_filename = $this->code.'.'.$this->file->extension; | |
} | |
return parent::beforeValidate(); | |
} | |
/** | |
* @param bool $insert | |
* @param array $changedAttributes | |
* @return bool|void | |
*/ | |
public function afterSave($insert, $changedAttributes) | |
{ | |
$this->saveIcon(); | |
parent::afterSave($insert, $changedAttributes); | |
} | |
/** | |
* Сохранение иконки | |
*/ | |
private function saveIcon() | |
{ | |
if ($this->file) { | |
$iconPath = Yii::getAlias("@webroot") . '/uploads/images/language'; | |
$oldFiles = FileHelper::findFiles($iconPath, [ | |
'only' => ["{$this->code}.*"] | |
] | |
); | |
foreach ($oldFiles as $oldFile) { | |
unlink($oldFile); | |
} | |
$this->file->saveAs("{$iconPath}/{$this->code}.{$this->file->extension}"); | |
} | |
} | |
/** | |
* Get default model or attribute of model | |
* @param null $attribute | |
* @return string|null|BaseLanguage | |
*/ | |
public static function getDefault($attribute = null) | |
{ | |
if (self::$_defaultModel === null) { | |
$languages = self::findAll(); | |
foreach ($languages as $language) { | |
if ($language->is_default) { | |
self::$_defaultModel = $language; | |
break; | |
} | |
} | |
} | |
return !empty(self::$_defaultModel) && !empty($attribute) && isset(self::$_defaultModel->{$attribute}) | |
? self::$_defaultModel->{$attribute} : self::$_defaultModel; | |
} | |
/** | |
* Get current model or attribute of model | |
* @param null $attribute | |
* @return string|null|BaseLanguage | |
*/ | |
public static function getCurrent($attribute = null) | |
{ | |
/** @var \app\components\Request $request */ | |
$request = Yii::$app->request; | |
$languageCode = $request->getLanguageCode(); | |
if ($attribute == 'code') { | |
return $languageCode; | |
} | |
if (self::$_currentModel === null) { | |
$languages = self::findAll(); | |
self::$_currentModel = !empty($languages[$languageCode]) ? | |
$languages[$languageCode] : null; | |
} | |
if (self::$_currentModel === null) { | |
self::$_currentModel = self::getDefault(); | |
} | |
return !empty(self::$_currentModel) && !empty($attribute) && isset(self::$_currentModel->{$attribute}) | |
? self::$_currentModel->{$attribute} : self::$_currentModel; | |
} | |
/** | |
* @inheritdoc | |
* @return static[] an array of ActiveRecord instances, or an empty array if nothing matches. | |
*/ | |
public static function findAll($condition = []) | |
{ | |
if (self::$_allModels === null) { | |
self::$_allModels = self::find($condition) | |
->orderBy([ | |
'is_default' => SORT_DESC, | |
'position' => SORT_ASC | |
]) | |
->indexBy('code') | |
->all(); | |
} | |
return self::$_allModels; | |
} | |
/** | |
* Данные для элемента формы DropDownList | |
* Возвращает массив записей или пустой массив | |
* | |
* @param string $indexField - атрибут индексации | |
* @param string $labelField - атрибут отображаемого имени | |
* @return array | |
*/ | |
public static function listAll($indexField = 'id', $labelField = 'name') | |
{ | |
$query = static::find(); | |
$models = $query | |
->orderBy([ | |
'is_default' => SORT_ASC, | |
'position' => SORT_DESC | |
]) | |
->asArray() | |
->all(); | |
$items = array_column($models, $labelField, $indexField); | |
return (count($items) > 0) ? $items : []; | |
} | |
/** | |
* Get change language url | |
* @param bool $scheme | |
* @return string | |
*/ | |
public function viewUrl($scheme = false) | |
{ | |
return Url::to('/' . $this->code, $scheme); | |
} | |
/** | |
* Get icon url | |
* @return string | |
*/ | |
public function getIcon() | |
{ | |
return "/uploads/images/language/{$this->icon_filename}"; | |
} | |
} | |
abstract class ActiveRecordMultiLanguage extends ActiveRecord implements MultiLanguageInterface | |
{ | |
/** | |
* Get translate model in current or default translations | |
* @param string $languageCode | |
* @return ActiveRecord|null | |
* @throws HttpException | |
*/ | |
public function getTranslate($languageCode = null) | |
{ | |
if ($this->getTranslates()->indexBy != 'language_code') { | |
throw new HttpException(500, | |
'The result should be indexed on a column "language_code". Use indexBy(\'language_code\').'); | |
} | |
if (empty($languageCode)) { | |
$languageCode = Language::getCurrent('code'); | |
} | |
$translate = !empty($this->translates[$languageCode]) ? | |
$this->translates[$languageCode] : null; | |
if (empty($translate)) { | |
$languageCode = Language::getDefault('code'); | |
$translate = !empty($this->translates[$languageCode]) ? | |
$this->translates[$languageCode] : null; | |
} | |
return $translate; | |
} | |
/** | |
* Get attribute from translate model | |
* @param $attributeName | |
* @param null $defaultValue | |
* @param null $languageCode | |
* @return mixed|null | |
*/ | |
public function getTranslateAttribute($attributeName, $defaultValue = null, $languageCode = null) | |
{ | |
$translate = $this->getTranslate($languageCode); | |
return (isset($translate->{$attributeName})) ? $translate->{$attributeName} : $defaultValue; | |
} | |
/** | |
* Данные для элемента формы DropDownList | |
* Возвращает массив записей или пустой массив | |
* | |
* @param string $indexField - атрибут индексации | |
* @param string $labelField - атрибут отображаемого имени | |
* @param string $languageCode - код языка | |
* @return array | |
*/ | |
public static function listAll($indexField = 'id', $labelField = 'translate.name', $languageCode = null) | |
{ | |
if ($indexOfTranslate = strpos($indexField, 'translate') !== false) { | |
$indexField = str_replace('translate.', '', $indexField); | |
} | |
if ($labelOfTranslate = strpos($labelField, 'translate') !== false) { | |
$labelField = str_replace('translate.', '', $labelField); | |
} | |
$query = static::find()->with('translates'); | |
$models = $query->all(); | |
$items = []; | |
/** @var static $model */ | |
foreach ($models as $model) { | |
// Index | |
if ($indexOfTranslate) { | |
/** @var \app\db\ActiveRecord $translate */ | |
$translate = $model->getTranslate($languageCode); | |
$index = isset($translate->{$indexField}) ? $translate->{$indexField} : null; | |
} else { | |
$index = isset($model->{$indexField}) ? $model->{$indexField} : null; | |
} | |
// Label | |
if ($labelOfTranslate) { | |
/** @var \app\db\ActiveRecord $translate */ | |
$translate = $model->getTranslate($languageCode); | |
$label = isset($translate->{$labelField}) ? $translate->{$labelField} : Yii::t('app', '(not set)'); | |
} else { | |
$label = isset($model->{$labelField}) ? $model->{$labelField} : Yii::t('app', '(not set)'); | |
} | |
$items[$index] = $label; | |
} | |
return (count($items) > 0) ? $items : []; | |
} | |
/** | |
* Поиск записи по слагу и текущему языку | |
* | |
* @param $slug | |
* @return null|ActiveRecordMultiLanguage | |
*/ | |
public static function findBySlug($slug) | |
{ | |
$currentLanguage = Language::getCurrent('code'); | |
$defaultLanguage = Language::getDefault('code'); | |
$models = self::find() | |
->joinWith([ | |
'translates' => function ($query) { | |
/** @var \yii\db\Query $query */ | |
return $query->from(['t1' => $query->from['t']]); | |
} | |
]) | |
->andWhere(['t1.slug' => $slug]) | |
->andWhere('t1.language_code = :currentLanguage OR t1.language_code = :defaultLanguage', [ | |
':currentLanguage' => $currentLanguage, | |
':defaultLanguage' => $defaultLanguage, | |
]) | |
->indexBy(function ($row) { | |
/** @var static $row */ | |
return $row->getTranslateAttribute('language_code'); | |
}) | |
->all(); | |
if (!empty ($models[$currentLanguage])) { | |
return $models[$currentLanguage]; | |
} else { | |
if (!empty ($models[$defaultLanguage])) { | |
return $models[$defaultLanguage]; | |
} | |
} | |
return null; | |
} | |
} | |
interface MultiLanguageInterface | |
{ | |
/** | |
* Все переводы модели | |
* @return mixed | |
*/ | |
public function getTranslates(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment