加入Model的翻譯機制
This commit is contained in:
parent
c68e25de0a
commit
9ebaa6fcd2
25
app/ModelTranslation.php
Normal file
25
app/ModelTranslation.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Observers/TranslatableModelOberserver.php
Normal file
66
app/Observers/TranslatableModelOberserver.php
Normal 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)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
334
app/Reposotories/ModelTranslationRepository.php
Normal file
334
app/Reposotories/ModelTranslationRepository.php
Normal 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
69
app/TranslatableModel.php
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user