加入Model的翻譯機制

This commit is contained in:
kroutony 2020-02-23 14:35:27 +08:00
parent c68e25de0a
commit 9ebaa6fcd2
5 changed files with 530 additions and 0 deletions

25
app/ModelTranslation.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ModelTranslation extends Model
{
protected $fillable = [
'model_type',
'model_id',
'locale',
'text'
];
protected $hidden = [
'model_type',
'model_id',
];
public function model()
{
return $this->morphTo();
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Observers;
use App\Repositories\ModelTranslationRepository;
use App\TranslatableModel;
class TranslatableModelOberserver
{
/**
* Handle the translatable model "created" event.
*
* @param \App\TranslatableModel $translatableModel
* @return void
*/
public function created(TranslatableModel $translatableModel)
{
//
}
/**
* Handle the translatable model "updated" event.
*
* @param \App\TranslatableModel $translatableModel
* @return void
*/
public function updated(TranslatableModel $translatableModel)
{
//
}
/**
* Handle the translatable model "deleted" event.
*
* @param \App\TranslatableModel $translatableModel
* @return void
*/
public function deleted(TranslatableModel $translatableModel)
{
$modelTranslationRepository = app(ModelTranslationRepository::class);
$modelTranslationRepository->setTranslatedModel($translatableModel);
$modelTranslationRepository->deleteModelTranslations($translatableModel->id);
}
/**
* Handle the translatable model "restored" event.
*
* @param \App\TranslatableModel $translatableModel
* @return void
*/
public function restored(TranslatableModel $translatableModel)
{
//
}
/**
* Handle the translatable model "force deleted" event.
*
* @param \App\TranslatableModel $translatableModel
* @return void
*/
public function forceDeleted(TranslatableModel $translatableModel)
{
//
}
}

View File

@ -0,0 +1,334 @@
<?php
namespace App\Repositories;
use App\TranslatableModel;
use Illuminate\Database\Eloquent\Model;
use App\ModelTranslation;
use Cache;
class ModelTranslationRepository extends BaseRepository
{
/**
* @var Model $translatedModel
*/
private $translatedModel = '';
public function __construct(ModelTranslation $modelTranslation)
{
$this->setModel($modelTranslation);
}
/**
* 產生Cache key
*
* @param $model
* @param $modelId
* @param $attribute
* @param string $locale
* @return string
*/
private function getCacheKey($model, $modelId, $attribute, $locale = '')
{
return "model_translation_{$model}_{$modelId}_{$attribute}_{$locale}";
}
/**
* 設定當前的Model
*
* @param $model
*/
public function setTranslatedModel($model)
{
if(is_string($model)) {
$this->translatedModel = $model;
} elseif($model instanceof TranslatableModel) {
$this->translatedModel = get_class($model);
}
return $this;
}
/**
* 更新一項翻譯需傳入目標model
*
* @param $model
* @param $modelId
* @param $attribute
* @param $locale
* @param $content
*/
public function updateTranslation($model, $modelId, $attribute, $locale, $content)
{
$updated = $this->model->updateOrInsert([
'model_type' => $model,
'model_id' => $modelId,
'attribute' => $attribute,
'locale' => $locale,
], [
'model_type' => $model,
'model_id' => $modelId,
'attribute' => $attribute,
'locale' => $locale,
'content' => $content
]);
Cache::forget($this->getCacheKey($model, $modelId, $attribute, $locale));
}
/**
* 更新一項翻譯
*
* @param $modelId
* @param $attribute
* @param $locale
* @param $content
*/
public function updateModelTranslation($modelId, $attribute, $locale, $content)
{
return $this->updateTranslation($this->translatedModel, $modelId, $attribute, $locale, $content);
}
/**
* 取得一項翻譯需傳入目標model
*
* @param $model
* @param $modelId
* @param $attribute
* @param $locale
* @return string|null
*/
public function getTranslation($model, $modelId, $attribute, $locale)
{
$cacheKey = $this->getCacheKey($model, $modelId, $attribute, $locale);
if(Cache::has($cacheKey)) {
return Cache::get($cacheKey);
}
$record = $this->model->where([
'model_type' => $model,
'model_id' => $modelId,
'attribute' => $attribute,
'locale' => $locale,
])->first();
if($record) {
Cache::put($cacheKey, $record->content);
return $record->content;
} else {
Cache::put($cacheKey, null);
return null;
}
}
/**
* 取得一項翻譯
*
* @param $modelId
* @param $attribute
* @param $locale
* @return string|null
*/
public function getModelTranslation($modelId, $attribute, $locale)
{
return $this->getTranslation($this->translatedModel, $modelId, $attribute, $locale);
}
/**
* 取得所有語系的翻譯內容需傳入目標model
*
* @param $model
* @param $modelId
* @param $attribute
* @return array
*/
public function getTranslations($model, $modelId, $attribute)
{
$cacheKey = $this->getCacheKey($model, $modelId, $attribute);
if(Cache::has($cacheKey)) {
return Cache::get($cacheKey);
}
$record = $this->model->where([
'model_type' => $model,
'model_id' => $modelId,
'attribute' => $attribute,
])
->get()
->mapWithKeys(function($model){
/** @var \App\ModelTranslation $model */
return [$model->locale => $model->content];
})
->toArray();
Cache::put($cacheKey, $record);
return $record;
}
/**
* 取得所有語系的翻譯內容
*
* @param $modelId
* @param $attribute
* @return array
*/
public function getModelTranslations($modelId, $attribute)
{
return $this->getTranslations($this->translatedModel, $modelId, $attribute);
}
/**
* 取得所有屬性的所有語系翻譯需傳入目標model
*
* @param $model
* @param $modeId
* @return array
*/
public function getAllTranslations($model, $modeId)
{
/** @var TranslatableModel $modelInstance */
$modelInstance = app($model);
$translatableAttributes = $modelInstance->getTranslatableAttributes();
$translations = [];
$modelTranslations = $this->model->where([
'model_type' => $model,
'model_id' => $modeId
])
->whereIn('attribute', $translatableAttributes)
->get();
foreach ($modelTranslations as $modelTranslation) {
$translations[$modelTranslation->attribute][$modelTranslation->locale] = $modelTranslation->content;
}
return $translations;
}
/**
* 取得所有屬性的所有語系翻譯
*
* @param $modelId
* @return array
*/
public function getModelAllTranslations($modelId)
{
return $this->getAllTranslations($this->translatedModel, $modelId);
}
/**
* 刪除所有語系的翻譯可指定屬性需傳入目標model
*
* @param $model
* @param $modelId
* @param null $attribute
* @return bool|null
* @throws \Exception
*/
public function deleteTranslations($model, $modelId, $attribute = null)
{
$cacheKey = $this->getCacheKey($model, $modelId, $attribute);
$whereArg = [
'model_type' => $model,
'model_id' => $modelId
];
if(!empty($attribute)) {
$whereArg['attribute'] = $attribute;
}
$translations = $this->model->where($whereArg)->delete();
Cache::forget($cacheKey);
return $translations;
}
/**
* 刪除所有語系的翻譯,可指定屬性
*
* @param $modelId
* @param null $attribute
* @throws \Exception
*/
public function deleteModelTranslations($modelId, $attribute = null)
{
$this->deleteTranslations($this->translatedModel, $modelId, $attribute);
}
/**
* 批次取得指定屬性群的指定語系翻譯需傳入目標model
*
* @param $model
* @param $modelId
* @param $locale
* @param $attributes
* @return mixed
*/
public function getTranslationsWithAttributesInLocale($model, $modelId, $locale, $attributes)
{
$translations = $this->model->where([
'model_type' => $model,
'model_id' => $modelId,
'locale' => $locale,
])
->whereIn('attribute', $attributes)
->get();
return $translations;
}
/**
* 批次取得指定屬性群與指定ID群的指定語系翻譯需傳入目標model
*
* @param $model
* @param $locale
* @param $ids
* @param $attributes
* @return mixed
*/
public function getTranslationsWithIdsAndAttributesInLocale($model, $locale, $ids, $attributes)
{
$translations = $this->model->select(['model_id', 'attribute', 'content'])
->where([
'model_type' => $model,
'locale' => $locale,
])
->whereIn('attribute', $attributes)
->whereIn('model_id', $ids)
->orderBy('model_id')
->get();
return $translations;
}
/**
* 搜尋指定屬性的所有語系翻譯需傳入目標model可指定語系
*
* @param $model
* @param $attribute
* @param $search
* @param string $compare
* @param null $locale
* @return mixed
*/
public function searchAttribute($model, $attribute, $search, $compare = '=', $locale = null)
{
$modelQuery = $this->model
->where('model_type', $model)
->where('attribute', $attribute)
->where('content', $compare, $search);
if($locale) {
$modelQuery->where('locale', $locale);
}
return $modelQuery->get();
}
/**
* 搜尋指定屬性的所有語系翻譯,可指定語系
*
* @param $attribute
* @param $search
* @param string $compare
* @param null $locale
* @return mixed
*/
public function searchModelAttribute($attribute, $search, $compare = '=', $locale = null)
{
return $this->searchAttribute($this->translatedModel, $attribute, $search, $compare, $locale);
}
}

69
app/TranslatableModel.php Normal file
View File

@ -0,0 +1,69 @@
<?php
namespace App;
use App\Repositories\ModelTranslationRepository;
use Illuminate\Database\Eloquent\Model;
use App\Observers\TranslatableModelOberserver;
class TranslatableModel extends Model
{
protected $translatableAttributes = [];
protected static function boot()
{
parent::boot();
static::observe(TranslatableModelOberserver::class);
}
public function __get($attribute)
{
return $this->trans($attribute, app()->getLocale());
}
public function defaultValue($attribute)
{
return parent::__get($attribute);
}
public function trans($attribute, $locale = null)
{
if(in_array($attribute, $this->translatableAttributes)) {
$currentLocale = $locale ? $locale : app()->getLocale();
$transRepo = app(ModelTranslationRepository::class);
$transRepo->setTranslatedModel($this);
$value = $transRepo->getModelTranslation($this->id, $attribute, $currentLocale);
if($value === null) {
$value = $this->defaultValue($attribute);
}
return $value;
} else {
return $this->defaultValue($attribute);
}
}
public function getTranslatableAttributes()
{
return $this->translatableAttributes;
}
public function translations()
{
return $this->morphMany(ModelTranslation::class, 'model');
}
public function scopeSearchInTranslation($query, $attribute, $keyword)
{
$keyword = '%' . implode('%', preg_split('//u', $keyword, -1, PREG_SPLIT_NO_EMPTY)) . '%';
return $query->whereHas('translations', function($query) use($attribute, $keyword) {
$query->where('attribute', $attribute)->where('content', 'LIKE', $keyword);
});
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateModelTranslationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('model_translations', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('model_type')->comment('Model名稱');
$table->unsignedInteger('model_id')->comment('Model ID');
$table->string('attribute')->comment('Model屬性');
$table->string('locale')->comment('語系');
$table->text('content')->nullable()->comment('翻譯的值');
$table->unique(['model_type', 'model_id', 'attribute', 'locale'], 'model_locale_group');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('model_translations');
}
}