Skip to content

Instantly share code, notes, and snippets.

@voskobovich
Last active January 24, 2017 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save voskobovich/7df1c603d9a0dacfc22930157157f6a2 to your computer and use it in GitHub Desktop.
Save voskobovich/7df1c603d9a0dacfc22930157157f6a2 to your computer and use it in GitHub Desktop.
Yii2 Multilang Toolkit
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