From 9ebaa6fcd29f6d4bf9cd7c4a7f19eaecc8dc80aa Mon Sep 17 00:00:00 2001 From: kroutony Date: Sun, 23 Feb 2020 14:35:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5Model=E7=9A=84=E7=BF=BB?= =?UTF-8?q?=E8=AD=AF=E6=A9=9F=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/ModelTranslation.php | 25 ++ app/Observers/TranslatableModelOberserver.php | 66 ++++ .../ModelTranslationRepository.php | 334 ++++++++++++++++++ app/TranslatableModel.php | 69 ++++ ...061215_create_model_translations_table.php | 36 ++ 5 files changed, 530 insertions(+) create mode 100644 app/ModelTranslation.php create mode 100644 app/Observers/TranslatableModelOberserver.php create mode 100644 app/Reposotories/ModelTranslationRepository.php create mode 100644 app/TranslatableModel.php create mode 100644 database/migrations/2020_02_23_061215_create_model_translations_table.php diff --git a/app/ModelTranslation.php b/app/ModelTranslation.php new file mode 100644 index 0000000..4cd4641 --- /dev/null +++ b/app/ModelTranslation.php @@ -0,0 +1,25 @@ +morphTo(); + } +} diff --git a/app/Observers/TranslatableModelOberserver.php b/app/Observers/TranslatableModelOberserver.php new file mode 100644 index 0000000..1d3a4da --- /dev/null +++ b/app/Observers/TranslatableModelOberserver.php @@ -0,0 +1,66 @@ +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) + { + // + } +} diff --git a/app/Reposotories/ModelTranslationRepository.php b/app/Reposotories/ModelTranslationRepository.php new file mode 100644 index 0000000..38c8beb --- /dev/null +++ b/app/Reposotories/ModelTranslationRepository.php @@ -0,0 +1,334 @@ +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); + } +} diff --git a/app/TranslatableModel.php b/app/TranslatableModel.php new file mode 100644 index 0000000..8d07440 --- /dev/null +++ b/app/TranslatableModel.php @@ -0,0 +1,69 @@ +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); + }); + } +} diff --git a/database/migrations/2020_02_23_061215_create_model_translations_table.php b/database/migrations/2020_02_23_061215_create_model_translations_table.php new file mode 100644 index 0000000..79d265d --- /dev/null +++ b/database/migrations/2020_02_23_061215_create_model_translations_table.php @@ -0,0 +1,36 @@ +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'); + } +}