Для получения полного доступа
зарегистрируйтесь.
Сниппет,  PHP

YII2: Поведение для удобного перевода контента моделей


Если вы разрабатываете мультиязычное преложние на Yii2, то могу предложить использовать следующее поведение, которое позволяет очень удобно переводить значения полей моделей. Достаточно добавить модель, подключить поведение, указать, какие поля надо переводить и забыть об этом.

Итак, поехали. Модель Contents - в ней хранятся все переводы:

<?php

namespace app\models;

use Yii;
use app\components\Model;

/**
 * Модель для таблицы "contents".
 *
 * @property integer $id
 * @property string $model
 * @property integer $record
 * @property string $attribute
 * @property string $locale
 * @property string $content
 */
class Contents extends Model {
    /**
     * @inheritdoc
     */
    public static function tableName() {
        return 'contents';
    }

    /**
     * @inheritdoc
     */
    public function rules() {
        return [
            [['model', 'record', 'attribute', 'locale', 'content'], 'required'],
            [['record'], 'integer'],
            [['content'], 'string'],
            [['model', 'attribute'], 'string', 'max' => 25],
            [['locale'], 'string', 'max' => 5]
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels() {
        return [
            'id' => 'ID',
            'model' => 'Модель',
            'record' => 'Запись',
            'attribute' => 'Атрибут',
            'locale' => 'Язык',
            'content' => 'Содержимое',
        ];
    }
}

SQL-запрос не генерацию модели:

CREATE TABLE `contents` (
  `id` int(7) NOT NULL COMMENT 'ID',
  `model` varchar(25) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Модель',
  `record` int(7) NOT NULL COMMENT 'Запись',
  `attribute` varchar(25) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Атрибут',
  `locale` varchar(5) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Язык',
  `content` text COLLATE utf8_unicode_ci NOT NULL COMMENT 'Содержимое'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

ALTER TABLE `contents` ADD PRIMARY KEY (`id`);
ALTER TABLE `contents` MODIFY `id` int(7) NOT NULL AUTO_INCREMENT COMMENT 'ID';

Теперь само поведение:

<?php


/**
 * Поведение для работы перевода контента моделей
 * var $this->owner app\components\Model;
 */
namespace app\components\behaviors;

use app\models\Contents;
use yii\db\ActiveRecord;
use yii\base\Behavior;
use Yii;
use yii\base\ErrorException;

class TranslateBehavior extends Behavior {

    /**
     * @var array Атрибуты, которые мы будем подвергать переводам
     */
    public $attributes = [];
    private $translates = [];

    public function events() {
        return [
            ActiveRecord::EVENT_AFTER_FIND => 'afterFind',
            ActiveRecord::EVENT_AFTER_UPDATE => 'afterFind',
            ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
        ];
    }

    public function afterFind() {
        if(Yii::$app->language == Yii::$app->sourceLanguage) return;
        $this->owner->setAttributes($this->getTranslate());
    }

    public function beforeUpdate() {
        if(Yii::$app->language == Yii::$app->sourceLanguage) return;

        foreach ($this->attributes as $attribute) {
            if (isset($this->owner->$attribute)) {
                $this->owner->$attribute = $this->syncTranslate($attribute);
            } else {
                throw new ErrorException("В модели {$this->owner->className()} не найден атрибут {$attribute}");
            }
        }

    }

    /**
     * @param $attribute
     * @return array
     *
     * Возвращает условия для поиска
     */
    private function getCondition($attributes) {
        return [
            'model' => $this->owner->className(),
            'record' => $this->owner->id,
            'locale' => Yii::$app->language,
            'attribute' => $attributes
        ];
    }

    /**
     * @param $attribute
     * @return mixed
     *
     * Возвращает перевод для запрошенного атрибута или для всех сразу
     *
     */
    private function getTranslate($attribute = null) {

        if (empty($this->translates)) {
            $contents = Contents::find()->asArray()->select(["attribute","content"])->where($this->getCondition($this->attributes))->all();

            $this->translates = array_reduce($contents, function ($result, $item) {
                $result[$item['attribute']] = $item['content'];
                return $result;
            });
        }

        return $attribute? isset($this->translates[$attribute])? $this->translates[$attribute] : $this->owner->$attribute : $this->translates;
    }

    /**
     * @param $attribute
     * @return mixed
     *
     * Сохраняет перевод и возвращает правильное значение для поля
     */
    private function syncTranslate($attribute) {
        $content = Contents::find()->where($this->getCondition($attribute))->one();

        if (!$content) {
            $content = new Contents();
            $content->setAttributes($this->getCondition($attribute));
        }

        $content->content = $this->owner->$attribute;
        $content->save();

        return $this->owner->oldAttributes[$attribute];
    }
}

Готово! Теперь осталось подключить поведение к целевой модели:

use app\components\behaviors\TranslateBehavior;

И подключить поведение, указав, какие поля будет переводить:

public function behaviors() {
        return array_merge(parent::behaviors(), [
            [
                'class' => TranslateBehavior::className(),
                'attributes' => ['firstName','lastName'] /** TODO перечислить те поля, которые мы хотим использовать*/
            ]
        ]);
    }

Все, теперь можете об этом забыть! Переключая язык в Yii::$app->language вы будете получать всегда нужный текст.

  yii2 php I18N transale behavior

Автор


Маклай
  • Репутация: 8
  • Сниппеты : 1
  • Ревизии : 2
Подписаться

Чтобы увидеть комментарии, нужно быть участником сообщества

Регистрация